From 7e79dcdc06a2bbd4a1d3250b23dbfc024bf0a86f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 9 Mar 2024 20:02:47 +0000 Subject: [PATCH 1/4] Drive-by fix string fmt --- compiler/rustc_borrowck/src/diagnostics/mod.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 560928d3941..4ebbe592628 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1226,20 +1226,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { { let msg = match &errors[..] { [] => "you can `clone` the value and consume it, but this \ - might not be your desired behavior" + might not be your desired behavior" .to_string(), [error] => { format!( - "you could `clone` the value and consume it, if \ - the `{}` trait bound could be satisfied", + "you could `clone` the value and consume it, if the \ + `{}` trait bound could be satisfied", error.obligation.predicate, ) } [errors @ .., last] => { format!( - "you could `clone` the value and consume it, if \ - the following trait bounds could be satisfied: {} \ - and `{}`", + "you could `clone` the value and consume it, if the \ + following trait bounds could be satisfied: \ + {} and `{}`", errors .iter() .map(|e| format!("`{}`", e.obligation.predicate)) From 2d3435b4df844bb0c25b86bb73519a3c867a92a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 9 Mar 2024 20:18:43 +0000 Subject: [PATCH 2/4] Detect calls to `.clone()` on `T: !Clone` types on borrowck errors When encountering a lifetime error on a type that *holds* a type that doesn't implement `Clone`, explore the item's body for potential calls to `.clone()` that are only cloning the reference `&T` instead of `T` because `T: !Clone`. If we find this, suggest `T: Clone`. ``` error[E0502]: cannot borrow `*list` as mutable because it is also borrowed as immutable --> $DIR/clone-on-ref.rs:7:5 | LL | for v in list.iter() { | ---- immutable borrow occurs here LL | cloned_items.push(v.clone()) | ------- this call doesn't do anything, the result is still `&T` because `T` doesn't implement `Clone` LL | } LL | list.push(T::default()); | ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here LL | LL | drop(cloned_items); | ------------ immutable borrow later used here | help: consider further restricting this bound | LL | fn foo(list: &mut Vec) { | +++++++ ``` ``` error[E0505]: cannot move out of `x` because it is borrowed --> $DIR/clone-on-ref.rs:23:10 | LL | fn qux(x: A) { | - binding `x` declared here LL | let a = &x; | -- borrow of `x` occurs here LL | let b = a.clone(); | ------- this call doesn't do anything, the result is still `&A` because `A` doesn't implement `Clone` LL | drop(x); | ^ move out of `x` occurs here LL | LL | println!("{b:?}"); | ----- borrow later used here | help: consider annotating `A` with `#[derive(Clone)]` | LL + #[derive(Clone)] LL | struct A; | ``` --- .../src/diagnostics/conflict_errors.rs | 113 ++++++++++++++++-- tests/ui/borrowck/clone-on-ref.fixed | 32 +++++ tests/ui/borrowck/clone-on-ref.rs | 31 +++++ tests/ui/borrowck/clone-on-ref.stderr | 64 ++++++++++ 4 files changed, 233 insertions(+), 7 deletions(-) create mode 100644 tests/ui/borrowck/clone-on-ref.fixed create mode 100644 tests/ui/borrowck/clone-on-ref.rs create mode 100644 tests/ui/borrowck/clone-on-ref.stderr diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 0776f455efd..fcbad472475 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -12,7 +12,6 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{CoroutineDesugaring, PatField}; use rustc_hir::{CoroutineKind, CoroutineSource, LangItem}; -use rustc_infer::traits::ObligationCause; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::mir::tcx::PlaceTy; use rustc_middle::mir::{ @@ -21,16 +20,21 @@ use rustc_middle::mir::{ PlaceRef, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, VarBindingForm, }; -use rustc_middle::ty::{self, suggest_constraining_type_params, PredicateKind, Ty, TyCtxt}; +use rustc_middle::ty::{ + self, suggest_constraining_type_params, PredicateKind, ToPredicate, Ty, TyCtxt, + TypeSuperVisitable, TypeVisitor, +}; use rustc_middle::util::CallKind; use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex}; +use rustc_span::def_id::DefId; use rustc_span::def_id::LocalDefId; use rustc_span::hygiene::DesugaringKind; use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{BytePos, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt; use rustc_trait_selection::traits::error_reporting::FindExprBySpan; -use rustc_trait_selection::traits::ObligationCtxt; +use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; use std::iter; use crate::borrow_set::TwoPhaseActivation; @@ -283,7 +287,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // something that already has `Fn`-like bounds (or is a closure), so we can't // restrict anyways. } else { - self.suggest_adding_copy_bounds(&mut err, ty, span); + let copy_did = self.infcx.tcx.require_lang_item(LangItem::Copy, Some(span)); + self.suggest_adding_bounds(&mut err, ty, copy_did, span); } if needs_note { @@ -775,7 +780,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { } } - fn suggest_adding_copy_bounds(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, span: Span) { + fn suggest_adding_bounds(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, def_id: DefId, span: Span) { let tcx = self.infcx.tcx; let generics = tcx.generics_of(self.mir_def_id()); @@ -788,10 +793,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }; // Try to find predicates on *generic params* that would allow copying `ty` let ocx = ObligationCtxt::new(self.infcx); - let copy_did = tcx.require_lang_item(LangItem::Copy, Some(span)); let cause = ObligationCause::misc(span, self.mir_def_id()); - ocx.register_bound(cause, self.param_env, ty, copy_did); + ocx.register_bound(cause, self.param_env, ty, def_id); let errors = ocx.select_all_or_error(); // Only emit suggestion if all required predicates are on generic @@ -877,6 +881,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { Some(borrow_span), None, ); + self.suggest_copy_for_type_in_cloned_ref(&mut err, place); self.buffer_error(err); } @@ -1215,10 +1220,104 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); self.suggest_using_local_if_applicable(&mut err, location, issued_borrow, explanation); + self.suggest_copy_for_type_in_cloned_ref(&mut err, place); err } + fn suggest_copy_for_type_in_cloned_ref(&self, err: &mut Diag<'tcx>, place: Place<'tcx>) { + let tcx = self.infcx.tcx; + let hir = tcx.hir(); + let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return }; + struct FindUselessClone<'hir> { + pub clones: Vec<&'hir hir::Expr<'hir>>, + } + impl<'hir> FindUselessClone<'hir> { + pub fn new() -> Self { + Self { clones: vec![] } + } + } + + impl<'v> Visitor<'v> for FindUselessClone<'v> { + fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { + // FIXME: use `lookup_method_for_diagnostic`? + if let hir::ExprKind::MethodCall(segment, _rcvr, args, _span) = ex.kind + && segment.ident.name == sym::clone + && args.len() == 0 + { + self.clones.push(ex); + } + hir::intravisit::walk_expr(self, ex); + } + } + let mut expr_finder = FindUselessClone::new(); + + let body = hir.body(body_id).value; + expr_finder.visit_expr(body); + + pub struct Holds<'tcx> { + ty: Ty<'tcx>, + holds: bool, + } + + impl<'tcx> TypeVisitor> for Holds<'tcx> { + type Result = std::ops::ControlFlow<()>; + + fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result { + if t == self.ty { + self.holds = true; + } + t.super_visit_with(self) + } + } + + let mut types_to_constrain = FxIndexSet::default(); + + let local_ty = self.body.local_decls[place.local].ty; + let typeck_results = tcx.typeck(self.mir_def_id()); + let clone = tcx.require_lang_item(LangItem::Clone, Some(body.span)); + for expr in expr_finder.clones { + if let hir::ExprKind::MethodCall(_, rcvr, _, span) = expr.kind + && let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id) + && let Some(ty) = typeck_results.node_type_opt(expr.hir_id) + && rcvr_ty == ty + && let ty::Ref(_, inner, _) = rcvr_ty.kind() + && let inner = inner.peel_refs() + && let mut v = (Holds { ty: inner, holds: false }) + && let _ = v.visit_ty(local_ty) + && v.holds + && let None = self.infcx.type_implements_trait_shallow(clone, inner, self.param_env) + { + err.span_label( + span, + format!( + "this call doesn't do anything, the result is still `{rcvr_ty}` \ + because `{inner}` doesn't implement `Clone`", + ), + ); + types_to_constrain.insert(inner); + } + } + for ty in types_to_constrain { + self.suggest_adding_bounds(err, ty, clone, body.span); + if let ty::Adt(..) = ty.kind() { + // The type doesn't implement Clone. + let trait_ref = ty::Binder::dummy(ty::TraitRef::new(self.infcx.tcx, clone, [ty])); + let obligation = Obligation::new( + self.infcx.tcx, + ObligationCause::dummy(), + self.param_env, + trait_ref, + ); + self.infcx.err_ctxt().suggest_derive( + &obligation, + err, + trait_ref.to_predicate(self.infcx.tcx), + ); + } + } + } + #[instrument(level = "debug", skip(self, err))] fn suggest_using_local_if_applicable( &self, diff --git a/tests/ui/borrowck/clone-on-ref.fixed b/tests/ui/borrowck/clone-on-ref.fixed new file mode 100644 index 00000000000..b6927ba590e --- /dev/null +++ b/tests/ui/borrowck/clone-on-ref.fixed @@ -0,0 +1,32 @@ +//@ run-rustfix +fn foo(list: &mut Vec) { + let mut cloned_items = Vec::new(); + for v in list.iter() { + cloned_items.push(v.clone()) + } + list.push(T::default()); + //~^ ERROR cannot borrow `*list` as mutable because it is also borrowed as immutable + drop(cloned_items); +} +fn bar(x: T) { + let a = &x; + let b = a.clone(); + drop(x); + //~^ ERROR cannot move out of `x` because it is borrowed + println!("{b}"); +} +#[derive(Debug)] +#[derive(Clone)] +struct A; +fn qux(x: A) { + let a = &x; + let b = a.clone(); + drop(x); + //~^ ERROR cannot move out of `x` because it is borrowed + println!("{b:?}"); +} +fn main() { + foo(&mut vec![1, 2, 3]); + bar(""); + qux(A); +} diff --git a/tests/ui/borrowck/clone-on-ref.rs b/tests/ui/borrowck/clone-on-ref.rs new file mode 100644 index 00000000000..f8c94d3cce3 --- /dev/null +++ b/tests/ui/borrowck/clone-on-ref.rs @@ -0,0 +1,31 @@ +//@ run-rustfix +fn foo(list: &mut Vec) { + let mut cloned_items = Vec::new(); + for v in list.iter() { + cloned_items.push(v.clone()) + } + list.push(T::default()); + //~^ ERROR cannot borrow `*list` as mutable because it is also borrowed as immutable + drop(cloned_items); +} +fn bar(x: T) { + let a = &x; + let b = a.clone(); + drop(x); + //~^ ERROR cannot move out of `x` because it is borrowed + println!("{b}"); +} +#[derive(Debug)] +struct A; +fn qux(x: A) { + let a = &x; + let b = a.clone(); + drop(x); + //~^ ERROR cannot move out of `x` because it is borrowed + println!("{b:?}"); +} +fn main() { + foo(&mut vec![1, 2, 3]); + bar(""); + qux(A); +} diff --git a/tests/ui/borrowck/clone-on-ref.stderr b/tests/ui/borrowck/clone-on-ref.stderr new file mode 100644 index 00000000000..ee4fcadf55a --- /dev/null +++ b/tests/ui/borrowck/clone-on-ref.stderr @@ -0,0 +1,64 @@ +error[E0502]: cannot borrow `*list` as mutable because it is also borrowed as immutable + --> $DIR/clone-on-ref.rs:7:5 + | +LL | for v in list.iter() { + | ---- immutable borrow occurs here +LL | cloned_items.push(v.clone()) + | ------- this call doesn't do anything, the result is still `&T` because `T` doesn't implement `Clone` +LL | } +LL | list.push(T::default()); + | ^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here +LL | +LL | drop(cloned_items); + | ------------ immutable borrow later used here + | +help: consider further restricting this bound + | +LL | fn foo(list: &mut Vec) { + | +++++++ + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/clone-on-ref.rs:14:10 + | +LL | fn bar(x: T) { + | - binding `x` declared here +LL | let a = &x; + | -- borrow of `x` occurs here +LL | let b = a.clone(); + | ------- this call doesn't do anything, the result is still `&T` because `T` doesn't implement `Clone` +LL | drop(x); + | ^ move out of `x` occurs here +LL | +LL | println!("{b}"); + | --- borrow later used here + | +help: consider further restricting this bound + | +LL | fn bar(x: T) { + | +++++++ + +error[E0505]: cannot move out of `x` because it is borrowed + --> $DIR/clone-on-ref.rs:23:10 + | +LL | fn qux(x: A) { + | - binding `x` declared here +LL | let a = &x; + | -- borrow of `x` occurs here +LL | let b = a.clone(); + | ------- this call doesn't do anything, the result is still `&A` because `A` doesn't implement `Clone` +LL | drop(x); + | ^ move out of `x` occurs here +LL | +LL | println!("{b:?}"); + | ----- borrow later used here + | +help: consider annotating `A` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct A; + | + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0502, E0505. +For more information about an error, try `rustc --explain E0502`. From b367c25367117a4ada5c9a1c807b74f0efcf6d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 9 Mar 2024 20:32:13 +0000 Subject: [PATCH 3/4] Tweak wording --- compiler/rustc_mir_build/messages.ftl | 2 +- ...can-live-while-the-other-survives-1.stderr | 4 +- ...t-by-move-and-ref-inverse-promotion.stderr | 2 +- ...orrowck-pat-by-move-and-ref-inverse.stderr | 50 +++++++++---------- .../borrowck-pat-ref-mut-twice.stderr | 8 +-- ...inding-modes-both-sides-independent.stderr | 2 +- .../ui/suggestions/ref-pattern-binding.stderr | 4 +- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_mir_build/messages.ftl b/compiler/rustc_mir_build/messages.ftl index 8a6ccdb8578..1de691f32a7 100644 --- a/compiler/rustc_mir_build/messages.ftl +++ b/compiler/rustc_mir_build/messages.ftl @@ -24,7 +24,7 @@ mir_build_borrow_of_layout_constrained_field_requires_unsafe_unsafe_op_in_unsafe mir_build_borrow_of_moved_value = borrow of moved value .label = value moved into `{$name}` here - .occurs_because_label = move occurs because `{$name}` has type `{$ty}` which does not implement the `Copy` trait + .occurs_because_label = move occurs because `{$name}` has type `{$ty}`, which does not implement the `Copy` trait .value_borrowed_label = value borrowed here after move .suggestion = borrow this binding in the pattern to avoid moving the value diff --git a/tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr b/tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr index 25838fbf0ab..16a93a0d47d 100644 --- a/tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr +++ b/tests/ui/pattern/bindings-after-at/bind-by-move-neither-can-live-while-the-other-survives-1.stderr @@ -13,7 +13,7 @@ LL | Some(_z @ ref _y) => {} | ^^ ------ value borrowed here after move | | | value moved into `_z` here - | move occurs because `_z` has type `X` which does not implement the `Copy` trait + | move occurs because `_z` has type `X`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -35,7 +35,7 @@ LL | Some(_z @ ref mut _y) => {} | ^^ ---------- value borrowed here after move | | | value moved into `_z` here - | move occurs because `_z` has type `X` which does not implement the `Copy` trait + | move occurs because `_z` has type `X`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr index 815a4ade995..ea04d4bc055 100644 --- a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr +++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse-promotion.stderr @@ -5,7 +5,7 @@ LL | let a @ ref b = U; | ^ ----- value borrowed here after move | | | value moved into `a` here - | move occurs because `a` has type `U` which does not implement the `Copy` trait + | move occurs because `a` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr index fd7a51388bc..25c940a3200 100644 --- a/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr +++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-by-move-and-ref-inverse.stderr @@ -5,7 +5,7 @@ LL | let a @ ref b = U; | ^ ----- value borrowed here after move | | | value moved into `a` here - | move occurs because `a` has type `U` which does not implement the `Copy` trait + | move occurs because `a` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -20,7 +20,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (U, U); | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait + | move occurs because `a` has type `(U, U)`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -34,7 +34,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (U, U); | ^^^^^ --------- value borrowed here after move | | | value moved into `b` here - | move occurs because `b` has type `U` which does not implement the `Copy` trait + | move occurs because `b` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -48,7 +48,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (U, U); | ^ ----- value borrowed here after move | | | value moved into `d` here - | move occurs because `d` has type `U` which does not implement the `Copy` trait + | move occurs because `d` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -63,7 +63,7 @@ LL | let a @ [ref mut b, ref c] = [U, U]; | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `[U; 2]` which does not implement the `Copy` trait + | move occurs because `a` has type `[U; 2]`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -77,7 +77,7 @@ LL | let a @ ref b = u(); | ^ ----- value borrowed here after move | | | value moved into `a` here - | move occurs because `a` has type `U` which does not implement the `Copy` trait + | move occurs because `a` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -92,7 +92,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (u(), u()); | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait + | move occurs because `a` has type `(U, U)`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -106,7 +106,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (u(), u()); | ^^^^^ --------- value borrowed here after move | | | value moved into `b` here - | move occurs because `b` has type `U` which does not implement the `Copy` trait + | move occurs because `b` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -120,7 +120,7 @@ LL | let a @ (mut b @ ref mut c, d @ ref e) = (u(), u()); | ^ ----- value borrowed here after move | | | value moved into `d` here - | move occurs because `d` has type `U` which does not implement the `Copy` trait + | move occurs because `d` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -135,7 +135,7 @@ LL | let a @ [ref mut b, ref c] = [u(), u()]; | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `[U; 2]` which does not implement the `Copy` trait + | move occurs because `a` has type `[U; 2]`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -149,7 +149,7 @@ LL | a @ Some(ref b) => {} | ^ ----- value borrowed here after move | | | value moved into `a` here - | move occurs because `a` has type `Option` which does not implement the `Copy` trait + | move occurs because `a` has type `Option`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -164,7 +164,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `Option<(U, U)>` which does not implement the `Copy` trait + | move occurs because `a` has type `Option<(U, U)>`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -178,7 +178,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | ^^^^^ --------- value borrowed here after move | | | value moved into `b` here - | move occurs because `b` has type `U` which does not implement the `Copy` trait + | move occurs because `b` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -192,7 +192,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | ^ ----- value borrowed here after move | | | value moved into `d` here - | move occurs because `d` has type `U` which does not implement the `Copy` trait + | move occurs because `d` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -207,7 +207,7 @@ LL | mut a @ Some([ref b, ref mut c]) => {} | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `Option<[U; 2]>` which does not implement the `Copy` trait + | move occurs because `a` has type `Option<[U; 2]>`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -221,7 +221,7 @@ LL | a @ Some(ref b) => {} | ^ ----- value borrowed here after move | | | value moved into `a` here - | move occurs because `a` has type `Option` which does not implement the `Copy` trait + | move occurs because `a` has type `Option`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -236,7 +236,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `Option<(U, U)>` which does not implement the `Copy` trait + | move occurs because `a` has type `Option<(U, U)>`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -250,7 +250,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | ^^^^^ --------- value borrowed here after move | | | value moved into `b` here - | move occurs because `b` has type `U` which does not implement the `Copy` trait + | move occurs because `b` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -264,7 +264,7 @@ LL | a @ Some((mut b @ ref mut c, d @ ref e)) => {} | ^ ----- value borrowed here after move | | | value moved into `d` here - | move occurs because `d` has type `U` which does not implement the `Copy` trait + | move occurs because `d` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -279,7 +279,7 @@ LL | mut a @ Some([ref b, ref mut c]) => {} | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `Option<[U; 2]>` which does not implement the `Copy` trait + | move occurs because `a` has type `Option<[U; 2]>`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -349,7 +349,7 @@ LL | fn f1(a @ ref b: U) {} | ^ ----- value borrowed here after move | | | value moved into `a` here - | move occurs because `a` has type `U` which does not implement the `Copy` trait + | move occurs because `a` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -364,7 +364,7 @@ LL | fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {} | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait + | move occurs because `a` has type `(U, U)`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -378,7 +378,7 @@ LL | fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {} | ^ ----- value borrowed here after move | | | value moved into `b` here - | move occurs because `b` has type `U` which does not implement the `Copy` trait + | move occurs because `b` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -392,7 +392,7 @@ LL | fn f2(mut a @ (b @ ref c, mut d @ ref e): (U, U)) {} | ^^^^^ ----- value borrowed here after move | | | value moved into `d` here - | move occurs because `d` has type `U` which does not implement the `Copy` trait + | move occurs because `d` has type `U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -417,7 +417,7 @@ LL | fn f3(a @ [ref mut b, ref c]: [U; 2]) {} | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `[U; 2]` which does not implement the `Copy` trait + | move occurs because `a` has type `[U; 2]`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | diff --git a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr index 3446148d2b1..76085f897ff 100644 --- a/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr +++ b/tests/ui/pattern/bindings-after-at/borrowck-pat-ref-mut-twice.stderr @@ -78,7 +78,7 @@ LL | let a @ (ref mut b, ref mut c) = (U, U); | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `(U, U)` which does not implement the `Copy` trait + | move occurs because `a` has type `(U, U)`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -94,7 +94,7 @@ LL | let a @ (b, [c, d]) = &mut val; // Same as ^-- | | | value borrowed here after move | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `&mut (U, [U; 2])` which does not implement the `Copy` trait + | move occurs because `a` has type `&mut (U, [U; 2])`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -108,7 +108,7 @@ LL | let a @ &mut ref mut b = &mut U; | ^ --------- value borrowed here after move | | | value moved into `a` here - | move occurs because `a` has type `&mut U` which does not implement the `Copy` trait + | move occurs because `a` has type `&mut U`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -123,7 +123,7 @@ LL | let a @ &mut (ref mut b, ref mut c) = &mut (U, U); | | | | | value borrowed here after move | value moved into `a` here - | move occurs because `a` has type `&mut (U, U)` which does not implement the `Copy` trait + | move occurs because `a` has type `&mut (U, U)`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | diff --git a/tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr b/tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr index 36515c1a29b..a0a43e83a6a 100644 --- a/tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr +++ b/tests/ui/pattern/bindings-after-at/default-binding-modes-both-sides-independent.stderr @@ -29,7 +29,7 @@ LL | Ok(ref a @ b) | Err(b @ ref a) => { | ^ ----- value borrowed here after move | | | value moved into `b` here - | move occurs because `b` has type `NotCopy` which does not implement the `Copy` trait + | move occurs because `b` has type `NotCopy`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | diff --git a/tests/ui/suggestions/ref-pattern-binding.stderr b/tests/ui/suggestions/ref-pattern-binding.stderr index 69ce5d440af..af21c6aef70 100644 --- a/tests/ui/suggestions/ref-pattern-binding.stderr +++ b/tests/ui/suggestions/ref-pattern-binding.stderr @@ -5,7 +5,7 @@ LL | let _moved @ ref _from = String::from("foo"); | ^^^^^^ --------- value borrowed here after move | | | value moved into `_moved` here - | move occurs because `_moved` has type `String` which does not implement the `Copy` trait + | move occurs because `_moved` has type `String`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | @@ -35,7 +35,7 @@ LL | let _moved @ S { ref f } = S { f: String::from("foo") }; | ^^^^^^ ----- value borrowed here after move | | | value moved into `_moved` here - | move occurs because `_moved` has type `S` which does not implement the `Copy` trait + | move occurs because `_moved` has type `S`, which does not implement the `Copy` trait | help: borrow this binding in the pattern to avoid moving the value | From 0953608debfa9d3df955f976e7bc857584ba1ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 12 Mar 2024 17:21:15 +0000 Subject: [PATCH 4/4] Account for UnOps in borrowck message --- compiler/rustc_borrowck/messages.ftl | 3 +++ compiler/rustc_borrowck/src/diagnostics/mod.rs | 11 +++++++++-- compiler/rustc_borrowck/src/session_diagnostics.rs | 5 +++++ tests/ui/unop-move-semantics.stderr | 4 ++-- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_borrowck/messages.ftl b/compiler/rustc_borrowck/messages.ftl index bf14d5eb9a0..f2ca509e14b 100644 --- a/compiler/rustc_borrowck/messages.ftl +++ b/compiler/rustc_borrowck/messages.ftl @@ -16,6 +16,9 @@ borrowck_borrow_due_to_use_closure = borrowck_borrow_due_to_use_coroutine = borrow occurs due to use in coroutine +borrowck_calling_operator_moves = + calling this operator moves the value + borrowck_calling_operator_moves_lhs = calling this operator moves the left-hand side diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 4ebbe592628..914f68a38b4 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -1050,7 +1050,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ); err.subdiagnostic(self.dcx(), CaptureReasonNote::FnOnceMoveInCall { var_span }); } - CallKind::Operator { self_arg, .. } => { + CallKind::Operator { self_arg, trait_id, .. } => { let self_arg = self_arg.unwrap(); err.subdiagnostic( self.dcx(), @@ -1062,9 +1062,16 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { }, ); if self.fn_self_span_reported.insert(fn_span) { + let lang = self.infcx.tcx.lang_items(); err.subdiagnostic( self.dcx(), - CaptureReasonNote::LhsMoveByOperator { span: self_arg.span }, + if [lang.not_trait(), lang.deref_trait(), lang.neg_trait()] + .contains(&Some(trait_id)) + { + CaptureReasonNote::UnOpMoveByOperator { span: self_arg.span } + } else { + CaptureReasonNote::LhsMoveByOperator { span: self_arg.span } + }, ); } } diff --git a/compiler/rustc_borrowck/src/session_diagnostics.rs b/compiler/rustc_borrowck/src/session_diagnostics.rs index a055ce95e8e..77021ae4321 100644 --- a/compiler/rustc_borrowck/src/session_diagnostics.rs +++ b/compiler/rustc_borrowck/src/session_diagnostics.rs @@ -368,6 +368,11 @@ pub(crate) enum CaptureReasonNote { #[primary_span] var_span: Span, }, + #[note(borrowck_calling_operator_moves)] + UnOpMoveByOperator { + #[primary_span] + span: Span, + }, #[note(borrowck_calling_operator_moves_lhs)] LhsMoveByOperator { #[primary_span] diff --git a/tests/ui/unop-move-semantics.stderr b/tests/ui/unop-move-semantics.stderr index b6de7976ac9..187dd66b2fe 100644 --- a/tests/ui/unop-move-semantics.stderr +++ b/tests/ui/unop-move-semantics.stderr @@ -9,7 +9,7 @@ LL | LL | x.clone(); | ^ value borrowed here after move | -note: calling this operator moves the left-hand side +note: calling this operator moves the value --> $SRC_DIR/core/src/ops/bit.rs:LL:COL help: consider cloning the value if the performance cost is acceptable | @@ -57,7 +57,7 @@ LL | !*m; | |move occurs because `*m` has type `T`, which does not implement the `Copy` trait | `*m` moved due to usage in operator | -note: calling this operator moves the left-hand side +note: calling this operator moves the value --> $SRC_DIR/core/src/ops/bit.rs:LL:COL error[E0507]: cannot move out of `*n` which is behind a shared reference