diff --git a/src/librustc_typeck/check/autoderef.rs b/src/librustc_typeck/check/autoderef.rs index 73d4e2b78206d98300bce537efa8f31aebf2df0b..2570025959cb4f7320010f63837a7444209bd402 100644 --- a/src/librustc_typeck/check/autoderef.rs +++ b/src/librustc_typeck/check/autoderef.rs @@ -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 @@ pub fn step_count(&self) -> usize { } /// Returns the adjustment steps. - pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>, needs: Needs) -> Vec> { - fcx.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(fcx, needs)) + pub fn adjust_steps(&self, fcx: &FnCtxt<'a, 'tcx>) -> Vec> { + 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>> { let mut obligations = vec![]; let targets = self.steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(self.cur_ty)); @@ -186,7 +185,7 @@ pub fn adjust_steps_as_infer_ok( .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 @@ pub fn try_overloaded_deref( &self, span: Span, base_ty: Ty<'tcx>, - needs: Needs, ) -> Option>> { - self.try_overloaded_place_op(span, base_ty, &[], needs, PlaceOp::Deref) + self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref) } } diff --git a/src/librustc_typeck/check/callee.rs b/src/librustc_typeck/check/callee.rs index 13695be83a9da7bfe0b1dbcff73ae87421b88965..fe200a0ad2a1c09423999f77a1daa5a7a7aabe24 100644 --- a/src/librustc_typeck/check/callee.rs +++ b/src/librustc_typeck/check/callee.rs @@ -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 @@ fn try_overloaded_call_step( // 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 @@ fn try_overloaded_call_step( &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 @@ fn try_overloaded_call_step( 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 @@ fn try_overloaded_call_traits( 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)); } diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index dd3f292fb56846efc60dc75ac9d8c043e1ba51db..1c34d71ee31847dcdb7cff52e53c319aac472ce4 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -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 @@ fn coerce_borrowed_pointer( 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()); diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 69cfafc21648f114541db00c36f37b32fb0c2aea..188f4a9401422fe65dff2048295808f09219a2f4 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -29,9 +29,7 @@ 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 @@ pub(super) fn check_expr_with_hint( 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 @@ pub(super) fn check_expr_with_needs( /// 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 @@ fn check_expr_with_expectation_and_needs( 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 @@ fn check_expr_kind( &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 @@ fn check_expr_kind( 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 @@ fn check_expr_kind( 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 @@ fn check_expr_kind( 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 @@ fn check_expr_unary( 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 @@ fn check_expr_unary( 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 @@ fn check_expr_addr_of( _ => 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 @@ fn check_method_call( 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 @@ fn name_series_display(&self, names: Vec) -> String { 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 @@ fn check_field( // 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 @@ fn check_field( if let Ok(index) = fstr.parse::() { 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 @@ fn check_expr_index( &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 @@ fn check_expr_index( 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); diff --git a/src/librustc_typeck/check/method/confirm.rs b/src/librustc_typeck/check/method/confirm.rs index 8651c643cee879dbcbd44acbf51b0d6763207032..6844c9416af8a7a1a6a9c5b37a14afe11cb1ef4a 100644 --- a/src/librustc_typeck/check/method/confirm.rs +++ b/src/librustc_typeck/check/method/confirm.rs @@ -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 @@ fn confirm( // 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 @@ fn adjust_self_ty( }; 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 @@ fn add_obligations( 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 diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index b60b06567d6fa1c9e6ddfc1145808b5ea366993c..2ece8e8c2842369a8c7fe0ea88413700eab89560 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -79,6 +79,7 @@ pub mod method; mod op; mod pat; +mod place_op; mod regionck; mod upvar; mod wfcheck; @@ -114,7 +115,7 @@ 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 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 @@ pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec> 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 @@ pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec> *entry.get_mut() = adj; } } + + // If there is an mutable auto-borrow, it is equivalent to `&mut `. + // In this case implicit use of `Deref` and `Index` within `` 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 @@ fn make_overloaded_place_return_type( 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, 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>> { - 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, diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index fe508709116473a35fd75db9b72d589482d82fc6..56804792b19442a04481e078866ac341fb5f2a69 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -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 @@ fn check_overloaded_binop( // 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 @@ fn check_overloaded_binop( // 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); diff --git a/src/librustc_typeck/check/place_op.rs b/src/librustc_typeck/check/place_op.rs new file mode 100644 index 0000000000000000000000000000000000000000..d1c22cd1ac03ebe0d18747d73f5a0b6e38198517 --- /dev/null +++ b/src/librustc_typeck/check/place_op.rs @@ -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> { + 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>> { + 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>> { + 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]; + } + } + } +} diff --git a/src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs b/src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs new file mode 100644 index 0000000000000000000000000000000000000000..e4436260e70a07ac26d90c715b3995087ec58379 --- /dev/null +++ b/src/test/ui/typeck/issue-68590-reborrow-through-derefmut.rs @@ -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>) { + 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); +} diff --git a/src/test/ui/typeck/issue-72225-call-fnmut-through-derefmut.rs b/src/test/ui/typeck/issue-72225-call-fnmut-through-derefmut.rs new file mode 100644 index 0000000000000000000000000000000000000000..3ea05389f04a0b7ae8c2fdd3c8f31f34e37b1178 --- /dev/null +++ b/src/test/ui/typeck/issue-72225-call-fnmut-through-derefmut.rs @@ -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 +} + +fn test(s: &RefCell) { + let mut guard = s.borrow_mut(); + (guard.f)(); +} + +fn main() { + let s = RefCell::new(S { + f: Box::new(|| ()) + }); + test(&s); +}