On E0308 caused by cloning a reference due to missing bounds, account for derive

On type errors where the difference is expecting an owned type and getting a reference, if the expression is a `.clone()` call and the type is annotated with `#[derive(Clone)]`, we now explain implicit bounds and suggest manually implementing `Clone`.

```
error[E0308]: mismatched types
  --> $DIR/derive-implicit-bound-on-clone.rs:10:5
   |
LL | fn clone_me<T, K>(x: &ContainsRc<T, K>) -> ContainsRc<T, K> {
   |                                            ---------------- expected `ContainsRc<T, K>` because of return type
LL |     x.clone()
   |     ^^^^^^^^^ expected `ContainsRc<T, K>`, found `&ContainsRc<T, K>`
   |
   = note: expected struct `ContainsRc<_, _>`
           found reference `&ContainsRc<_, _>`
note: `ContainsRc<T, K>` does not implement `Clone`, so `&ContainsRc<T, K>` was cloned instead
  --> $DIR/derive-implicit-bound-on-clone.rs:10:5
   |
LL |     x.clone()
   |     ^
help: `Clone` is not implemented because the some trait bounds could not be satisfied
  --> $DIR/derive-implicit-bound-on-clone.rs:5:19
   |
LL | #[derive(Clone)]
   |          ----- in this derive macro expansion
LL | struct ContainsRc<T, K> {
   |                   ^  ^ derive introduces an implicit unsatisfied trait bound `K: Clone`
   |                   |
   |                   derive introduces an implicit unsatisfied trait bound `T: Clone`
   = help: consider manually implementing `Clone` to avoid the implict type parameter bounds
```
This commit is contained in:
Esteban Küber 2026-01-18 20:23:18 +00:00
parent b88fb8a0b0
commit 085da0cee4
5 changed files with 166 additions and 14 deletions

View file

@ -1932,25 +1932,96 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None,
);
} else {
let mut suggest_derive = true;
if let Some(errors) =
self.type_implements_trait_shallow(clone_trait_did, expected_ty, self.param_env)
{
let manually_impl = "consider manually implementing `Clone` to avoid the \
implict type parameter bounds";
match &errors[..] {
[] => {}
[error] => {
diag.help(format!(
"`Clone` is not implemented because the trait bound `{}` is \
not satisfied",
error.obligation.predicate,
));
// diag.note("{error:#?}");
// diag.note(format!("{:#?} {:#?} {:#?}", error.obligation, error.obligation.cause, error.obligation.cause.code()));
let msg = "`Clone` is not implemented because a trait bound is not \
satisfied";
if let traits::ObligationCauseCode::ImplDerived(data) =
error.obligation.cause.code()
{
let mut span: MultiSpan = data.span.into();
if self.tcx.is_automatically_derived(data.impl_or_alias_def_id) {
span.push_span_label(
data.span,
format!(
"derive introduces an implicit `{}` bound",
error.obligation.predicate
),
);
}
diag.span_help(span, msg);
if self.tcx.is_automatically_derived(data.impl_or_alias_def_id)
&& data.impl_or_alias_def_id.is_local()
{
diag.help(manually_impl);
suggest_derive = false;
}
} else {
diag.help(msg);
}
}
_ => {
diag.help(format!(
"`Clone` is not implemented because the following trait bounds \
could not be satisfied: {}",
listify(&errors, |e| format!("`{}`", e.obligation.predicate))
.unwrap(),
));
let unsatisfied_bounds: Vec<_> = errors
.iter()
.filter_map(|error| match error.obligation.cause.code() {
traits::ObligationCauseCode::ImplDerived(data) => {
let pre = if self
.tcx
.is_automatically_derived(data.impl_or_alias_def_id)
{
"derive introduces an implicit "
} else {
""
};
Some((
data.span,
format!(
"{pre}unsatisfied trait bound `{}`",
error.obligation.predicate
),
))
}
_ => None,
})
.collect();
let msg = "`Clone` is not implemented because the some trait bounds \
could not be satisfied";
if errors.len() == unsatisfied_bounds.len() {
let mut unsatisfied_bounds_spans: MultiSpan = unsatisfied_bounds
.iter()
.map(|(span, _)| *span)
.collect::<Vec<Span>>()
.into();
for (span, label) in unsatisfied_bounds {
unsatisfied_bounds_spans.push_span_label(span, label);
}
diag.span_help(unsatisfied_bounds_spans, msg);
if errors.iter().all(|error| match error.obligation.cause.code() {
traits::ObligationCauseCode::ImplDerived(data) => {
self.tcx.is_automatically_derived(data.impl_or_alias_def_id)
&& data.impl_or_alias_def_id.is_local()
}
_ => false,
}) {
diag.help(manually_impl);
suggest_derive = false;
}
} else {
diag.help(format!(
"{msg}: {}",
listify(&errors, |e| format!("`{}`", e.obligation.predicate))
.unwrap(),
));
}
}
}
for error in errors {
@ -1968,7 +2039,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
self.suggest_derive(diag, &vec![(trait_ref.upcast(self.tcx), None, None)]);
if suggest_derive {
self.suggest_derive(diag, &vec![(trait_ref.upcast(self.tcx), None, None)]);
}
}
}
}

View file

@ -13,7 +13,8 @@ note: `HashSet<Day>` does not implement `Clone`, so `&HashSet<Day>` was cloned i
|
LL | let mut x: HashSet<Day> = v.clone();
| ^
= help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied
help: `Clone` is not implemented because a trait bound is not satisfied
--> $SRC_DIR/std/src/collections/hash/set.rs:LL:COL
help: consider annotating `Day` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]

View file

@ -13,7 +13,8 @@ note: `HashSet<Day>` does not implement `Clone`, so `&HashSet<Day>` was cloned i
|
LL | let mut x: HashSet<Day> = v.clone();
| ^
= help: `Clone` is not implemented because the trait bound `Day: Clone` is not satisfied
help: `Clone` is not implemented because a trait bound is not satisfied
--> $SRC_DIR/std/src/collections/hash/set.rs:LL:COL
help: consider annotating `Day` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]

View file

@ -0,0 +1,24 @@
// Issue #146515
use std::rc::Rc;
#[derive(Clone)]
struct ContainsRc<T, K> { //~ HELP `Clone` is not implemented
value: Rc<(T, K)>,
}
fn clone_me<T, K>(x: &ContainsRc<T, K>) -> ContainsRc<T, K> {
x.clone() //~ ERROR E0308
//~^ HELP consider manually implementing `Clone`
}
#[derive(Clone)]
struct ContainsRcSingle<T> { //~ HELP `Clone` is not implemented
value: Rc<T>,
}
fn clone_me_single<T>(x: &ContainsRcSingle<T>) -> ContainsRcSingle<T> {
x.clone() //~ ERROR E0308
//~^ HELP consider manually implementing `Clone`
}
fn main() {}

View file

@ -0,0 +1,53 @@
error[E0308]: mismatched types
--> $DIR/derive-implicit-bound-on-clone.rs:10:5
|
LL | fn clone_me<T, K>(x: &ContainsRc<T, K>) -> ContainsRc<T, K> {
| ---------------- expected `ContainsRc<T, K>` because of return type
LL | x.clone()
| ^^^^^^^^^ expected `ContainsRc<T, K>`, found `&ContainsRc<T, K>`
|
= note: expected struct `ContainsRc<_, _>`
found reference `&ContainsRc<_, _>`
note: `ContainsRc<T, K>` does not implement `Clone`, so `&ContainsRc<T, K>` was cloned instead
--> $DIR/derive-implicit-bound-on-clone.rs:10:5
|
LL | x.clone()
| ^
help: `Clone` is not implemented because the some trait bounds could not be satisfied
--> $DIR/derive-implicit-bound-on-clone.rs:5:19
|
LL | #[derive(Clone)]
| ----- in this derive macro expansion
LL | struct ContainsRc<T, K> {
| ^ ^ derive introduces an implicit unsatisfied trait bound `K: Clone`
| |
| derive introduces an implicit unsatisfied trait bound `T: Clone`
= help: consider manually implementing `Clone` to avoid the implict type parameter bounds
error[E0308]: mismatched types
--> $DIR/derive-implicit-bound-on-clone.rs:20:5
|
LL | fn clone_me_single<T>(x: &ContainsRcSingle<T>) -> ContainsRcSingle<T> {
| ------------------- expected `ContainsRcSingle<T>` because of return type
LL | x.clone()
| ^^^^^^^^^ expected `ContainsRcSingle<T>`, found `&ContainsRcSingle<T>`
|
= note: expected struct `ContainsRcSingle<_>`
found reference `&ContainsRcSingle<_>`
note: `ContainsRcSingle<T>` does not implement `Clone`, so `&ContainsRcSingle<T>` was cloned instead
--> $DIR/derive-implicit-bound-on-clone.rs:20:5
|
LL | x.clone()
| ^
help: `Clone` is not implemented because a trait bound is not satisfied
--> $DIR/derive-implicit-bound-on-clone.rs:15:25
|
LL | #[derive(Clone)]
| ----- in this derive macro expansion
LL | struct ContainsRcSingle<T> {
| ^ derive introduces an implicit `T: Clone` bound
= help: consider manually implementing `Clone` to avoid the implict type parameter bounds
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.