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:
parent
b7fb220865
commit
7b62d97abd
3 changed files with 105 additions and 2 deletions
|
|
@ -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,
|
||||
|
|
|
|||
32
tests/ui/borrowck/derive-clone-implicit-bound.rs
Normal file
32
tests/ui/borrowck/derive-clone-implicit-bound.rs
Normal 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() {}
|
||||
38
tests/ui/borrowck/derive-clone-implicit-bound.stderr
Normal file
38
tests/ui/borrowck/derive-clone-implicit-bound.stderr
Normal 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`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue