From d18b4bb7a735add53cad717fd96ec5f4b89d752d Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sat, 25 Jul 2020 07:04:13 -0400 Subject: [PATCH] Note when a a move/borrow error is caused by a deref coercion Fixes #73268 When a deref coercion occurs, we may end up with a move error if the base value has been partially moved out of. However, we do not indicate anywhere that a deref coercion is occuring, resulting in an error message with a confusing span. This PR adds an explicit note to move errors when a deref coercion is involved. We mention the name of the type that the deref-coercion resolved to, as well as the `Deref::Target` associated type being used. --- compiler/rustc_middle/src/ty/adjustment.rs | 4 + .../rustc_middle/src/ty/structural_impls.rs | 7 +- .../diagnostics/conflict_errors.rs | 27 ++++++- .../diagnostics/explain_borrow.rs | 2 +- .../src/borrow_check/diagnostics/mod.rs | 73 +++++++++++++++---- .../borrow_check/diagnostics/move_errors.rs | 6 +- compiler/rustc_mir/src/borrow_check/mod.rs | 5 +- .../transform/check_const_item_mutation.rs | 2 +- compiler/rustc_mir/src/util/find_self_call.rs | 13 ++-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 29 ++++++-- compiler/rustc_span/src/symbol.rs | 2 + .../rustc_trait_selection/src/autoderef.rs | 7 ++ .../src/traits/error_reporting/suggestions.rs | 2 +- compiler/rustc_typeck/src/check/autoderef.rs | 19 ++++- .../rustc_typeck/src/check/method/confirm.rs | 3 +- .../rustc_typeck/src/check/method/probe.rs | 7 +- compiler/rustc_typeck/src/check/place_op.rs | 2 +- library/core/src/ops/deref.rs | 2 + .../lint/lint-unconditional-recursion.stderr | 2 +- src/test/ui/moves/move-deref-coercion.rs | 33 +++++++++ src/test/ui/moves/move-deref-coercion.stderr | 35 +++++++++ src/test/ui/no-capture-arc.stderr | 11 ++- src/test/ui/no-reuse-move-arc.stderr | 11 ++- 23 files changed, 250 insertions(+), 54 deletions(-) create mode 100644 src/test/ui/moves/move-deref-coercion.rs create mode 100644 src/test/ui/moves/move-deref-coercion.stderr diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index 6a9bb8d6c28..46ef5ff7dd8 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -4,6 +4,7 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_macros::HashStable; +use rustc_span::Span; #[derive(Clone, Copy, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)] pub enum PointerCast { @@ -113,6 +114,9 @@ pub enum Adjust<'tcx> { pub struct OverloadedDeref<'tcx> { pub region: ty::Region<'tcx>, pub mutbl: hir::Mutability, + /// The `Span` associated with the field access or method call + /// that triggered this overloaded deref. + pub span: Span, } impl<'tcx> OverloadedDeref<'tcx> { diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index f8627e2f1b6..daa8b649596 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -606,8 +606,11 @@ impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::Adjust<'a> { impl<'a, 'tcx> Lift<'tcx> for ty::adjustment::OverloadedDeref<'a> { type Lifted = ty::adjustment::OverloadedDeref<'tcx>; fn lift_to_tcx(&self, tcx: TyCtxt<'tcx>) -> Option { - tcx.lift(&self.region) - .map(|region| ty::adjustment::OverloadedDeref { region, mutbl: self.mutbl }) + tcx.lift(&self.region).map(|region| ty::adjustment::OverloadedDeref { + region, + mutbl: self.mutbl, + span: self.span, + }) } } diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index 676065007b7..11122b195c0 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -66,7 +66,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { let span = use_spans.args_or_use(); let move_site_vec = self.get_moved_indexes(location, mpi); - debug!("report_use_of_moved_or_uninitialized: move_site_vec={:?}", move_site_vec); + debug!( + "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}", + move_site_vec, use_spans + ); let move_out_indices: Vec<_> = move_site_vec.iter().map(|move_site| move_site.moi).collect(); @@ -229,6 +232,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); } } + // Deref::deref takes &self, which cannot cause a move + FnSelfUseKind::DerefCoercion { .. } => unreachable!(), } } else { err.span_label( @@ -355,6 +360,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { self.note_type_does_not_implement_copy(&mut err, ¬e_msg, ty, span, partial_str); } + if let UseSpans::FnSelfUse { + kind: FnSelfUseKind::DerefCoercion { deref_target, deref_target_ty }, + .. + } = use_spans + { + err.note(&format!( + "{} occurs due to deref coercion to `{}`", + desired_action.as_noun(), + deref_target_ty + )); + + err.span_note(deref_target, "deref defined here"); + } + if let Some((_, mut old_err)) = self.move_error_reported.insert(move_out_indices, (used_place, err)) { @@ -945,7 +964,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { name: &str, borrow: &BorrowData<'tcx>, drop_span: Span, - borrow_spans: UseSpans, + borrow_spans: UseSpans<'tcx>, explanation: BorrowExplanation, ) -> DiagnosticBuilder<'cx> { debug!( @@ -1146,7 +1165,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { location: Location, borrow: &BorrowData<'tcx>, drop_span: Span, - borrow_spans: UseSpans, + borrow_spans: UseSpans<'tcx>, proper_span: Span, explanation: BorrowExplanation, ) -> DiagnosticBuilder<'cx> { @@ -1274,7 +1293,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn report_escaping_closure_capture( &mut self, - use_span: UseSpans, + use_span: UseSpans<'tcx>, var_span: Span, fr_name: &RegionName, category: ConstraintCategory, diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs index eaaf202f3bd..eccb6168229 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs @@ -501,7 +501,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { fn later_use_kind( &self, borrow: &BorrowData<'tcx>, - use_spans: UseSpans, + use_spans: UseSpans<'tcx>, location: Location, ) -> (LaterUseKind, Span) { match use_spans { diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs index 3cee32834be..4256f6e39d5 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/mod.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::{ PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::print::Print; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt}; +use rustc_middle::ty::{self, DefIdTree, Instance, Ty, TyCtxt}; use rustc_span::{ hygiene::{DesugaringKind, ForLoopLoc}, symbol::sym, @@ -538,7 +538,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// The span(s) associated to a use of a place. #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum UseSpans { +pub(super) enum UseSpans<'tcx> { /// The access is caused by capturing a variable for a closure. ClosureUse { /// This is true if the captured variable was from a generator. @@ -558,7 +558,7 @@ pub(super) enum UseSpans { fn_call_span: Span, /// The definition span of the method being called fn_span: Span, - kind: FnSelfUseKind, + kind: FnSelfUseKind<'tcx>, }, /// This access is caused by a `match` or `if let` pattern. PatUse(Span), @@ -567,22 +567,32 @@ pub(super) enum UseSpans { } #[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub(super) enum FnSelfUseKind { +pub(super) enum FnSelfUseKind<'tcx> { /// A normal method call of the form `receiver.foo(a, b, c)` Normal { self_arg: Ident, implicit_into_iter: bool }, /// A call to `FnOnce::call_once`, desugared from `my_closure(a, b, c)` FnOnceCall, /// A call to an operator trait, desuraged from operator syntax (e.g. `a << b`) Operator { self_arg: Ident }, + DerefCoercion { + /// The `Span` of the `Target` associated type + /// in the `Deref` impl we are using. + deref_target: Span, + /// The type `T::Deref` we are dereferencing to + deref_target_ty: Ty<'tcx>, + }, } -impl UseSpans { +impl UseSpans<'_> { pub(super) fn args_or_use(self) -> Span { match self { UseSpans::ClosureUse { args_span: span, .. } | UseSpans::PatUse(span) - | UseSpans::FnSelfUse { var_span: span, .. } | UseSpans::OtherUse(span) => span, + UseSpans::FnSelfUse { + fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. + } => fn_call_span, + UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -590,8 +600,11 @@ impl UseSpans { match self { UseSpans::ClosureUse { var_span: span, .. } | UseSpans::PatUse(span) - | UseSpans::FnSelfUse { var_span: span, .. } | UseSpans::OtherUse(span) => span, + UseSpans::FnSelfUse { + fn_call_span, kind: FnSelfUseKind::DerefCoercion { .. }, .. + } => fn_call_span, + UseSpans::FnSelfUse { var_span, .. } => var_span, } } @@ -754,7 +767,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { &self, moved_place: PlaceRef<'tcx>, // Could also be an upvar. location: Location, - ) -> UseSpans { + ) -> UseSpans<'tcx> { use self::UseSpans::*; let stmt = match self.body[location.block].statements.get(location.statement_index) { @@ -809,36 +822,64 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, .. }) = &self.body[location.block].terminator { - let method_did = if let Some(method_did) = + let (method_did, method_substs) = if let Some(info) = crate::util::find_self_call(self.infcx.tcx, &self.body, target_temp, location.block) { - method_did + info } else { return normal_ret; }; let tcx = self.infcx.tcx; - let parent = tcx.parent(method_did); let is_fn_once = parent == tcx.lang_items().fn_once_trait(); let is_operator = !from_hir_call && parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p)); + let is_deref = !from_hir_call && tcx.is_diagnostic_item(sym::deref_method, method_did); let fn_call_span = *fn_span; let self_arg = tcx.fn_arg_names(method_did)[0]; + debug!( + "terminator = {:?} from_hir_call={:?}", + self.body[location.block].terminator, from_hir_call + ); + + // Check for a 'special' use of 'self' - + // an FnOnce call, an operator (e.g. `<<`), or a + // deref coercion. let kind = if is_fn_once { - FnSelfUseKind::FnOnceCall + Some(FnSelfUseKind::FnOnceCall) } else if is_operator { - FnSelfUseKind::Operator { self_arg } + Some(FnSelfUseKind::Operator { self_arg }) + } else if is_deref { + let deref_target = + tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| { + Instance::resolve(tcx, self.param_env, deref_target, method_substs) + .transpose() + }); + if let Some(Ok(instance)) = deref_target { + let deref_target_ty = instance.ty(tcx, self.param_env); + Some(FnSelfUseKind::DerefCoercion { + deref_target: tcx.def_span(instance.def_id()), + deref_target_ty, + }) + } else { + None + } } else { + None + }; + + let kind = kind.unwrap_or_else(|| { + // This isn't a 'special' use of `self` debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span); let implicit_into_iter = matches!( fn_call_span.desugaring_kind(), Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter)) ); FnSelfUseKind::Normal { self_arg, implicit_into_iter } - }; + }); return FnSelfUse { var_span: stmt.source_info.span, @@ -859,7 +900,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// and its usage of the local assigned at `location`. /// This is done by searching in statements succeeding `location` /// and originating from `maybe_closure_span`. - pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans { + pub(super) fn borrow_spans(&self, use_span: Span, location: Location) -> UseSpans<'tcx> { use self::UseSpans::*; debug!("borrow_spans: use_span={:?} location={:?}", use_span, location); @@ -963,7 +1004,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { /// Helper to retrieve span(s) of given borrow from the current MIR /// representation - pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans { + pub(super) fn retrieve_borrow_spans(&self, borrow: &BorrowData<'_>) -> UseSpans<'tcx> { let span = self.body.source_info(borrow.reserve_location).span; self.borrow_spans(span, borrow.reserve_location) } diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs index 6cf1cf20b5a..e256fb55b12 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/move_errors.rs @@ -47,7 +47,7 @@ enum GroupedMoveError<'tcx> { // Everything that isn't from pattern matching. OtherIllegalMove { original_path: Place<'tcx>, - use_spans: UseSpans, + use_spans: UseSpans<'tcx>, kind: IllegalMoveOriginKind<'tcx>, }, } @@ -222,7 +222,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let (mut err, err_span) = { let (span, use_spans, original_path, kind): ( Span, - Option, + Option>, Place<'tcx>, &IllegalMoveOriginKind<'_>, ) = match error { @@ -291,7 +291,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { move_place: Place<'tcx>, deref_target_place: Place<'tcx>, span: Span, - use_spans: Option, + use_spans: Option>, ) -> DiagnosticBuilder<'a> { // Inspect the type of the content behind the // borrow to provide feedback about why this diff --git a/compiler/rustc_mir/src/borrow_check/mod.rs b/compiler/rustc_mir/src/borrow_check/mod.rs index acd9e3dcf3f..64ad0627720 100644 --- a/compiler/rustc_mir/src/borrow_check/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/mod.rs @@ -17,7 +17,7 @@ use rustc_middle::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind use rustc_middle::mir::{Field, ProjectionElem, Promoted, Rvalue, Statement, StatementKind}; use rustc_middle::mir::{InlineAsmOperand, Terminator, TerminatorKind}; use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, InstanceDef, RegionVid, TyCtxt}; +use rustc_middle::ty::{self, InstanceDef, ParamEnv, RegionVid, TyCtxt}; use rustc_session::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT, UNUSED_MUT}; use rustc_span::{Span, Symbol, DUMMY_SP}; @@ -287,6 +287,7 @@ fn do_mir_borrowck<'a, 'tcx>( if let Err((move_data, move_errors)) = move_data_results { let mut promoted_mbcx = MirBorrowckCtxt { infcx, + param_env, body: promoted_body, mir_def_id: def.did, move_data: &move_data, @@ -320,6 +321,7 @@ fn do_mir_borrowck<'a, 'tcx>( let mut mbcx = MirBorrowckCtxt { infcx, + param_env, body, mir_def_id: def.did, move_data: &mdpe.move_data, @@ -473,6 +475,7 @@ fn do_mir_borrowck<'a, 'tcx>( crate struct MirBorrowckCtxt<'cx, 'tcx> { crate infcx: &'cx InferCtxt<'cx, 'tcx>, + param_env: ParamEnv<'tcx>, body: &'cx Body<'tcx>, mir_def_id: LocalDefId, move_data: &'cx MoveData<'tcx>, diff --git a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs index 589268e39bd..70c1aed0957 100644 --- a/compiler/rustc_mir/src/transform/check_const_item_mutation.rs +++ b/compiler/rustc_mir/src/transform/check_const_item_mutation.rs @@ -101,7 +101,7 @@ impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> { .note("each usage of a `const` item creates a new temporary") .note("the mutable reference will refer to this temporary, not the original `const` item"); - if let Some(method_did) = method_did { + if let Some((method_did, _substs)) = method_did { lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method"); } diff --git a/compiler/rustc_mir/src/util/find_self_call.rs b/compiler/rustc_mir/src/util/find_self_call.rs index 049b5f01214..5b146eeb87c 100644 --- a/compiler/rustc_mir/src/util/find_self_call.rs +++ b/compiler/rustc_mir/src/util/find_self_call.rs @@ -1,30 +1,31 @@ use rustc_middle::mir::*; +use rustc_middle::ty::subst::SubstsRef; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::def_id::DefId; /// Checks if the specified `local` is used as the `self` prameter of a method call /// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is /// returned. -pub fn find_self_call( - tcx: TyCtxt<'_>, - body: &Body<'_>, +pub fn find_self_call<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, local: Local, block: BasicBlock, -) -> Option { +) -> Option<(DefId, SubstsRef<'tcx>)> { debug!("find_self_call(local={:?}): terminator={:?}", local, &body[block].terminator); if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) = &body[block].terminator { debug!("find_self_call: func={:?}", func); if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func { - if let ty::FnDef(def_id, _) = *ty.kind() { + if let ty::FnDef(def_id, substs) = *ty.kind() { if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) = tcx.opt_associated_item(def_id) { debug!("find_self_call: args={:?}", args); if let [Operand::Move(self_place) | Operand::Copy(self_place), ..] = **args { if self_place.as_local() == Some(local) { - return Some(def_id); + return Some((def_id, substs)); } } } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 70c7abc2654..13e69474cfb 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -117,7 +117,14 @@ fn apply_adjustment<'a, 'tcx>( }, }; - overloaded_place(cx, hir_expr, adjustment.target, Some(call), vec![expr.to_ref()]) + overloaded_place( + cx, + hir_expr, + adjustment.target, + Some(call), + vec![expr.to_ref()], + deref.span, + ) } Adjust::Borrow(AutoBorrow::Ref(_, m)) => { ExprKind::Borrow { borrow_kind: m.to_borrow_kind(), arg: expr.to_ref() } @@ -277,7 +284,14 @@ fn make_mirror_unadjusted<'a, 'tcx>( hir::ExprKind::Index(ref lhs, ref index) => { if cx.typeck_results().is_method_call(expr) { - overloaded_place(cx, expr, expr_ty, None, vec![lhs.to_ref(), index.to_ref()]) + overloaded_place( + cx, + expr, + expr_ty, + None, + vec![lhs.to_ref(), index.to_ref()], + expr.span, + ) } else { ExprKind::Index { lhs: lhs.to_ref(), index: index.to_ref() } } @@ -285,7 +299,7 @@ fn make_mirror_unadjusted<'a, 'tcx>( hir::ExprKind::Unary(hir::UnOp::UnDeref, ref arg) => { if cx.typeck_results().is_method_call(expr) { - overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()]) + overloaded_place(cx, expr, expr_ty, None, vec![arg.to_ref()], expr.span) } else { ExprKind::Deref { arg: arg.to_ref() } } @@ -1025,6 +1039,7 @@ fn overloaded_place<'a, 'tcx>( place_ty: Ty<'tcx>, overloaded_callee: Option<(DefId, SubstsRef<'tcx>)>, args: Vec>, + span: Span, ) -> ExprKind<'tcx> { // For an overloaded *x or x[y] expression of type T, the method // call returns an &T and we must add the deref so that the types @@ -1040,24 +1055,24 @@ fn overloaded_place<'a, 'tcx>( // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. let (region, mutbl) = match *recv_ty.kind() { ty::Ref(region, _, mutbl) => (region, mutbl), - _ => span_bug!(expr.span, "overloaded_place: receiver is not a reference"), + _ => span_bug!(span, "overloaded_place: receiver is not a reference"), }; let ref_ty = cx.tcx.mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); // construct the complete expression `foo()` for the overloaded call, // which will yield the &T type let temp_lifetime = cx.region_scope_tree.temporary_scope(expr.hir_id.local_id); - let fun = method_callee(cx, expr, expr.span, overloaded_callee); + let fun = method_callee(cx, expr, span, overloaded_callee); let ref_expr = Expr { temp_lifetime, ty: ref_ty, - span: expr.span, + span, kind: ExprKind::Call { ty: fun.ty, fun: fun.to_ref(), args, from_hir_call: false, - fn_span: expr.span, + fn_span: span, }, }; diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 407663e5757..94f795b11ea 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -416,7 +416,9 @@ symbols! { deny, deprecated, deref, + deref_method, deref_mut, + deref_target, derive, diagnostic, direct, diff --git a/compiler/rustc_trait_selection/src/autoderef.rs b/compiler/rustc_trait_selection/src/autoderef.rs index 02eefe56223..b9c5123e49a 100644 --- a/compiler/rustc_trait_selection/src/autoderef.rs +++ b/compiler/rustc_trait_selection/src/autoderef.rs @@ -27,6 +27,7 @@ pub struct Autoderef<'a, 'tcx> { // Meta infos: infcx: &'a InferCtxt<'a, 'tcx>, span: Span, + overloaded_span: Span, body_id: hir::HirId, param_env: ty::ParamEnv<'tcx>, @@ -98,10 +99,12 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { body_id: hir::HirId, span: Span, base_ty: Ty<'tcx>, + overloaded_span: Span, ) -> Autoderef<'a, 'tcx> { Autoderef { infcx, span, + overloaded_span, body_id, param_env, state: AutoderefSnapshot { @@ -190,6 +193,10 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> { self.span } + pub fn overloaded_span(&self) -> Span { + self.overloaded_span + } + pub fn reached_recursion_limit(&self) -> bool { self.state.reached_recursion_limit } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 82e809d014d..90a8d9634ae 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -483,7 +483,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { }; if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() { - let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty); + let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span); if let Some(steps) = autoderef.find_map(|(ty, steps)| { // Re-add the `&` let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl }); diff --git a/compiler/rustc_typeck/src/check/autoderef.rs b/compiler/rustc_typeck/src/check/autoderef.rs index 17364897bde..59c366ad7d7 100644 --- a/compiler/rustc_typeck/src/check/autoderef.rs +++ b/compiler/rustc_typeck/src/check/autoderef.rs @@ -12,7 +12,18 @@ use std::iter; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> { - Autoderef::new(self, self.param_env, self.body_id, span, base_ty) + Autoderef::new(self, self.param_env, self.body_id, span, base_ty, span) + } + + /// Like `autoderef`, but provides a custom `Span` to use for calls to + /// an overloaded `Deref` operator + pub fn autoderef_overloaded_span( + &'a self, + span: Span, + base_ty: Ty<'tcx>, + overloaded_span: Span, + ) -> Autoderef<'a, 'tcx> { + Autoderef::new(self, self.param_env, self.body_id, span, base_ty, overloaded_span) } pub fn try_overloaded_deref( @@ -44,7 +55,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |InferOk { value: method, obligations: o }| { obligations.extend(o); if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { - Some(OverloadedDeref { region, mutbl }) + Some(OverloadedDeref { + region, + mutbl, + span: autoderef.overloaded_span(), + }) } else { None } diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index ae338cccf79..fd2700b85e2 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -137,7 +137,8 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> Ty<'tcx> { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. - let mut autoderef = self.autoderef(self.span, unadjusted_self_ty); + let mut autoderef = + self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span); let (_, n) = match autoderef.nth(pick.autoderefs) { Some(n) => n, None => { diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 14d80fded71..37e8e82e60a 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -446,9 +446,10 @@ fn method_autoderef_steps<'tcx>( tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| { let ParamEnvAnd { param_env, value: self_ty } = goal; - let mut autoderef = Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty) - .include_raw_pointers() - .silence_errors(); + let mut autoderef = + Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) + .include_raw_pointers() + .silence_errors(); let mut reached_raw_pointer = false; let mut steps: Vec<_> = autoderef .by_ref() diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs index aed2af20e52..502cb562385 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs @@ -242,7 +242,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ) { let method = self.register_infer_ok_obligations(ok); if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { - *deref = OverloadedDeref { region, mutbl }; + *deref = OverloadedDeref { region, mutbl, span: deref.span }; } // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). // This helps avoid accidental drops. diff --git a/library/core/src/ops/deref.rs b/library/core/src/ops/deref.rs index d6c097eee17..245152e5490 100644 --- a/library/core/src/ops/deref.rs +++ b/library/core/src/ops/deref.rs @@ -63,11 +63,13 @@ pub trait Deref { /// The resulting type after dereferencing. #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "deref_target"] type Target: ?Sized; /// Dereferences the value. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_diagnostic_item = "deref_method"] fn deref(&self) -> &Self::Target; } diff --git a/src/test/ui/lint/lint-unconditional-recursion.stderr b/src/test/ui/lint/lint-unconditional-recursion.stderr index 1770d71e2e2..fb884e31299 100644 --- a/src/test/ui/lint/lint-unconditional-recursion.stderr +++ b/src/test/ui/lint/lint-unconditional-recursion.stderr @@ -149,7 +149,7 @@ error: function cannot return without recursing LL | fn deref(&self) -> &Baz { | ^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing LL | self.as_ref() - | ---- recursive call site + | ------------- recursive call site | = help: a `loop` may express intention better if this is on purpose diff --git a/src/test/ui/moves/move-deref-coercion.rs b/src/test/ui/moves/move-deref-coercion.rs new file mode 100644 index 00000000000..41154388f56 --- /dev/null +++ b/src/test/ui/moves/move-deref-coercion.rs @@ -0,0 +1,33 @@ +use std::ops::Deref; + +struct NotCopy { + inner: bool +} + +impl NotCopy { + fn inner_method(&self) {} +} + +struct Foo { + first: NotCopy, + second: NotCopy +} + +impl Deref for Foo { + type Target = NotCopy; + fn deref(&self) -> &NotCopy { + &self.second + } +} + +fn use_field(val: Foo) { + let _val = val.first; + val.inner; //~ ERROR borrow of +} + +fn use_method(val: Foo) { + let _val = val.first; + val.inner_method(); //~ ERROR borrow of +} + +fn main() {} diff --git a/src/test/ui/moves/move-deref-coercion.stderr b/src/test/ui/moves/move-deref-coercion.stderr new file mode 100644 index 00000000000..e3bdf6d7832 --- /dev/null +++ b/src/test/ui/moves/move-deref-coercion.stderr @@ -0,0 +1,35 @@ +error[E0382]: borrow of partially moved value: `val` + --> $DIR/move-deref-coercion.rs:25:5 + | +LL | let _val = val.first; + | --------- value partially moved here +LL | val.inner; + | ^^^^^^^^^ value borrowed here after partial move + | + = note: partial move occurs because `val.first` has type `NotCopy`, which does not implement the `Copy` trait + = note: borrow occurs due to deref coercion to `NotCopy` +note: deref defined here + --> $DIR/move-deref-coercion.rs:17:5 + | +LL | type Target = NotCopy; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error[E0382]: borrow of partially moved value: `val` + --> $DIR/move-deref-coercion.rs:30:5 + | +LL | let _val = val.first; + | --------- value partially moved here +LL | val.inner_method(); + | ^^^^^^^^^^^^^^^^^^ value borrowed here after partial move + | + = note: partial move occurs because `val.first` has type `NotCopy`, which does not implement the `Copy` trait + = note: borrow occurs due to deref coercion to `NotCopy` +note: deref defined here + --> $DIR/move-deref-coercion.rs:17:5 + | +LL | type Target = NotCopy; + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0382`. diff --git a/src/test/ui/no-capture-arc.stderr b/src/test/ui/no-capture-arc.stderr index 0081841fec0..37032e73f19 100644 --- a/src/test/ui/no-capture-arc.stderr +++ b/src/test/ui/no-capture-arc.stderr @@ -1,5 +1,5 @@ error[E0382]: borrow of moved value: `arc_v` - --> $DIR/no-capture-arc.rs:14:18 + --> $DIR/no-capture-arc.rs:14:16 | LL | let arc_v = Arc::new(v); | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait @@ -10,7 +10,14 @@ LL | assert_eq!((*arc_v)[3], 4); | ----- variable moved due to use in closure ... LL | assert_eq!((*arc_v)[2], 3); - | ^^^^^ value borrowed here after move + | ^^^^^^^^ value borrowed here after move + | + = note: borrow occurs due to deref coercion to `Vec` +note: deref defined here + --> $SRC_DIR/alloc/src/sync.rs:LL:COL + | +LL | type Target = T; + | ^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/no-reuse-move-arc.stderr b/src/test/ui/no-reuse-move-arc.stderr index bb4e0871224..6f37d4c9d86 100644 --- a/src/test/ui/no-reuse-move-arc.stderr +++ b/src/test/ui/no-reuse-move-arc.stderr @@ -1,5 +1,5 @@ error[E0382]: borrow of moved value: `arc_v` - --> $DIR/no-reuse-move-arc.rs:12:18 + --> $DIR/no-reuse-move-arc.rs:12:16 | LL | let arc_v = Arc::new(v); | ----- move occurs because `arc_v` has type `Arc>`, which does not implement the `Copy` trait @@ -10,7 +10,14 @@ LL | assert_eq!((*arc_v)[3], 4); | ----- variable moved due to use in closure ... LL | assert_eq!((*arc_v)[2], 3); - | ^^^^^ value borrowed here after move + | ^^^^^^^^ value borrowed here after move + | + = note: borrow occurs due to deref coercion to `Vec` +note: deref defined here + --> $SRC_DIR/alloc/src/sync.rs:LL:COL + | +LL | type Target = T; + | ^^^^^^^^^^^^^^^^ error: aborting due to previous error