mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-17 01:13:11 +00:00
Rollup merge of #72280 - nbdd0121:typeck, r=nikomatsakis
Fix up autoderef when reborrowing Currently `(f)()` and `f.call_mut()` behaves differently if expression `f` contains autoderef in it. This causes a weird error in #72225. When `f` is type checked, `Deref` is used (this is expected as we can't yet determine if we should use `Fn` or `FnMut`). When subsequently we determine the actual trait to be used, when using the `f.call_mut()` syntax the `Deref` is patched to `DerefMut`, while for the `(f)()` syntax case it is not. This PR replicates the fixup for the first case. Fixes #72225 Fixes #68590
This commit is contained in:
commit
70622db43d
@ -1,5 +1,5 @@
|
||||
use super::method::MethodCallee;
|
||||
use super::{FnCtxt, Needs, PlaceOp};
|
||||
use super::{FnCtxt, PlaceOp};
|
||||
|
||||
use rustc_errors::struct_span_err;
|
||||
use rustc_hir as hir;
|
||||
@ -170,14 +170,13 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
|
||||
}
|
||||
|
||||
/// Returns the adjustment steps.
|
||||
pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>, needs: Needs) -> Vec<Adjustment<'tcx>> {
|
||||
fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx, needs))
|
||||
pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>) -> Vec<Adjustment<'tcx>> {
|
||||
fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx))
|
||||
}
|
||||
|
||||
pub fn adjust_steps_as_infer_ok(
|
||||
&self,
|
||||
fcx: &FnCtxt<'a, 'tcx>,
|
||||
needs: Needs,
|
||||
) -> InferOk<'tcx, Vec<Adjustment<'tcx>>> {
|
||||
let mut obligations = vec![];
|
||||
let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(self.cur_ty));
|
||||
@ -186,7 +185,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
|
||||
.iter()
|
||||
.map(|&(source, kind)| {
|
||||
if let AutoderefKind::Overloaded = kind {
|
||||
fcx.try_overloaded_deref(self.span, source, needs).and_then(
|
||||
fcx.try_overloaded_deref(self.span, source).and_then(
|
||||
|InferOk { value: method, obligations: o }| {
|
||||
obligations.extend(o);
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
|
||||
@ -266,8 +265,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
span: Span,
|
||||
base_ty: Ty<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
self.try_overloaded_place_op(span, base_ty, &[], needs, PlaceOp::Deref)
|
||||
self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::autoderef::Autoderef;
|
||||
use super::method::MethodCallee;
|
||||
use super::{Expectation, FnCtxt, Needs, TupleArgumentsFlag};
|
||||
use super::{Expectation, FnCtxt, TupleArgumentsFlag};
|
||||
use crate::type_error_struct;
|
||||
|
||||
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
|
||||
@ -115,7 +115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// If the callee is a bare function or a closure, then we're all set.
|
||||
match adjusted_ty.kind {
|
||||
ty::FnDef(..) | ty::FnPtr(_) => {
|
||||
let adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
let adjustments = autoderef.adjust_steps(self);
|
||||
self.apply_adjustments(callee_expr, adjustments);
|
||||
return Some(CallStep::Builtin(adjusted_ty));
|
||||
}
|
||||
@ -135,7 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&closure_sig,
|
||||
)
|
||||
.0;
|
||||
let adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
let adjustments = autoderef.adjust_steps(self);
|
||||
self.record_deferred_call_resolution(
|
||||
def_id,
|
||||
DeferredCallResolution {
|
||||
@ -176,7 +176,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs))
|
||||
.or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None))
|
||||
.map(|(autoref, method)| {
|
||||
let mut adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
let mut adjustments = autoderef.adjust_steps(self);
|
||||
adjustments.extend(autoref);
|
||||
self.apply_adjustments(callee_expr, adjustments);
|
||||
CallStep::Overloaded(method)
|
||||
@ -220,21 +220,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
let mut autoref = None;
|
||||
if borrow {
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind {
|
||||
let mutbl = match mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// For initial two-phase borrow
|
||||
// deployment, conservatively omit
|
||||
// overloaded function call ops.
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
},
|
||||
};
|
||||
autoref = Some(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
|
||||
target: method.sig.inputs()[0],
|
||||
});
|
||||
}
|
||||
// Check for &self vs &mut self in the method signature. Since this is either
|
||||
// the Fn or FnMut trait, it should be one of those.
|
||||
let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind
|
||||
{
|
||||
(r, mutbl)
|
||||
} else {
|
||||
span_bug!(call_expr.span, "input to call/call_mut is not a ref?");
|
||||
};
|
||||
|
||||
let mutbl = match mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// For initial two-phase borrow
|
||||
// deployment, conservatively omit
|
||||
// overloaded function call ops.
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
},
|
||||
};
|
||||
autoref = Some(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
|
||||
target: method.sig.inputs()[0],
|
||||
});
|
||||
}
|
||||
return Some((autoref, method));
|
||||
}
|
||||
|
@ -51,7 +51,7 @@
|
||||
//! we may want to adjust precisely when coercions occur.
|
||||
|
||||
use crate::astconv::AstConv;
|
||||
use crate::check::{FnCtxt, Needs};
|
||||
use crate::check::FnCtxt;
|
||||
use rustc_errors::{struct_span_err, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
@ -421,9 +421,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||
return success(vec![], ty, obligations);
|
||||
}
|
||||
|
||||
let needs = Needs::maybe_mut_place(mutbl_b);
|
||||
let InferOk { value: mut adjustments, obligations: o } =
|
||||
autoderef.adjust_steps_as_infer_ok(self, needs);
|
||||
autoderef.adjust_steps_as_infer_ok(self);
|
||||
obligations.extend(o);
|
||||
obligations.extend(autoderef.into_obligations());
|
||||
|
||||
|
@ -29,9 +29,7 @@ use rustc_hir::{ExprKind, QPath};
|
||||
use rustc_infer::infer;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::adjustment::{
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||
};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::ty::TypeFoldable;
|
||||
use rustc_middle::ty::{AdtKind, Visibility};
|
||||
@ -113,12 +111,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.check_expr_with_expectation(expr, ExpectHasType(expected))
|
||||
}
|
||||
|
||||
pub(super) fn check_expr_with_expectation(
|
||||
fn check_expr_with_expectation_and_needs(
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Ty<'tcx> {
|
||||
self.check_expr_with_expectation_and_needs(expr, expected, Needs::None)
|
||||
let ty = self.check_expr_with_expectation(expr, expected);
|
||||
|
||||
// If the expression is used in a place whether mutable place is required
|
||||
// e.g. LHS of assignment, perform the conversion.
|
||||
if let Needs::MutPlace = needs {
|
||||
self.convert_place_derefs_to_mutable(expr);
|
||||
}
|
||||
|
||||
ty
|
||||
}
|
||||
|
||||
pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> {
|
||||
@ -143,11 +150,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// Note that inspecting a type's structure *directly* may expose the fact
|
||||
/// that there are actually multiple representations for `Error`, so avoid
|
||||
/// that when err needs to be handled differently.
|
||||
fn check_expr_with_expectation_and_needs(
|
||||
pub(super) fn check_expr_with_expectation(
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Ty<'tcx> {
|
||||
debug!(">> type-checking: expr={:?} expected={:?}", expr, expected);
|
||||
|
||||
@ -171,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let old_diverges = self.diverges.replace(Diverges::Maybe);
|
||||
let old_has_errors = self.has_errors.replace(false);
|
||||
|
||||
let ty = self.check_expr_kind(expr, expected, needs);
|
||||
let ty = self.check_expr_kind(expr, expected);
|
||||
|
||||
// Warn for non-block expressions with diverging children.
|
||||
match expr.kind {
|
||||
@ -213,9 +219,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Ty<'tcx> {
|
||||
debug!("check_expr_kind(expr={:?}, expected={:?}, needs={:?})", expr, expected, needs,);
|
||||
debug!("check_expr_kind(expr={:?}, expected={:?})", expr, expected);
|
||||
|
||||
let tcx = self.tcx;
|
||||
match expr.kind {
|
||||
@ -226,9 +231,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.check_expr_assign(expr, expected, lhs, rhs, span)
|
||||
}
|
||||
ExprKind::AssignOp(op, ref lhs, ref rhs) => self.check_binop_assign(expr, op, lhs, rhs),
|
||||
ExprKind::Unary(unop, ref oprnd) => {
|
||||
self.check_expr_unary(unop, oprnd, expected, needs, expr)
|
||||
}
|
||||
ExprKind::Unary(unop, ref oprnd) => self.check_expr_unary(unop, oprnd, expected, expr),
|
||||
ExprKind::AddrOf(kind, mutbl, ref oprnd) => {
|
||||
self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr)
|
||||
}
|
||||
@ -264,7 +267,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ExprKind::Block(ref body, _) => self.check_block_with_expected(&body, expected),
|
||||
ExprKind::Call(ref callee, ref args) => self.check_call(expr, &callee, args, expected),
|
||||
ExprKind::MethodCall(ref segment, span, ref args, _) => {
|
||||
self.check_method_call(expr, segment, span, args, expected, needs)
|
||||
self.check_method_call(expr, segment, span, args, expected)
|
||||
}
|
||||
ExprKind::Cast(ref e, ref t) => self.check_expr_cast(e, t, expr),
|
||||
ExprKind::Type(ref e, ref t) => {
|
||||
@ -281,8 +284,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ExprKind::Struct(ref qpath, fields, ref base_expr) => {
|
||||
self.check_expr_struct(expr, expected, qpath, fields, base_expr)
|
||||
}
|
||||
ExprKind::Field(ref base, field) => self.check_field(expr, needs, &base, field),
|
||||
ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, needs, expr),
|
||||
ExprKind::Field(ref base, field) => self.check_field(expr, &base, field),
|
||||
ExprKind::Index(ref base, ref idx) => self.check_expr_index(base, idx, expr),
|
||||
ExprKind::Yield(ref value, ref src) => self.check_expr_yield(value, expr, src),
|
||||
hir::ExprKind::Err => tcx.ty_error(),
|
||||
}
|
||||
@ -302,7 +305,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
unop: hir::UnOp,
|
||||
oprnd: &'tcx hir::Expr<'tcx>,
|
||||
expected: Expectation<'tcx>,
|
||||
needs: Needs,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
@ -310,40 +312,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
hir::UnOp::UnNot | hir::UnOp::UnNeg => expected,
|
||||
hir::UnOp::UnDeref => NoExpectation,
|
||||
};
|
||||
let needs = match unop {
|
||||
hir::UnOp::UnDeref => needs,
|
||||
_ => Needs::None,
|
||||
};
|
||||
let mut oprnd_t = self.check_expr_with_expectation_and_needs(&oprnd, expected_inner, needs);
|
||||
let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner);
|
||||
|
||||
if !oprnd_t.references_error() {
|
||||
oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t);
|
||||
match unop {
|
||||
hir::UnOp::UnDeref => {
|
||||
if let Some(mt) = oprnd_t.builtin_deref(true) {
|
||||
oprnd_t = mt.ty;
|
||||
} else if let Some(ok) = self.try_overloaded_deref(expr.span, oprnd_t, needs) {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind {
|
||||
let mutbl = match mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// (It shouldn't actually matter for unary ops whether
|
||||
// we enable two-phase borrows or not, since a unary
|
||||
// op has no additional operands.)
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
},
|
||||
};
|
||||
self.apply_adjustments(
|
||||
oprnd,
|
||||
vec![Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
|
||||
target: method.sig.inputs()[0],
|
||||
}],
|
||||
);
|
||||
}
|
||||
oprnd_t = self.make_overloaded_place_return_type(method).ty;
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) {
|
||||
oprnd_t = ty;
|
||||
} else {
|
||||
let mut err = type_error_struct!(
|
||||
tcx.sess,
|
||||
@ -405,8 +381,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
_ => NoExpectation,
|
||||
}
|
||||
});
|
||||
let needs = Needs::maybe_mut_place(mutbl);
|
||||
let ty = self.check_expr_with_expectation_and_needs(&oprnd, hint, needs);
|
||||
let ty =
|
||||
self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl));
|
||||
|
||||
let tm = ty::TypeAndMut { ty, mutbl };
|
||||
match kind {
|
||||
@ -861,10 +837,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
span: Span,
|
||||
args: &'tcx [hir::Expr<'tcx>],
|
||||
expected: Expectation<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Ty<'tcx> {
|
||||
let rcvr = &args[0];
|
||||
let rcvr_t = self.check_expr_with_needs(&rcvr, needs);
|
||||
let rcvr_t = self.check_expr(&rcvr);
|
||||
// no need to check for bot/err -- callee does that
|
||||
let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t);
|
||||
|
||||
@ -1443,11 +1418,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
fn check_field(
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
needs: Needs,
|
||||
base: &'tcx hir::Expr<'tcx>,
|
||||
field: Ident,
|
||||
) -> Ty<'tcx> {
|
||||
let expr_t = self.check_expr_with_needs(base, needs);
|
||||
let expr_t = self.check_expr(base);
|
||||
let expr_t = self.structurally_resolved_type(base.span, expr_t);
|
||||
let mut private_candidate = None;
|
||||
let mut autoderef = self.autoderef(expr.span, expr_t);
|
||||
@ -1467,7 +1441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// of error recovery.
|
||||
self.write_field_index(expr.hir_id, index);
|
||||
if field.vis.is_accessible_from(def_scope, self.tcx) {
|
||||
let adjustments = autoderef.adjust_steps(self, needs);
|
||||
let adjustments = autoderef.adjust_steps(self);
|
||||
self.apply_adjustments(base, adjustments);
|
||||
autoderef.finalize(self);
|
||||
|
||||
@ -1482,7 +1456,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
if let Ok(index) = fstr.parse::<usize>() {
|
||||
if fstr == index.to_string() {
|
||||
if let Some(field_ty) = tys.get(index) {
|
||||
let adjustments = autoderef.adjust_steps(self, needs);
|
||||
let adjustments = autoderef.adjust_steps(self);
|
||||
self.apply_adjustments(base, adjustments);
|
||||
autoderef.finalize(self);
|
||||
|
||||
@ -1721,10 +1695,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
base: &'tcx hir::Expr<'tcx>,
|
||||
idx: &'tcx hir::Expr<'tcx>,
|
||||
needs: Needs,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let base_t = self.check_expr_with_needs(&base, needs);
|
||||
let base_t = self.check_expr(&base);
|
||||
let idx_t = self.check_expr(&idx);
|
||||
|
||||
if base_t.references_error() {
|
||||
@ -1733,7 +1706,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
idx_t
|
||||
} else {
|
||||
let base_t = self.structurally_resolved_type(base.span, base_t);
|
||||
match self.lookup_indexing(expr, base, base_t, idx_t, needs) {
|
||||
match self.lookup_indexing(expr, base, base_t, idx_t) {
|
||||
Some((index_ty, element_ty)) => {
|
||||
// two-phase not needed because index_ty is never mutable
|
||||
self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
|
||||
|
@ -1,12 +1,12 @@
|
||||
use super::{probe, MethodCallee};
|
||||
|
||||
use crate::astconv::AstConv;
|
||||
use crate::check::{callee, FnCtxt, Needs, PlaceOp};
|
||||
use crate::check::{callee, FnCtxt};
|
||||
use crate::hir::def_id::DefId;
|
||||
use crate::hir::GenericArg;
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::{self, InferOk};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
|
||||
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::fold::TypeFoldable;
|
||||
use rustc_middle::ty::subst::{Subst, SubstsRef};
|
||||
@ -119,11 +119,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
|
||||
// Create the final `MethodCallee`.
|
||||
let callee = MethodCallee { def_id: pick.item.def_id, substs: all_substs, sig: method_sig };
|
||||
|
||||
if let Some(hir::Mutability::Mut) = pick.autoref {
|
||||
self.convert_place_derefs_to_mutable();
|
||||
}
|
||||
|
||||
ConfirmResult { callee, illegal_sized_bound }
|
||||
}
|
||||
|
||||
@ -149,7 +144,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
};
|
||||
assert_eq!(n, pick.autoderefs);
|
||||
|
||||
let mut adjustments = autoderef.adjust_steps(self, Needs::None);
|
||||
let mut adjustments = autoderef.adjust_steps(self);
|
||||
|
||||
let mut target = autoderef.unambiguous_final_ty(self);
|
||||
|
||||
@ -415,151 +410,6 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
|
||||
self.register_wf_obligation(fty.into(), self.span, traits::MiscObligation);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// RECONCILIATION
|
||||
|
||||
/// When we select a method with a mutable autoref, we have to go convert any
|
||||
/// auto-derefs, indices, etc from `Deref` and `Index` into `DerefMut` and `IndexMut`
|
||||
/// respectively.
|
||||
fn convert_place_derefs_to_mutable(&self) {
|
||||
// Gather up expressions we want to munge.
|
||||
let mut exprs = vec![self.self_expr];
|
||||
|
||||
loop {
|
||||
match exprs.last().unwrap().kind {
|
||||
hir::ExprKind::Field(ref expr, _)
|
||||
| hir::ExprKind::Index(ref expr, _)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
|
||||
|
||||
// Fix up autoderefs and derefs.
|
||||
for (i, &expr) in exprs.iter().rev().enumerate() {
|
||||
debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
|
||||
|
||||
// Fix up the autoderefs. Autorefs can only occur immediately preceding
|
||||
// overloaded place ops, and will be fixed by them in order to get
|
||||
// the correct region.
|
||||
let mut source = self.node_ty(expr.hir_id);
|
||||
// Do not mutate adjustments in place, but rather take them,
|
||||
// and replace them after mutating them, to avoid having the
|
||||
// tables borrowed during (`deref_mut`) method resolution.
|
||||
let previous_adjustments =
|
||||
self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id);
|
||||
if let Some(mut adjustments) = previous_adjustments {
|
||||
let needs = Needs::MutPlace;
|
||||
for adjustment in &mut adjustments {
|
||||
if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
|
||||
if let Some(ok) = self.try_overloaded_deref(expr.span, source, needs) {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
|
||||
*deref = OverloadedDeref { region, mutbl };
|
||||
}
|
||||
}
|
||||
}
|
||||
source = adjustment.target;
|
||||
}
|
||||
self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Index(ref base_expr, ref index_expr) => {
|
||||
// We need to get the final type in case dereferences were needed for the trait
|
||||
// to apply (#72002).
|
||||
let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr);
|
||||
self.convert_place_op_to_mutable(
|
||||
PlaceOp::Index,
|
||||
expr,
|
||||
base_expr,
|
||||
&[index_expr_ty],
|
||||
);
|
||||
}
|
||||
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
|
||||
self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_place_op_to_mutable(
|
||||
&self,
|
||||
op: PlaceOp,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &hir::Expr<'_>,
|
||||
arg_tys: &[Ty<'tcx>],
|
||||
) {
|
||||
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
|
||||
if !self.tables.borrow().is_method_call(expr) {
|
||||
debug!("convert_place_op_to_mutable - builtin, nothing to do");
|
||||
return;
|
||||
}
|
||||
|
||||
let base_ty = self
|
||||
.tables
|
||||
.borrow()
|
||||
.expr_adjustments(base_expr)
|
||||
.last()
|
||||
.map_or_else(|| self.node_ty(expr.hir_id), |adj| adj.target);
|
||||
let base_ty = self.resolve_vars_if_possible(&base_ty);
|
||||
|
||||
// Need to deref because overloaded place ops take self by-reference.
|
||||
let base_ty =
|
||||
base_ty.builtin_deref(false).expect("place op takes something that is not a ref").ty;
|
||||
|
||||
let method = self.try_overloaded_place_op(expr.span, base_ty, arg_tys, Needs::MutPlace, op);
|
||||
let method = match method {
|
||||
Some(ok) => self.register_infer_ok_obligations(ok),
|
||||
None => return self.tcx.sess.delay_span_bug(expr.span, "re-trying op failed"),
|
||||
};
|
||||
debug!("convert_place_op_to_mutable: method={:?}", method);
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
|
||||
let (region, mutbl) = if let ty::Ref(r, _, mutbl) = method.sig.inputs()[0].kind {
|
||||
(r, mutbl)
|
||||
} else {
|
||||
span_bug!(expr.span, "input to place op is not a ref?");
|
||||
};
|
||||
|
||||
// Convert the autoref in the base expr to mutable with the correct
|
||||
// region and mutability.
|
||||
let base_expr_ty = self.node_ty(base_expr.hir_id);
|
||||
if let Some(adjustments) =
|
||||
self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
|
||||
{
|
||||
let mut source = base_expr_ty;
|
||||
for adjustment in &mut adjustments[..] {
|
||||
if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
|
||||
debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
|
||||
let mutbl = match mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// For initial two-phase borrow
|
||||
// deployment, conservatively omit
|
||||
// overloaded operators.
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
},
|
||||
};
|
||||
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
|
||||
adjustment.target =
|
||||
self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
|
||||
}
|
||||
source = adjustment.target;
|
||||
}
|
||||
|
||||
// If we have an autoref followed by unsizing at the end, fix the unsize target.
|
||||
|
||||
if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
|
||||
adjustments[..]
|
||||
{
|
||||
*target = method.sig.inputs()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// MISCELLANY
|
||||
|
||||
|
@ -79,6 +79,7 @@ pub mod intrinsic;
|
||||
pub mod method;
|
||||
mod op;
|
||||
mod pat;
|
||||
mod place_op;
|
||||
mod regionck;
|
||||
mod upvar;
|
||||
mod wfcheck;
|
||||
@ -114,7 +115,7 @@ use rustc_infer::infer::{InferCtxt, InferOk, InferResult, RegionVariableOrigin,
|
||||
use rustc_middle::hir::map::blocks::FnLikeNode;
|
||||
use rustc_middle::mir::interpret::ConstValue;
|
||||
use rustc_middle::ty::adjustment::{
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast,
|
||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||
};
|
||||
use rustc_middle::ty::fold::{TypeFoldable, TypeFolder};
|
||||
use rustc_middle::ty::query::Providers;
|
||||
@ -156,7 +157,6 @@ use std::slice;
|
||||
use crate::require_c_abi_if_c_variadic;
|
||||
use crate::util::common::indenter;
|
||||
|
||||
use self::autoderef::Autoderef;
|
||||
use self::callee::DeferredCallResolution;
|
||||
use self::coercion::{CoerceMany, DynamicCoerceMany};
|
||||
use self::compare_method::{compare_const_impl, compare_impl_method, compare_ty_impl};
|
||||
@ -3333,6 +3333,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
return;
|
||||
}
|
||||
|
||||
let autoborrow_mut = adj.iter().any(|adj| {
|
||||
matches!(adj, &Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })),
|
||||
..
|
||||
})
|
||||
});
|
||||
|
||||
match self.tables.borrow_mut().adjustments_mut().entry(expr.hir_id) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(adj);
|
||||
@ -3362,6 +3369,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
*entry.get_mut() = adj;
|
||||
}
|
||||
}
|
||||
|
||||
// If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`.
|
||||
// In this case implicit use of `Deref` and `Index` within `<expr>` should
|
||||
// instead be `DerefMut` and `IndexMut`, so fix those up.
|
||||
if autoborrow_mut {
|
||||
self.convert_place_derefs_to_mutable(expr);
|
||||
}
|
||||
}
|
||||
|
||||
/// Basically whenever we are converting from a type scheme into
|
||||
@ -3753,154 +3767,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
ret_ty.builtin_deref(true).unwrap()
|
||||
}
|
||||
|
||||
fn lookup_indexing(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &'tcx hir::Expr<'tcx>,
|
||||
base_ty: Ty<'tcx>,
|
||||
idx_ty: Ty<'tcx>,
|
||||
needs: Needs,
|
||||
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
|
||||
// FIXME(#18741) -- this is almost but not quite the same as the
|
||||
// autoderef that normal method probing does. They could likely be
|
||||
// consolidated.
|
||||
|
||||
let mut autoderef = self.autoderef(base_expr.span, base_ty);
|
||||
let mut result = None;
|
||||
while result.is_none() && autoderef.next().is_some() {
|
||||
result = self.try_index_step(expr, base_expr, &autoderef, needs, idx_ty);
|
||||
}
|
||||
autoderef.finalize(self);
|
||||
result
|
||||
}
|
||||
|
||||
/// To type-check `base_expr[index_expr]`, we progressively autoderef
|
||||
/// (and otherwise adjust) `base_expr`, looking for a type which either
|
||||
/// supports builtin indexing or overloaded indexing.
|
||||
/// This loop implements one step in that search; the autoderef loop
|
||||
/// is implemented by `lookup_indexing`.
|
||||
fn try_index_step(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &hir::Expr<'_>,
|
||||
autoderef: &Autoderef<'a, 'tcx>,
|
||||
needs: Needs,
|
||||
index_ty: Ty<'tcx>,
|
||||
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
|
||||
let adjusted_ty = autoderef.unambiguous_final_ty(self);
|
||||
debug!(
|
||||
"try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
|
||||
index_ty={:?})",
|
||||
expr, base_expr, adjusted_ty, index_ty
|
||||
);
|
||||
|
||||
for &unsize in &[false, true] {
|
||||
let mut self_ty = adjusted_ty;
|
||||
if unsize {
|
||||
// We only unsize arrays here.
|
||||
if let ty::Array(element_ty, _) = adjusted_ty.kind {
|
||||
self_ty = self.tcx.mk_slice(element_ty);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If some lookup succeeds, write callee into table and extract index/element
|
||||
// type from the method signature.
|
||||
// If some lookup succeeded, install method in table
|
||||
let input_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::AutoDeref,
|
||||
span: base_expr.span,
|
||||
});
|
||||
let method = self.try_overloaded_place_op(
|
||||
expr.span,
|
||||
self_ty,
|
||||
&[input_ty],
|
||||
needs,
|
||||
PlaceOp::Index,
|
||||
);
|
||||
|
||||
let result = method.map(|ok| {
|
||||
debug!("try_index_step: success, using overloaded indexing");
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
|
||||
let mut adjustments = autoderef.adjust_steps(self, needs);
|
||||
if let ty::Ref(region, _, r_mutbl) = method.sig.inputs()[0].kind {
|
||||
let mutbl = match r_mutbl {
|
||||
hir::Mutability::Not => AutoBorrowMutability::Not,
|
||||
hir::Mutability::Mut => AutoBorrowMutability::Mut {
|
||||
// Indexing can be desugared to a method call,
|
||||
// so maybe we could use two-phase here.
|
||||
// See the documentation of AllowTwoPhase for why that's
|
||||
// not the case today.
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
},
|
||||
};
|
||||
adjustments.push(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)),
|
||||
target: self
|
||||
.tcx
|
||||
.mk_ref(region, ty::TypeAndMut { mutbl: r_mutbl, ty: adjusted_ty }),
|
||||
});
|
||||
}
|
||||
if unsize {
|
||||
adjustments.push(Adjustment {
|
||||
kind: Adjust::Pointer(PointerCast::Unsize),
|
||||
target: method.sig.inputs()[0],
|
||||
});
|
||||
}
|
||||
self.apply_adjustments(base_expr, adjustments);
|
||||
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
(input_ty, self.make_overloaded_place_return_type(method).ty)
|
||||
});
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn resolve_place_op(&self, op: PlaceOp, is_mut: bool) -> (Option<DefId>, Ident) {
|
||||
let (tr, name) = match (op, is_mut) {
|
||||
(PlaceOp::Deref, false) => (self.tcx.lang_items().deref_trait(), sym::deref),
|
||||
(PlaceOp::Deref, true) => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
|
||||
(PlaceOp::Index, false) => (self.tcx.lang_items().index_trait(), sym::index),
|
||||
(PlaceOp::Index, true) => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
|
||||
};
|
||||
(tr, Ident::with_dummy_span(name))
|
||||
}
|
||||
|
||||
fn try_overloaded_place_op(
|
||||
&self,
|
||||
span: Span,
|
||||
base_ty: Ty<'tcx>,
|
||||
arg_tys: &[Ty<'tcx>],
|
||||
needs: Needs,
|
||||
op: PlaceOp,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
debug!("try_overloaded_place_op({:?},{:?},{:?},{:?})", span, base_ty, needs, op);
|
||||
|
||||
// Try Mut first, if needed.
|
||||
let (mut_tr, mut_op) = self.resolve_place_op(op, true);
|
||||
let method = match (needs, mut_tr) {
|
||||
(Needs::MutPlace, Some(trait_did)) => {
|
||||
self.lookup_method_in_trait(span, mut_op, trait_did, base_ty, Some(arg_tys))
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Otherwise, fall back to the immutable version.
|
||||
let (imm_tr, imm_op) = self.resolve_place_op(op, false);
|
||||
match (method, imm_tr) {
|
||||
(None, Some(trait_did)) => {
|
||||
self.lookup_method_in_trait(span, imm_op, trait_did, base_ty, Some(arg_tys))
|
||||
}
|
||||
(method, _) => method,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_method_argument_types(
|
||||
&self,
|
||||
sp: Span,
|
||||
|
@ -1,7 +1,7 @@
|
||||
//! Code related to processing overloaded binary and unary operators.
|
||||
|
||||
use super::method::MethodCallee;
|
||||
use super::{FnCtxt, Needs};
|
||||
use super::FnCtxt;
|
||||
use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
@ -165,7 +165,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// trait matching creating lifetime constraints that are too strict.
|
||||
// e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
|
||||
// in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
|
||||
let lhs_ty = self.check_expr_with_needs(lhs_expr, Needs::None);
|
||||
let lhs_ty = self.check_expr(lhs_expr);
|
||||
let fresh_var = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
span: lhs_expr.span,
|
||||
@ -177,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// equivalence on the LHS of an assign-op like `+=`;
|
||||
// overwritten or mutably-borrowed places cannot be
|
||||
// coerced to a supertype.
|
||||
self.check_expr_with_needs(lhs_expr, Needs::MutPlace)
|
||||
self.check_expr(lhs_expr)
|
||||
}
|
||||
};
|
||||
let lhs_ty = self.resolve_vars_with_obligations(lhs_ty);
|
||||
|
336
src/librustc_typeck/check/place_op.rs
Normal file
336
src/librustc_typeck/check/place_op.rs
Normal file
@ -0,0 +1,336 @@
|
||||
use crate::check::autoderef::Autoderef;
|
||||
use crate::check::method::MethodCallee;
|
||||
use crate::check::{FnCtxt, PlaceOp};
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::infer::InferOk;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast};
|
||||
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::symbol::{sym, Ident};
|
||||
use rustc_span::Span;
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
/// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already.
|
||||
pub(super) fn lookup_derefing(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
oprnd_expr: &'tcx hir::Expr<'tcx>,
|
||||
oprnd_ty: Ty<'tcx>,
|
||||
) -> Option<Ty<'tcx>> {
|
||||
if let Some(mt) = oprnd_ty.builtin_deref(true) {
|
||||
return Some(mt.ty);
|
||||
}
|
||||
|
||||
let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?;
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
|
||||
self.apply_adjustments(
|
||||
oprnd_expr,
|
||||
vec![Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
|
||||
target: method.sig.inputs()[0],
|
||||
}],
|
||||
);
|
||||
} else {
|
||||
span_bug!(expr.span, "input to deref is not a ref?");
|
||||
}
|
||||
let ty = self.make_overloaded_place_return_type(method).ty;
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
Some(ty)
|
||||
}
|
||||
|
||||
/// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already.
|
||||
pub(super) fn lookup_indexing(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &'tcx hir::Expr<'tcx>,
|
||||
base_ty: Ty<'tcx>,
|
||||
idx_ty: Ty<'tcx>,
|
||||
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
|
||||
// FIXME(#18741) -- this is almost but not quite the same as the
|
||||
// autoderef that normal method probing does. They could likely be
|
||||
// consolidated.
|
||||
|
||||
let mut autoderef = self.autoderef(base_expr.span, base_ty);
|
||||
let mut result = None;
|
||||
while result.is_none() && autoderef.next().is_some() {
|
||||
result = self.try_index_step(expr, base_expr, &autoderef, idx_ty);
|
||||
}
|
||||
autoderef.finalize(self);
|
||||
result
|
||||
}
|
||||
|
||||
/// To type-check `base_expr[index_expr]`, we progressively autoderef
|
||||
/// (and otherwise adjust) `base_expr`, looking for a type which either
|
||||
/// supports builtin indexing or overloaded indexing.
|
||||
/// This loop implements one step in that search; the autoderef loop
|
||||
/// is implemented by `lookup_indexing`.
|
||||
fn try_index_step(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &hir::Expr<'_>,
|
||||
autoderef: &Autoderef<'a, 'tcx>,
|
||||
index_ty: Ty<'tcx>,
|
||||
) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> {
|
||||
let adjusted_ty = autoderef.unambiguous_final_ty(self);
|
||||
debug!(
|
||||
"try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \
|
||||
index_ty={:?})",
|
||||
expr, base_expr, adjusted_ty, index_ty
|
||||
);
|
||||
|
||||
for &unsize in &[false, true] {
|
||||
let mut self_ty = adjusted_ty;
|
||||
if unsize {
|
||||
// We only unsize arrays here.
|
||||
if let ty::Array(element_ty, _) = adjusted_ty.kind {
|
||||
self_ty = self.tcx.mk_slice(element_ty);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If some lookup succeeds, write callee into table and extract index/element
|
||||
// type from the method signature.
|
||||
// If some lookup succeeded, install method in table
|
||||
let input_ty = self.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::AutoDeref,
|
||||
span: base_expr.span,
|
||||
});
|
||||
let method =
|
||||
self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index);
|
||||
|
||||
let result = method.map(|ok| {
|
||||
debug!("try_index_step: success, using overloaded indexing");
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
|
||||
let mut adjustments = autoderef.adjust_steps(self);
|
||||
if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind {
|
||||
adjustments.push(Adjustment {
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(region, AutoBorrowMutability::Not)),
|
||||
target: self.tcx.mk_ref(
|
||||
region,
|
||||
ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty },
|
||||
),
|
||||
});
|
||||
} else {
|
||||
span_bug!(expr.span, "input to index is not a ref?");
|
||||
}
|
||||
if unsize {
|
||||
adjustments.push(Adjustment {
|
||||
kind: Adjust::Pointer(PointerCast::Unsize),
|
||||
target: method.sig.inputs()[0],
|
||||
});
|
||||
}
|
||||
self.apply_adjustments(base_expr, adjustments);
|
||||
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
(input_ty, self.make_overloaded_place_return_type(method).ty)
|
||||
});
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Try to resolve an overloaded place op. We only deal with the immutable
|
||||
/// variant here (Deref/Index). In some contexts we would need the mutable
|
||||
/// variant (DerefMut/IndexMut); those would be later converted by
|
||||
/// `convert_place_derefs_to_mutable`.
|
||||
pub(super) fn try_overloaded_place_op(
|
||||
&self,
|
||||
span: Span,
|
||||
base_ty: Ty<'tcx>,
|
||||
arg_tys: &[Ty<'tcx>],
|
||||
op: PlaceOp,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
|
||||
|
||||
let (imm_tr, imm_op) = match op {
|
||||
PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
|
||||
PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index),
|
||||
};
|
||||
imm_tr.and_then(|trait_did| {
|
||||
self.lookup_method_in_trait(
|
||||
span,
|
||||
Ident::with_dummy_span(imm_op),
|
||||
trait_did,
|
||||
base_ty,
|
||||
Some(arg_tys),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn try_mutable_overloaded_place_op(
|
||||
&self,
|
||||
span: Span,
|
||||
base_ty: Ty<'tcx>,
|
||||
arg_tys: &[Ty<'tcx>],
|
||||
op: PlaceOp,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op);
|
||||
|
||||
let (mut_tr, mut_op) = match op {
|
||||
PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
|
||||
PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut),
|
||||
};
|
||||
mut_tr.and_then(|trait_did| {
|
||||
self.lookup_method_in_trait(
|
||||
span,
|
||||
Ident::with_dummy_span(mut_op),
|
||||
trait_did,
|
||||
base_ty,
|
||||
Some(arg_tys),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index`
|
||||
/// into `DerefMut` and `IndexMut` respectively.
|
||||
///
|
||||
/// This is a second pass of typechecking derefs/indices. We need this we do not
|
||||
/// always know whether a place needs to be mutable or not in the first pass.
|
||||
/// This happens whether there is an implicit mutable reborrow, e.g. when the type
|
||||
/// is used as the receiver of a method call.
|
||||
pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) {
|
||||
// Gather up expressions we want to munge.
|
||||
let mut exprs = vec![expr];
|
||||
|
||||
loop {
|
||||
match exprs.last().unwrap().kind {
|
||||
hir::ExprKind::Field(ref expr, _)
|
||||
| hir::ExprKind::Index(ref expr, _)
|
||||
| hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
|
||||
|
||||
// Fix up autoderefs and derefs.
|
||||
for (i, &expr) in exprs.iter().rev().enumerate() {
|
||||
debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr);
|
||||
|
||||
// Fix up the autoderefs. Autorefs can only occur immediately preceding
|
||||
// overloaded place ops, and will be fixed by them in order to get
|
||||
// the correct region.
|
||||
let mut source = self.node_ty(expr.hir_id);
|
||||
// Do not mutate adjustments in place, but rather take them,
|
||||
// and replace them after mutating them, to avoid having the
|
||||
// tables borrowed during (`deref_mut`) method resolution.
|
||||
let previous_adjustments =
|
||||
self.tables.borrow_mut().adjustments_mut().remove(expr.hir_id);
|
||||
if let Some(mut adjustments) = previous_adjustments {
|
||||
for adjustment in &mut adjustments {
|
||||
if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind {
|
||||
if let Some(ok) = self.try_mutable_overloaded_place_op(
|
||||
expr.span,
|
||||
source,
|
||||
&[],
|
||||
PlaceOp::Deref,
|
||||
) {
|
||||
let method = self.register_infer_ok_obligations(ok);
|
||||
if let ty::Ref(region, _, mutbl) = method.sig.output().kind {
|
||||
*deref = OverloadedDeref { region, mutbl };
|
||||
}
|
||||
}
|
||||
}
|
||||
source = adjustment.target;
|
||||
}
|
||||
self.tables.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments);
|
||||
}
|
||||
|
||||
match expr.kind {
|
||||
hir::ExprKind::Index(ref base_expr, ref index_expr) => {
|
||||
// We need to get the final type in case dereferences were needed for the trait
|
||||
// to apply (#72002).
|
||||
let index_expr_ty = self.tables.borrow().expr_ty_adjusted(index_expr);
|
||||
self.convert_place_op_to_mutable(
|
||||
PlaceOp::Index,
|
||||
expr,
|
||||
base_expr,
|
||||
&[index_expr_ty],
|
||||
);
|
||||
}
|
||||
hir::ExprKind::Unary(hir::UnOp::UnDeref, ref base_expr) => {
|
||||
self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr, &[]);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_place_op_to_mutable(
|
||||
&self,
|
||||
op: PlaceOp,
|
||||
expr: &hir::Expr<'_>,
|
||||
base_expr: &hir::Expr<'_>,
|
||||
arg_tys: &[Ty<'tcx>],
|
||||
) {
|
||||
debug!("convert_place_op_to_mutable({:?}, {:?}, {:?}, {:?})", op, expr, base_expr, arg_tys);
|
||||
if !self.tables.borrow().is_method_call(expr) {
|
||||
debug!("convert_place_op_to_mutable - builtin, nothing to do");
|
||||
return;
|
||||
}
|
||||
|
||||
// Need to deref because overloaded place ops take self by-reference.
|
||||
let base_ty = self
|
||||
.tables
|
||||
.borrow()
|
||||
.expr_ty_adjusted(base_expr)
|
||||
.builtin_deref(false)
|
||||
.expect("place op takes something that is not a ref")
|
||||
.ty;
|
||||
|
||||
let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op);
|
||||
let method = match method {
|
||||
Some(ok) => self.register_infer_ok_obligations(ok),
|
||||
// Couldn't find the mutable variant of the place op, keep the
|
||||
// current, immutable version.
|
||||
None => return,
|
||||
};
|
||||
debug!("convert_place_op_to_mutable: method={:?}", method);
|
||||
self.write_method_call(expr.hir_id, method);
|
||||
|
||||
let region = if let ty::Ref(r, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind {
|
||||
r
|
||||
} else {
|
||||
span_bug!(expr.span, "input to mutable place op is not a mut ref?");
|
||||
};
|
||||
|
||||
// Convert the autoref in the base expr to mutable with the correct
|
||||
// region and mutability.
|
||||
let base_expr_ty = self.node_ty(base_expr.hir_id);
|
||||
if let Some(adjustments) =
|
||||
self.tables.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id)
|
||||
{
|
||||
let mut source = base_expr_ty;
|
||||
for adjustment in &mut adjustments[..] {
|
||||
if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind {
|
||||
debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment);
|
||||
let mutbl = AutoBorrowMutability::Mut {
|
||||
// Deref/indexing can be desugared to a method call,
|
||||
// so maybe we could use two-phase here.
|
||||
// See the documentation of AllowTwoPhase for why that's
|
||||
// not the case today.
|
||||
allow_two_phase_borrow: AllowTwoPhase::No,
|
||||
};
|
||||
adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(region, mutbl));
|
||||
adjustment.target =
|
||||
self.tcx.mk_ref(region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() });
|
||||
}
|
||||
source = adjustment.target;
|
||||
}
|
||||
|
||||
// If we have an autoref followed by unsizing at the end, fix the unsize target.
|
||||
if let [.., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }] =
|
||||
adjustments[..]
|
||||
{
|
||||
*target = method.sig.inputs()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
25
src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs
Normal file
25
src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// check-pass
|
||||
|
||||
// rust-lang/rust#68590: confusing diagnostics when reborrowing through DerefMut.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
struct A;
|
||||
|
||||
struct S<'a> {
|
||||
a: &'a mut A,
|
||||
}
|
||||
|
||||
fn take_a(_: &mut A) {}
|
||||
|
||||
fn test<'a>(s: &RefCell<S<'a>>) {
|
||||
let mut guard = s.borrow_mut();
|
||||
take_a(guard.a);
|
||||
let _s2 = S { a: guard.a };
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a = &mut A;
|
||||
let s = RefCell::new(S { a });
|
||||
test(&s);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
// check-pass
|
||||
|
||||
// rust-lang/rust#72225: confusing diagnostics when calling FnMut through DerefMut.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
struct S {
|
||||
f: Box<dyn FnMut()>
|
||||
}
|
||||
|
||||
fn test(s: &RefCell<S>) {
|
||||
let mut guard = s.borrow_mut();
|
||||
(guard.f)();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let s = RefCell::new(S {
|
||||
f: Box::new(|| ())
|
||||
});
|
||||
test(&s);
|
||||
}
|
Loading…
Reference in New Issue
Block a user