Mention implicit bounds from #[derive(Clone)] on moved value

When encountering a value that has a borrow checker error where the type was previously moved, when suggesting cloning verify that it is not already being derived. If it is, explain why the `derive(Clone)` doesn't apply:

```
note: if `TypedAddress<T>` implemented `Clone`, you could clone the value
  --> $DIR/derive-clone-implicit-bound.rs:6:1
   |
LL | #[derive(Clone, Copy)]
   |          ----- derived `Clone` adds implicit bounds on type parameters
LL | pub struct TypedAddress<T>{
   | ^^^^^^^^^^^^^^^^^^^^^^^^-^
   | |                       |
   | |                       introduces an implicit `T: Clone` bound
   | consider manually implementing `Clone` for this type
...
LL |         let old = self.return_value(offset);
   |                                     ------ you could clone this value
```
This commit is contained in:
Esteban Küber 2026-01-17 00:19:20 +00:00
parent b7fb220865
commit 7b62d97abd
3 changed files with 105 additions and 2 deletions

View file

@ -1256,7 +1256,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
self.suggest_cloning_inner(err, ty, expr);
}
} else if let ty::Adt(def, args) = ty.kind()
&& def.did().as_local().is_some()
&& let Some(local_did) = def.did().as_local()
&& def.variants().iter().all(|variant| {
variant
.fields
@ -1266,7 +1266,40 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
{
let ty_span = self.infcx.tcx.def_span(def.did());
let mut span: MultiSpan = ty_span.into();
span.push_span_label(ty_span, "consider implementing `Clone` for this type");
let mut derive_clone = false;
self.infcx.tcx.for_each_relevant_impl(
self.infcx.tcx.lang_items().clone_trait().unwrap(),
ty,
|def_id| {
if self.infcx.tcx.is_automatically_derived(def_id) {
derive_clone = true;
span.push_span_label(
self.infcx.tcx.def_span(def_id),
"derived `Clone` adds implicit bounds on type parameters",
);
if let Some(generics) = self.infcx.tcx.hir_get_generics(local_did) {
for param in generics.params {
if let hir::GenericParamKind::Type { .. } = param.kind {
span.push_span_label(
param.span,
format!(
"introduces an implicit `{}: Clone` bound",
param.name.ident()
),
);
}
}
}
}
},
);
span.push_span_label(
ty_span,
format!(
"consider {}implementing `Clone` for this type",
if derive_clone { "manually " } else { "" }
),
);
span.push_span_label(expr.span, "you could clone this value");
err.span_note(
span,

View file

@ -0,0 +1,32 @@
// Issue #108894
use std::marker::PhantomData;
#[derive(Clone, Copy)] //~ NOTE derived `Clone` adds implicit bounds on type parameters
pub struct TypedAddress<T>{
//~^ NOTE if `TypedAddress<T>` implemented `Clone`, you could clone the value
//~| NOTE consider manually implementing `Clone` for this type
//~| NOTE introduces an implicit `T: Clone` bound
inner: u64,
phantom: PhantomData<T>,
}
pub trait Memory {
fn write_value<T>(&self, offset: TypedAddress<T>, value: &T);
fn return_value<T>(&self, offset: TypedAddress<T>) -> T;
//~^ NOTE consider changing this parameter type in method `return_value` to borrow instead if owning the value isn't necessary
//~| NOTE in this method
//~| NOTE this parameter takes ownership of the value
fn update_value<T, F>(&self, offset: TypedAddress<T>, update: F)
//~^ NOTE move occurs because `offset` has type `TypedAddress<T>`, which does not implement the `Copy` trait
where F: FnOnce(T) -> T
{
let old = self.return_value(offset); //~ NOTE value moved here
//~^ NOTE you could clone this value
let new = update(old);
self.write_value(offset, &new); //~ ERROR use of moved value: `offset`
//~^ NOTE value used here after move
}
}
fn main() {}

View file

@ -0,0 +1,38 @@
error[E0382]: use of moved value: `offset`
--> $DIR/derive-clone-implicit-bound.rs:27:26
|
LL | fn update_value<T, F>(&self, offset: TypedAddress<T>, update: F)
| ------ move occurs because `offset` has type `TypedAddress<T>`, which does not implement the `Copy` trait
...
LL | let old = self.return_value(offset);
| ------ value moved here
...
LL | self.write_value(offset, &new);
| ^^^^^^ value used here after move
|
note: consider changing this parameter type in method `return_value` to borrow instead if owning the value isn't necessary
--> $DIR/derive-clone-implicit-bound.rs:16:39
|
LL | fn return_value<T>(&self, offset: TypedAddress<T>) -> T;
| ------------ in this method ^^^^^^^^^^^^^^^ this parameter takes ownership of the value
note: if `TypedAddress<T>` implemented `Clone`, you could clone the value
--> $DIR/derive-clone-implicit-bound.rs:6:1
|
LL | #[derive(Clone, Copy)]
| ----- derived `Clone` adds implicit bounds on type parameters
LL | pub struct TypedAddress<T>{
| ^^^^^^^^^^^^^^^^^^^^^^^^-^
| | |
| | introduces an implicit `T: Clone` bound
| consider manually implementing `Clone` for this type
...
LL | let old = self.return_value(offset);
| ------ you could clone this value
help: consider further restricting type parameter `T` with trait `Copy`
|
LL | where F: FnOnce(T) -> T, T: Copy
| +++++++++
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0382`.