Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4374,7 +4374,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
err.span_note(spans, msg);
if derived && trait_name != "Copy" {
if derived
&& trait_name != "Copy"
&& self.is_truly_imperfect_derive(
parent_trait_pred,
predicate,
param_env,
)
{
err.help(format!(
"consider manually implementing `{trait_name}` to avoid undesired bounds caused by \"imperfect derives\"",
));
Expand Down Expand Up @@ -6338,6 +6345,90 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
_ => {}
}
}

/// Checks whether the field independently satisfies the trait bound, ignoring
/// the specific generic parameter that caused the original E0277 error.
fn is_truly_imperfect_derive(
&self,
parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
predicate: ty::Predicate<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> bool {
let tcx = self.tcx;
let ty::Adt(adt_def, parent_args) = parent_trait_pred.skip_binder().self_ty().kind() else {
return false;
};
let Some(trait_clause) = predicate.as_trait_clause() else {
return false;
};
let failing_ty = trait_clause.skip_binder().self_ty();
let trait_def_id = parent_trait_pred.def_id();

let failing_adt_param_indices: FxHashSet<u32> = parent_args
.iter()
.enumerate()
.filter_map(|(idx, arg)| {
if let Some(t) = arg.as_type()
&& t == failing_ty
{
Some(idx as u32)
} else {
None
}
})
.collect();

if failing_adt_param_indices.is_empty() {
return false;
}

adt_def.all_fields().all(|field| {
let raw_field_ty = tcx.type_of(field.did).skip_binder();
// If the field type is exactly one of the failing parameters, it is not an imperfect derive.
if let ty::Param(p) = raw_field_ty.kind()
&& failing_adt_param_indices.contains(&p.index)
{
return false;
}
// If the field type doesn't mention the parameter at all, it's independent.
if !raw_field_ty.walk().any(|arg| {
matches!(arg.as_type().map(|t| t.kind()), Some(ty::Param(p)) if failing_adt_param_indices.contains(&p.index))
}) {
return true;
}

self.probe(|_| {
// We keep the generic parameters that didn't fail as-is from the parent args,
// but replace the ones that did fail with fresh inference variable placeholders.
let fresh_args = ty::GenericArgs::for_item(tcx, adt_def.did(), |param, _| {
if failing_adt_param_indices.contains(&param.index) {
self.var_for_def(DUMMY_SP, param)
} else {
parent_args[param.index as usize]
}
});
Comment thread
raushan728 marked this conversation as resolved.

let field_ty = field.ty(tcx, fresh_args).skip_norm_wip();
// Substitute the field's type for the bound's `Self` type to check whether
// the field alone would satisfy the trait, independent of other generics.
let parent_trait_args = parent_trait_pred.skip_binder().trait_ref.args;
let trait_args = parent_trait_args.iter().map(|arg| {
if arg.as_type() == Some(parent_trait_pred.skip_binder().self_ty()) {
field_ty.into()
} else {
arg
}
});
let obligation = Obligation::new(
tcx,
ObligationCause::dummy(),
param_env,
ty::TraitRef::new(tcx, trait_def_id, trait_args),
);
self.predicate_may_hold(&obligation)
})
})
}
}

/// Add a hint to add a missing borrow or remove an unnecessary one.
Expand Down
4 changes: 0 additions & 4 deletions tests/ui/associated-types/issue-38821.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,6 @@ LL | pub enum ColumnInsertValue<Col, Expr> where
...
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>,
| ------------------------------------------------ unsatisfied trait bound
= help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
help: consider further restricting the associated type
|
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
Expand Down Expand Up @@ -239,8 +237,6 @@ LL | pub enum ColumnInsertValue<Col, Expr> where
...
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>,
| ------------------------------------------------ unsatisfied trait bound
= help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
help: consider further restricting the associated type
|
LL | Expr: Expression<SqlType=<Col::SqlType as IntoNullable>::Nullable>, <Col as Expression>::SqlType: NotNull,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)]
| ----- in this derive macro expansion
LL | struct Bar<T>(T);
| ^^^ - unsatisfied trait bound
= help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
= note: 2 redundant requirements hidden
= note: required for `&&'static Bar<(dyn Debug + 'static)>` to implement `Debug`
= note: required for the cast from `&&&'static Bar<(dyn Debug + 'static)>` to `&dyn Debug`
Expand Down Expand Up @@ -103,8 +101,6 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)]
| -- in this derive macro expansion
LL | struct Bar<T>(T);
| ^^^ - type parameter would need to implement `Eq`
= help: consider manually implementing `Eq` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
= note: 1 redundant requirement hidden
= note: required for `&'static Bar<dyn Debug>` to implement `Eq`
note: required by a bound in `std::cmp::AssertParamIsEq`
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/derives/clone-copy/deriving-copyclone.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ LL | #[derive(Copy, Clone)]
| ----- in this derive macro expansion
LL | struct B<T> {
| ^ - type parameter would need to implement `Clone`
= help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
note: required by a bound in `is_clone`
--> $DIR/deriving-copyclone.rs:19:16
|
Expand Down
42 changes: 42 additions & 0 deletions tests/ui/derives/imperfect-derive-advanced.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Test that the "imperfect derives" note is emitted for associated types,
// `Rc<T>`, and coinductive types.

use std::rc::Rc;

trait Trait {
type Assoc: Clone;
}

#[derive(Clone)]
struct AssocStruct<T: Trait> {
field: T::Assoc,
}

struct NonClone;
impl Trait for NonClone {
type Assoc = u32;
}

#[derive(Clone)]
struct RcStruct<T> {
field: Rc<T>,
}

#[derive(Clone)]
struct List<T> {
value: Rc<T>,
next: Option<Box<List<T>>>,
}

fn require_clone<T: Clone>() {}

fn main() {
require_clone::<AssocStruct<NonClone>>();
//~^ ERROR the trait bound `NonClone: Clone` is not satisfied

require_clone::<RcStruct<NonClone>>();
//~^ ERROR the trait bound `NonClone: Clone` is not satisfied

require_clone::<List<NonClone>>();
//~^ ERROR the trait bound `NonClone: Clone` is not satisfied
}
81 changes: 81 additions & 0 deletions tests/ui/derives/imperfect-derive-advanced.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
error[E0277]: the trait bound `NonClone: Clone` is not satisfied
--> $DIR/imperfect-derive-advanced.rs:34:21
|
LL | require_clone::<AssocStruct<NonClone>>();
| ^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClone`
|
note: required for `AssocStruct<NonClone>` to implement `Clone`
--> $DIR/imperfect-derive-advanced.rs:11:8
|
LL | #[derive(Clone)]
| ----- in this derive macro expansion
LL | struct AssocStruct<T: Trait> {
| ^^^^^^^^^^^ - type parameter would need to implement `Clone`
= help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
note: required by a bound in `require_clone`
--> $DIR/imperfect-derive-advanced.rs:31:21
|
LL | fn require_clone<T: Clone>() {}
| ^^^^^ required by this bound in `require_clone`
help: consider annotating `NonClone` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | struct NonClone;
|

error[E0277]: the trait bound `NonClone: Clone` is not satisfied
--> $DIR/imperfect-derive-advanced.rs:37:21
|
LL | require_clone::<RcStruct<NonClone>>();
| ^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClone`
|
note: required for `RcStruct<NonClone>` to implement `Clone`
--> $DIR/imperfect-derive-advanced.rs:21:8
|
LL | #[derive(Clone)]
| ----- in this derive macro expansion
LL | struct RcStruct<T> {
| ^^^^^^^^ - type parameter would need to implement `Clone`
= help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
note: required by a bound in `require_clone`
--> $DIR/imperfect-derive-advanced.rs:31:21
|
LL | fn require_clone<T: Clone>() {}
| ^^^^^ required by this bound in `require_clone`
help: consider annotating `NonClone` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | struct NonClone;
|

error[E0277]: the trait bound `NonClone: Clone` is not satisfied
--> $DIR/imperfect-derive-advanced.rs:40:21
|
LL | require_clone::<List<NonClone>>();
| ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClone`
|
note: required for `List<NonClone>` to implement `Clone`
--> $DIR/imperfect-derive-advanced.rs:26:8
|
LL | #[derive(Clone)]
| ----- in this derive macro expansion
LL | struct List<T> {
| ^^^^ - type parameter would need to implement `Clone`
= help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
note: required by a bound in `require_clone`
--> $DIR/imperfect-derive-advanced.rs:31:21
|
LL | fn require_clone<T: Clone>() {}
| ^^^^^ required by this bound in `require_clone`
help: consider annotating `NonClone` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | struct NonClone;
|

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
16 changes: 16 additions & 0 deletions tests/ui/derives/imperfect-derive-phantom.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Test that the "imperfect derives" note is emitted when the derive bound
// is genuinely unnecessary (e.g., `PhantomData<T>`).

use std::marker::PhantomData;

#[derive(Clone)]
struct S<T>(PhantomData<T>);

struct X;

fn require_clone<T: Clone>(_t: T) {}

fn main() {
require_clone(S::<X>(PhantomData));
//~^ ERROR the trait bound `S<X>: Clone` is not satisfied
}
30 changes: 30 additions & 0 deletions tests/ui/derives/imperfect-derive-phantom.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
error[E0277]: the trait bound `S<X>: Clone` is not satisfied
--> $DIR/imperfect-derive-phantom.rs:14:26
|
LL | require_clone(S::<X>(PhantomData));
| ------------- ^^^^^^^^^^^ the trait `Clone` is not implemented for `S<X>`
| |
| required by a bound introduced by this call
|
note: required for `S<X>` to implement `Clone`
--> $DIR/imperfect-derive-phantom.rs:7:8
|
LL | #[derive(Clone)]
| ----- in this derive macro expansion
LL | struct S<T>(PhantomData<T>);
| ^ - type parameter would need to implement `Clone`
= help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
note: required by a bound in `require_clone`
--> $DIR/imperfect-derive-phantom.rs:11:21
|
LL | fn require_clone<T: Clone>(_t: T) {}
| ^^^^^ required by this bound in `require_clone`
help: consider borrowing here
|
LL | require_clone(S::<X>(&PhantomData));
| +

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ LL | #[derive(Debug)]
| ----- in this derive macro expansion
LL | struct S<T>(T);
| ^ - type parameter would need to implement `Debug`
= help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
help: consider annotating `X` with `#[derive(Debug)]`
|
LL + #[derive(Debug)]
Expand Down
2 changes: 0 additions & 2 deletions tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ LL | #[derive(PartialOrd, AddImpl)]
...
LL | struct PriorityQueue<T>(BinaryHeap<PriorityQueueEntry<T>>);
| ^^^^^^^^^^^^^ - type parameter would need to implement `PartialOrd`
= help: consider manually implementing `PartialOrd` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
note: required by a bound in `Ord`
--> $SRC_DIR/core/src/cmp.rs:LL:COL

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ LL | #[derive(Debug, Copy, Clone)]
| ----- in this derive macro expansion
LL | pub struct Vector2<T: Debug + Copy + Clone> {
| ^^^^^^^ ---- unsatisfied trait bound
= help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
= note: required for the cast from `&Vector2<K>` to `&dyn Debug`
help: consider further restricting type parameter `K` with trait `Copy`
|
Expand Down Expand Up @@ -73,8 +71,6 @@ LL | #[derive(Debug, Copy, Clone)]
| ----- in this derive macro expansion
LL | pub struct Vector2<T: Debug + Copy + Clone> {
| ^^^^^^^ ---- unsatisfied trait bound
= help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
help: consider further restricting type parameter `K` with trait `Copy`
|
LL | pub struct AABB<K: Debug + std::marker::Copy> {
Expand Down
4 changes: 0 additions & 4 deletions tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,6 @@ LL | #[derive(Debug, Copy, Clone)]
| ----- in this derive macro expansion
LL | pub struct Vector2<T: Debug + Copy + Clone> {
| ^^^^^^^ ---- unsatisfied trait bound
= help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
= note: required for the cast from `&Vector2<K>` to `&dyn Debug`
help: consider restricting type parameter `K` with trait `Copy`
|
Expand Down Expand Up @@ -137,8 +135,6 @@ LL | #[derive(Debug, Copy, Clone)]
| ----- in this derive macro expansion
LL | pub struct Vector2<T: Debug + Copy + Clone> {
| ^^^^^^^ ---- unsatisfied trait bound
= help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives"
= note: to learn more, visit <https://github.com/rust-lang/rust/issues/26925>
help: consider restricting type parameter `K` with trait `Copy`
|
LL | pub struct AABB<K: std::marker::Copy> {
Expand Down
Loading