Skip to content
Closed
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 @@ -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\"",
));
Expand Down Expand Up @@ -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<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]
}
});

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
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! Regression test for https://github.com/rust-lang/rust/issues/143896.

trait TraitA<'a> {
const K: usize = 0;
}

impl<T> 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() {}
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/rust-lang/rust/issues/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<T> 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`.
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-copy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Test that the "imperfect derives" note is emitted for Copy when the derive
// bound is genuinely unnecessary (e.g., `PhantomData<T>`).

use std::marker::PhantomData;

#[derive(Copy, Clone)]
struct X<T>(PhantomData<T>);

struct Y; // does not implement Copy

fn require_copy<T: Copy>(_t: T) {}

fn main() {
require_copy(X::<Y>(PhantomData));
//~^ ERROR the trait bound `X<Y>: Copy` is not satisfied
}
30 changes: 30 additions & 0 deletions tests/ui/derives/imperfect-derive-copy.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
error[E0277]: the trait bound `X<Y>: Copy` is not satisfied
--> $DIR/imperfect-derive-copy.rs:14:25
|
LL | require_copy(X::<Y>(PhantomData));
| ------------ ^^^^^^^^^^^ the trait `Copy` is not implemented for `X<Y>`
| |
| required by a bound introduced by this call
|
note: required for `X<Y>` to implement `Copy`
--> $DIR/imperfect-derive-copy.rs:7:8
|
LL | #[derive(Copy, Clone)]
| ---- in this derive macro expansion
LL | struct X<T>(PhantomData<T>);
| ^ - 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 <https://github.com/rust-lang/rust/issues/26925>
note: required by a bound in `require_copy`
--> $DIR/imperfect-derive-copy.rs:11:20
|
LL | fn require_copy<T: Copy>(_t: T) {}
| ^^^^ required by this bound in `require_copy`
help: consider borrowing here
|
LL | require_copy(X::<Y>(&PhantomData));
| +

error: aborting due to 1 previous error

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
}
Loading
Loading