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 0a4691447b3d6..7921250a85a9e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4520,7 +4520,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } err.span_note(spans, msg); - if derived && trait_name != "Copy" { + if derived + && 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\"", )); @@ -6472,6 +6478,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::PolyTraitClause<'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-consts/assoc-const-equality-unexpected-region.rs b/tests/ui/associated-consts/assoc-const-equality-unexpected-region.rs new file mode 100644 index 0000000000000..e8a3dcf47eea3 --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-equality-unexpected-region.rs @@ -0,0 +1,14 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/143896. + +trait TraitA<'a> { + const K: usize = 0; +} + +impl TraitA<'_> for () {} +//~^ ERROR the type parameter `T` is not constrained + +impl dyn TraitA<'_> where (): TraitA<'a, K = 0> {} +//~^ ERROR use of undeclared lifetime name `'a` +//~| ERROR associated const equality is incomplete + +pub fn main() {} diff --git a/tests/ui/associated-consts/assoc-const-equality-unexpected-region.stderr b/tests/ui/associated-consts/assoc-const-equality-unexpected-region.stderr new file mode 100644 index 0000000000000..6e38e3e41acbf --- /dev/null +++ b/tests/ui/associated-consts/assoc-const-equality-unexpected-region.stderr @@ -0,0 +1,40 @@ +error[E0261]: use of undeclared lifetime name `'a` + --> $DIR/assoc-const-equality-unexpected-region.rs:10:38 + | +LL | impl dyn TraitA<'_> where (): TraitA<'a, K = 0> {} + | ^^ undeclared lifetime + | + = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | impl dyn TraitA<'_> where (): for<'a> TraitA<'a, K = 0> {} + | +++++++ +help: consider making the bound lifetime-generic with a new `'a` lifetime + | +LL | impl dyn TraitA<'_> where for<'a> (): TraitA<'a, K = 0> {} + | +++++++ +help: consider introducing lifetime `'a` here + | +LL | impl<'a> dyn TraitA<'_> where (): TraitA<'a, K = 0> {} + | ++++ + +error[E0658]: associated const equality is incomplete + --> $DIR/assoc-const-equality-unexpected-region.rs:10:42 + | +LL | impl dyn TraitA<'_> where (): TraitA<'a, K = 0> {} + | ^^^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0207]: the type parameter `T` is not constrained by the impl trait, self type, or predicates + --> $DIR/assoc-const-equality-unexpected-region.rs:7:6 + | +LL | impl TraitA<'_> for () {} + | ^ unconstrained type parameter + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0207, E0261, E0658. +For more information about an error, try `rustc --explain E0207`. 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-copy.rs b/tests/ui/derives/imperfect-derive-copy.rs new file mode 100644 index 0000000000000..cb68cfdb0507a --- /dev/null +++ b/tests/ui/derives/imperfect-derive-copy.rs @@ -0,0 +1,16 @@ +// Test that the "imperfect derives" note is emitted for Copy when the derive +// bound is genuinely unnecessary (e.g., `PhantomData`). + +use std::marker::PhantomData; + +#[derive(Copy, Clone)] +struct X(PhantomData); + +struct Y; // does not implement Copy + +fn require_copy(_t: T) {} + +fn main() { + require_copy(X::(PhantomData)); + //~^ ERROR the trait bound `X: Copy` is not satisfied +} diff --git a/tests/ui/derives/imperfect-derive-copy.stderr b/tests/ui/derives/imperfect-derive-copy.stderr new file mode 100644 index 0000000000000..125852de9b417 --- /dev/null +++ b/tests/ui/derives/imperfect-derive-copy.stderr @@ -0,0 +1,30 @@ +error[E0277]: the trait bound `X: Copy` is not satisfied + --> $DIR/imperfect-derive-copy.rs:14:25 + | +LL | require_copy(X::(PhantomData)); + | ------------ ^^^^^^^^^^^ the trait `Copy` is not implemented for `X` + | | + | required by a bound introduced by this call + | +note: required for `X` to implement `Copy` + --> $DIR/imperfect-derive-copy.rs:7:8 + | +LL | #[derive(Copy, Clone)] + | ---- in this derive macro expansion +LL | struct X(PhantomData); + | ^ - type parameter would need to implement `Copy` + = help: consider manually implementing `Copy` to avoid undesired bounds caused by "imperfect derives" + = note: to learn more, visit +note: required by a bound in `require_copy` + --> $DIR/imperfect-derive-copy.rs:11:20 + | +LL | fn require_copy(_t: T) {} + | ^^^^ required by this bound in `require_copy` +help: consider borrowing here + | +LL | require_copy(X::(&PhantomData)); + | + + +error: aborting due to 1 previous error + +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/lint/improper-ctypes/tait-extern-fn-next-solver-issue-156345.rs b/tests/ui/lint/improper-ctypes/tait-extern-fn-next-solver-issue-156345.rs new file mode 100644 index 0000000000000..fd8ba56b10be6 --- /dev/null +++ b/tests/ui/lint/improper-ctypes/tait-extern-fn-next-solver-issue-156345.rs @@ -0,0 +1,42 @@ +//@ compile-flags: -Znext-solver=globally +//@ edition: 2021 +//@ check-pass + +// Regression test for . +// An `extern "C" fn` taking a type alias impl trait argument used to ICE with +// the new solver, leaving an entry in the `OpaqueTypeStorage`. Only the new +// solver was affected. + +#![feature(type_alias_impl_trait)] +#![allow(improper_ctypes_definitions)] + +struct Foo { + field: String, +} + +type Tait = impl Sized; + +#[define_opaque(Tait)] +extern "C" fn ice_cold(beverage: Tait) { + let Foo { field } = beverage; + let _ = field; +} + +// A second reproducer from the same issue, with the opaque type in return +// position behind a higher-ranked closure bound. +struct Parser(H); + +impl Parser +where + H: for<'a> Fn(&'a str) -> T, +{ + fn new(handler: H) -> Parser { + Parser(handler) + } + + extern "C" fn many<'s>() -> Parser Fn(&'a str) + 's> { + Parser::new(|_| ()) + } +} + +fn main() {} 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 {