diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index d2e8f136d7491..1fdc924abc238 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -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\"", )); @@ -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 = 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(¶m.index) { + self.var_for_def(DUMMY_SP, param) + } else { + parent_args[param.index as usize] + } + }); + + 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. diff --git a/tests/ui/associated-types/issue-38821.stderr b/tests/ui/associated-types/issue-38821.stderr index d7fcec380a3de..050054ad704bc 100644 --- a/tests/ui/associated-types/issue-38821.stderr +++ b/tests/ui/associated-types/issue-38821.stderr @@ -108,8 +108,6 @@ LL | pub enum ColumnInsertValue where ... LL | Expr: Expression::Nullable>, | ------------------------------------------------ unsatisfied trait bound - = help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit help: consider further restricting the associated type | LL | Expr: Expression::Nullable>, ::SqlType: NotNull, @@ -239,8 +237,6 @@ LL | pub enum ColumnInsertValue where ... LL | Expr: Expression::Nullable>, | ------------------------------------------------ unsatisfied trait bound - = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit help: consider further restricting the associated type | LL | Expr: Expression::Nullable>, ::SqlType: NotNull, diff --git a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr index 029c41b46ff40..ffbd0acd0f1e9 100644 --- a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr +++ b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr @@ -67,8 +67,6 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | ----- in this derive macro expansion LL | struct Bar(T); | ^^^ - unsatisfied trait bound - = help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit = 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` @@ -103,8 +101,6 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | -- in this derive macro expansion LL | struct Bar(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 = note: 1 redundant requirement hidden = note: required for `&'static Bar` to implement `Eq` note: required by a bound in `std::cmp::AssertParamIsEq` diff --git a/tests/ui/derives/clone-copy/deriving-copyclone.stderr b/tests/ui/derives/clone-copy/deriving-copyclone.stderr index 356678ada17bb..285ff40f4a877 100644 --- a/tests/ui/derives/clone-copy/deriving-copyclone.stderr +++ b/tests/ui/derives/clone-copy/deriving-copyclone.stderr @@ -38,8 +38,6 @@ LL | #[derive(Copy, Clone)] | ----- in this derive macro expansion LL | struct B { | ^ - 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 note: required by a bound in `is_clone` --> $DIR/deriving-copyclone.rs:19:16 | diff --git a/tests/ui/derives/imperfect-derive-advanced.rs b/tests/ui/derives/imperfect-derive-advanced.rs new file mode 100644 index 0000000000000..52dcb03b71caa --- /dev/null +++ b/tests/ui/derives/imperfect-derive-advanced.rs @@ -0,0 +1,42 @@ +// Test that the "imperfect derives" note is emitted for associated types, +// `Rc`, and coinductive types. + +use std::rc::Rc; + +trait Trait { + type Assoc: Clone; +} + +#[derive(Clone)] +struct AssocStruct { + field: T::Assoc, +} + +struct NonClone; +impl Trait for NonClone { + type Assoc = u32; +} + +#[derive(Clone)] +struct RcStruct { + field: Rc, +} + +#[derive(Clone)] +struct List { + value: Rc, + next: Option>>, +} + +fn require_clone() {} + +fn main() { + require_clone::>(); + //~^ ERROR the trait bound `NonClone: Clone` is not satisfied + + require_clone::>(); + //~^ ERROR the trait bound `NonClone: Clone` is not satisfied + + require_clone::>(); + //~^ ERROR the trait bound `NonClone: Clone` is not satisfied +} diff --git a/tests/ui/derives/imperfect-derive-advanced.stderr b/tests/ui/derives/imperfect-derive-advanced.stderr new file mode 100644 index 0000000000000..d97d96e6dd4f8 --- /dev/null +++ b/tests/ui/derives/imperfect-derive-advanced.stderr @@ -0,0 +1,81 @@ +error[E0277]: the trait bound `NonClone: Clone` is not satisfied + --> $DIR/imperfect-derive-advanced.rs:34:21 + | +LL | require_clone::>(); + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClone` + | +note: required for `AssocStruct` to implement `Clone` + --> $DIR/imperfect-derive-advanced.rs:11:8 + | +LL | #[derive(Clone)] + | ----- in this derive macro expansion +LL | struct AssocStruct { + | ^^^^^^^^^^^ - 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 +note: required by a bound in `require_clone` + --> $DIR/imperfect-derive-advanced.rs:31:21 + | +LL | fn require_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::>(); + | ^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClone` + | +note: required for `RcStruct` to implement `Clone` + --> $DIR/imperfect-derive-advanced.rs:21:8 + | +LL | #[derive(Clone)] + | ----- in this derive macro expansion +LL | struct RcStruct { + | ^^^^^^^^ - 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 +note: required by a bound in `require_clone` + --> $DIR/imperfect-derive-advanced.rs:31:21 + | +LL | fn require_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::>(); + | ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClone` + | +note: required for `List` to implement `Clone` + --> $DIR/imperfect-derive-advanced.rs:26:8 + | +LL | #[derive(Clone)] + | ----- in this derive macro expansion +LL | struct List { + | ^^^^ - 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 +note: required by a bound in `require_clone` + --> $DIR/imperfect-derive-advanced.rs:31:21 + | +LL | fn require_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`. diff --git a/tests/ui/derives/imperfect-derive-phantom.rs b/tests/ui/derives/imperfect-derive-phantom.rs new file mode 100644 index 0000000000000..23cc05626fb08 --- /dev/null +++ b/tests/ui/derives/imperfect-derive-phantom.rs @@ -0,0 +1,16 @@ +// Test that the "imperfect derives" note is emitted when the derive bound +// is genuinely unnecessary (e.g., `PhantomData`). + +use std::marker::PhantomData; + +#[derive(Clone)] +struct S(PhantomData); + +struct X; + +fn require_clone(_t: T) {} + +fn main() { + require_clone(S::(PhantomData)); + //~^ ERROR the trait bound `S: Clone` is not satisfied +} diff --git a/tests/ui/derives/imperfect-derive-phantom.stderr b/tests/ui/derives/imperfect-derive-phantom.stderr new file mode 100644 index 0000000000000..ff0d7e09f286f --- /dev/null +++ b/tests/ui/derives/imperfect-derive-phantom.stderr @@ -0,0 +1,30 @@ +error[E0277]: the trait bound `S: Clone` is not satisfied + --> $DIR/imperfect-derive-phantom.rs:14:26 + | +LL | require_clone(S::(PhantomData)); + | ------------- ^^^^^^^^^^^ the trait `Clone` is not implemented for `S` + | | + | required by a bound introduced by this call + | +note: required for `S` to implement `Clone` + --> $DIR/imperfect-derive-phantom.rs:7:8 + | +LL | #[derive(Clone)] + | ----- in this derive macro expansion +LL | struct S(PhantomData); + | ^ - 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 +note: required by a bound in `require_clone` + --> $DIR/imperfect-derive-phantom.rs:11:21 + | +LL | fn require_clone(_t: T) {} + | ^^^^^ required by this bound in `require_clone` +help: consider borrowing here + | +LL | require_clone(S::(&PhantomData)); + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr b/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr index ef3c94b4f07f7..69e6e53a19d28 100644 --- a/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr +++ b/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr @@ -21,8 +21,6 @@ LL | #[derive(Debug)] | ----- in this derive macro expansion LL | struct S(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 help: consider annotating `X` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr b/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr index 179f9fc51a4ba..0390295a66a42 100644 --- a/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr +++ b/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr @@ -44,8 +44,6 @@ LL | #[derive(PartialOrd, AddImpl)] ... LL | struct PriorityQueue(BinaryHeap>); | ^^^^^^^^^^^^^ - 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 note: required by a bound in `Ord` --> $SRC_DIR/core/src/cmp.rs:LL:COL diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr index 779339232e692..ad359b538cef9 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr @@ -30,8 +30,6 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct Vector2 { | ^^^^^^^ ---- unsatisfied trait bound - = help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit = note: required for the cast from `&Vector2` to `&dyn Debug` help: consider further restricting type parameter `K` with trait `Copy` | @@ -73,8 +71,6 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct Vector2 { | ^^^^^^^ ---- unsatisfied trait bound - = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit help: consider further restricting type parameter `K` with trait `Copy` | LL | pub struct AABB { diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr index 225ab3cd5838d..92e4188e2039f 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr @@ -66,8 +66,6 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct Vector2 { | ^^^^^^^ ---- unsatisfied trait bound - = help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit = note: required for the cast from `&Vector2` to `&dyn Debug` help: consider restricting type parameter `K` with trait `Copy` | @@ -137,8 +135,6 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct Vector2 { | ^^^^^^^ ---- unsatisfied trait bound - = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit help: consider restricting type parameter `K` with trait `Copy` | LL | pub struct AABB {