Skip to content

Commit fbb6fac

Browse files
committed
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 ```
1 parent 503745e commit fbb6fac

File tree

3 files changed

+105
-2
lines changed

3 files changed

+105
-2
lines changed

compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,7 +1259,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12591259
self.suggest_cloning_inner(err, ty, expr);
12601260
}
12611261
} else if let ty::Adt(def, args) = ty.kind()
1262-
&& def.did().as_local().is_some()
1262+
&& let Some(local_did) = def.did().as_local()
12631263
&& def.variants().iter().all(|variant| {
12641264
variant
12651265
.fields
@@ -1269,7 +1269,40 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
12691269
{
12701270
let ty_span = self.infcx.tcx.def_span(def.did());
12711271
let mut span: MultiSpan = ty_span.into();
1272-
span.push_span_label(ty_span, "consider implementing `Clone` for this type");
1272+
let mut derive_clone = false;
1273+
self.infcx.tcx.for_each_relevant_impl(
1274+
self.infcx.tcx.lang_items().clone_trait().unwrap(),
1275+
ty,
1276+
|def_id| {
1277+
if self.infcx.tcx.is_automatically_derived(def_id) {
1278+
derive_clone = true;
1279+
span.push_span_label(
1280+
self.infcx.tcx.def_span(def_id),
1281+
"derived `Clone` adds implicit bounds on type parameters",
1282+
);
1283+
if let Some(generics) = self.infcx.tcx.hir_get_generics(local_did) {
1284+
for param in generics.params {
1285+
if let hir::GenericParamKind::Type { .. } = param.kind {
1286+
span.push_span_label(
1287+
param.span,
1288+
format!(
1289+
"introduces an implicit `{}: Clone` bound",
1290+
param.name.ident()
1291+
),
1292+
);
1293+
}
1294+
}
1295+
}
1296+
}
1297+
},
1298+
);
1299+
span.push_span_label(
1300+
ty_span,
1301+
format!(
1302+
"consider {}implementing `Clone` for this type",
1303+
if derive_clone { "manually " } else { "" }
1304+
),
1305+
);
12731306
span.push_span_label(expr.span, "you could clone this value");
12741307
err.span_note(
12751308
span,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Issue #108894
2+
3+
use std::marker::PhantomData;
4+
5+
#[derive(Clone, Copy)] //~ NOTE derived `Clone` adds implicit bounds on type parameters
6+
pub struct TypedAddress<T>{
7+
//~^ NOTE if `TypedAddress<T>` implemented `Clone`, you could clone the value
8+
//~| NOTE consider manually implementing `Clone` for this type
9+
//~| NOTE introduces an implicit `T: Clone` bound
10+
inner: u64,
11+
phantom: PhantomData<T>,
12+
}
13+
14+
pub trait Memory {
15+
fn write_value<T>(&self, offset: TypedAddress<T>, value: &T);
16+
fn return_value<T>(&self, offset: TypedAddress<T>) -> T;
17+
//~^ NOTE consider changing this parameter type in method `return_value` to borrow instead if owning the value isn't necessary
18+
//~| NOTE in this method
19+
//~| NOTE this parameter takes ownership of the value
20+
fn update_value<T, F>(&self, offset: TypedAddress<T>, update: F)
21+
//~^ NOTE move occurs because `offset` has type `TypedAddress<T>`, which does not implement the `Copy` trait
22+
where F: FnOnce(T) -> T
23+
{
24+
let old = self.return_value(offset); //~ NOTE value moved here
25+
//~^ NOTE you could clone this value
26+
let new = update(old);
27+
self.write_value(offset, &new); //~ ERROR use of moved value: `offset`
28+
//~^ NOTE value used here after move
29+
}
30+
}
31+
32+
fn main() {}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error[E0382]: use of moved value: `offset`
2+
--> $DIR/derive-clone-implicit-bound.rs:27:26
3+
|
4+
LL | fn update_value<T, F>(&self, offset: TypedAddress<T>, update: F)
5+
| ------ move occurs because `offset` has type `TypedAddress<T>`, which does not implement the `Copy` trait
6+
...
7+
LL | let old = self.return_value(offset);
8+
| ------ value moved here
9+
...
10+
LL | self.write_value(offset, &new);
11+
| ^^^^^^ value used here after move
12+
|
13+
note: consider changing this parameter type in method `return_value` to borrow instead if owning the value isn't necessary
14+
--> $DIR/derive-clone-implicit-bound.rs:16:39
15+
|
16+
LL | fn return_value<T>(&self, offset: TypedAddress<T>) -> T;
17+
| ------------ in this method ^^^^^^^^^^^^^^^ this parameter takes ownership of the value
18+
note: if `TypedAddress<T>` implemented `Clone`, you could clone the value
19+
--> $DIR/derive-clone-implicit-bound.rs:6:1
20+
|
21+
LL | #[derive(Clone, Copy)]
22+
| ----- derived `Clone` adds implicit bounds on type parameters
23+
LL | pub struct TypedAddress<T>{
24+
| ^^^^^^^^^^^^^^^^^^^^^^^^-^
25+
| | |
26+
| | introduces an implicit `T: Clone` bound
27+
| consider manually implementing `Clone` for this type
28+
...
29+
LL | let old = self.return_value(offset);
30+
| ------ you could clone this value
31+
help: consider further restricting type parameter `T` with trait `Copy`
32+
|
33+
LL | where F: FnOnce(T) -> T, T: Copy
34+
| +++++++++
35+
36+
error: aborting due to 1 previous error
37+
38+
For more information about this error, try `rustc --explain E0382`.

0 commit comments

Comments
 (0)