Skip to content

Suggest derive(Trait) or T: Trait from transitive obligation in some cases #127997

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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
106 changes: 105 additions & 1 deletion compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1363,7 +1363,111 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"the following trait bounds were not satisfied:\n{bound_list}"
));
}
suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates);

let mut suggest_derive = true;
for (pred, _, cause) in unsatisfied_predicates {
let Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred))) =
pred.kind().no_bound_vars()
else {
continue;
};
let lang = tcx.lang_items();
if ![
lang.copy_trait(),
lang.clone_trait(),
tcx.get_diagnostic_item(sym::Debug),
tcx.get_diagnostic_item(sym::PartialEq),
tcx.get_diagnostic_item(sym::Default),
]
.contains(&Some(trait_pred.def_id()))
{
// We restrict ourselves only to built-in `derive`s.
continue;
}
let (adt, params) = match trait_pred.self_ty().kind() {
ty::Adt(adt, params) if adt.did().is_local() => (*adt, params),
_ => continue,
};
if tcx
.all_impls(trait_pred.def_id())
.filter_map(|imp_did| tcx.impl_trait_header(imp_did).map(|h| (imp_did, h)))
.filter(|(did, header)| {
let imp = header.trait_ref.instantiate_identity();
let impl_adt = match imp.self_ty().ty_adt_def() {
Some(impl_adt) if adt.did().is_local() => impl_adt,
_ => return false,
};
header.polarity == ty::ImplPolarity::Positive
&& impl_adt == adt
&& tcx.is_automatically_derived(*did)
})
.count()
== 1
{
// We now know that for this predicate, there *was* a `derive(Trait)` for
// the trait at hand, so we don't want to suggest writing that again.
for param in &params[..] {
// Look at the type parameters for the currently obligated type to see
// if a restriciton of `TypeParam: Trait` would help. If the
// instantiated type param is not a type param but instead an actual
// type, see if we can suggest `derive(Trait)` on *that* type.
// See `tests/ui/suggestions/f1000.rs`
let Some(ty) = param.as_type() else {
continue;
};
match ty.kind() {
ty::Adt(adt, _) if adt.did().is_local() => {
// The type param at hand is a local type, try to suggest
// `derive(Trait)`.
let trait_ref =
ty::TraitRef::identity(tcx, trait_pred.trait_ref.def_id)
.with_self_ty(tcx, ty);
Comment on lines +1423 to +1424
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't right :( PartialEq has two type parameters -- Self and Rhs. This only substitutes the first one.

let trait_pred = ty::Binder::dummy(ty::TraitPredicate {
trait_ref,
polarity: ty::PredicatePolarity::Positive,
});
suggested_derive = self.suggest_derive(
&mut err,
&[(
<_ as ty::UpcastFrom<_, _>>::upcast_from(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use UpcastFrom::upcast_from, just call upcast.

trait_pred, self.tcx,
),
None,
cause.clone(),
)],
);
}
ty::Param(_) => {
// It was a type param. See if it corresponds to the current
// `fn` and suggest `T: Trait`.
if let Some(obligation) = cause {
let trait_ref = ty::TraitRef::new(
tcx,
trait_pred.trait_ref.def_id,
[ty],
);
let trait_pred = ty::Binder::dummy(ty::TraitPredicate {
trait_ref,
polarity: ty::PredicatePolarity::Positive,
});
suggested_derive =
self.err_ctxt().suggest_restricting_param_bound(
&mut err,
trait_pred,
None,
obligation.body_id,
);
}
}
_ => {}
}
}
suggest_derive = false
}
}
if suggest_derive {
suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates);
}

unsatisfied_bounds = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
associated_ty: Option<(&'static str, Ty<'tcx>)>,
mut body_id: LocalDefId,
) {
) -> bool {
if trait_pred.skip_binder().polarity != ty::PredicatePolarity::Positive {
return;
return false;
}

let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);
Expand Down Expand Up @@ -279,7 +279,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_pred,
Some((ident, bounds)),
);
return;
return true;
}

hir::Node::TraitItem(hir::TraitItem {
Expand All @@ -293,7 +293,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred,
None,
);
return;
return true;
}

hir::Node::TraitItem(hir::TraitItem {
Expand Down Expand Up @@ -321,7 +321,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_pred,
None,
);
return;
return true;
}
hir::Node::Item(hir::Item {
kind:
Expand All @@ -341,7 +341,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_pred,
None,
);
return;
return true;
}

hir::Node::Item(hir::Item {
Expand Down Expand Up @@ -374,7 +374,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
.iter()
.all(|g| g.is_suggestable(self.tcx, false))
{
return;
return false;
}
// Missing generic type parameter bound.
let param_name = self_ty.to_string();
Expand Down Expand Up @@ -406,7 +406,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
Some(trait_pred.def_id()),
None,
) {
return;
return true;
}
}

Expand All @@ -432,10 +432,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
trait_pred,
associated_ty,
) {
return;
return true;
}
}
hir::Node::Crate(..) => return,
hir::Node::Crate(..) => return false,

_ => {}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//@ run-rustfix
#![allow(warnings)]
use std::collections::HashMap;

#[derive(Clone)]
struct Ctx<A> {
a_map: HashMap<String, B<A>>,
}

#[derive(Clone, PartialEq, Eq)]
struct B<A> {
a: A,
}

fn foo<Z: std::clone::Clone>(ctx: &mut Ctx<Z>) {
let a_map = ctx.a_map.clone(); //~ ERROR E0599
}

#[derive(Clone)]
#[derive(PartialEq)]
#[derive(Eq)]
struct S;
fn bar(ctx: &mut Ctx<S>) {
let a_map = ctx.a_map.clone(); //~ ERROR E0599
}

fn qux<Z: std::cmp::Eq>(ctx: &mut Ctx<Z>) where Z: PartialEq {
ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599
<_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]); //~ ERROR E0277
}

fn qut(ctx: &mut Ctx<S>) {
ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599
<_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]); //~ ERROR E0277
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//@ run-rustfix
#![allow(warnings)]
use std::collections::HashMap;

#[derive(Clone)]
struct Ctx<A> {
a_map: HashMap<String, B<A>>,
}

#[derive(Clone, PartialEq, Eq)]
struct B<A> {
a: A,
}

fn foo<Z>(ctx: &mut Ctx<Z>) {
let a_map = ctx.a_map.clone(); //~ ERROR E0599
}

struct S;
fn bar(ctx: &mut Ctx<S>) {
let a_map = ctx.a_map.clone(); //~ ERROR E0599
}

fn qux<Z>(ctx: &mut Ctx<Z>) {
ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599
<_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]); //~ ERROR E0277
}

fn qut(ctx: &mut Ctx<S>) {
ctx.a_map["a"].eq(&ctx.a_map["a"]); //~ ERROR E0599
<_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]); //~ ERROR E0277
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
error[E0599]: the method `clone` exists for struct `HashMap<String, B<Z>>`, but its trait bounds were not satisfied
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:16:27
|
LL | struct B<A> {
| ----------- doesn't satisfy `B<Z>: Clone`
...
LL | let a_map = ctx.a_map.clone();
| ^^^^^ method cannot be called on `HashMap<String, B<Z>>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`B<Z>: Clone`
which is required by `HashMap<String, B<Z>>: Clone`
help: consider restricting type parameter `Z`
|
LL | fn foo<Z: std::clone::Clone>(ctx: &mut Ctx<Z>) {
| +++++++++++++++++++

error[E0599]: the method `clone` exists for struct `HashMap<String, B<S>>`, but its trait bounds were not satisfied
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:21:27
|
LL | struct B<A> {
| ----------- doesn't satisfy `B<S>: Clone`
...
LL | let a_map = ctx.a_map.clone();
| ^^^^^ method cannot be called on `HashMap<String, B<S>>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`B<S>: Clone`
which is required by `HashMap<String, B<S>>: Clone`
help: consider annotating `S` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | struct S;
|

error[E0599]: the method `eq` exists for struct `B<Z>`, but its trait bounds were not satisfied
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:25:20
|
LL | struct B<A> {
| ----------- method `eq` not found for this struct because it doesn't satisfy `B<Z>: Iterator` or `B<Z>: PartialEq`
...
LL | ctx.a_map["a"].eq(&ctx.a_map["a"]);
| ^^ method cannot be called on `B<Z>` due to unsatisfied trait bounds
|
note: trait bound `Z: PartialEq` was not satisfied
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:10:17
|
LL | #[derive(Clone, PartialEq, Eq)]
| ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
= note: the following trait bounds were not satisfied:
`B<Z>: Iterator`
which is required by `&mut B<Z>: Iterator`
note: the trait `Iterator` must be implemented
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
help: consider restricting the type parameter to satisfy the trait bound
|
LL | fn qux<Z>(ctx: &mut Ctx<Z>) where Z: PartialEq {
| ++++++++++++++++++

error[E0277]: the trait bound `Z: Eq` is not satisfied
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:26:6
|
LL | <_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]);
| ^ the trait `Eq` is not implemented for `Z`, which is required by `B<Z>: Eq`
|
note: required for `B<Z>` to implement `Eq`
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:10:28
|
LL | #[derive(Clone, PartialEq, Eq)]
| ^^ unsatisfied trait bound introduced in this `derive` macro
= note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider restricting type parameter `Z`
|
LL | fn qux<Z: std::cmp::Eq>(ctx: &mut Ctx<Z>) {
| ++++++++++++++

error[E0599]: the method `eq` exists for struct `B<S>`, but its trait bounds were not satisfied
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:30:20
|
LL | struct B<A> {
| ----------- method `eq` not found for this struct because it doesn't satisfy `B<S>: Iterator` or `B<S>: PartialEq`
...
LL | struct S;
| -------- doesn't satisfy `S: PartialEq`
...
LL | ctx.a_map["a"].eq(&ctx.a_map["a"]);
| ^^ method cannot be called on `B<S>` due to unsatisfied trait bounds
|
note: trait bound `S: PartialEq` was not satisfied
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:10:17
|
LL | #[derive(Clone, PartialEq, Eq)]
| ^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
= note: the following trait bounds were not satisfied:
`B<S>: Iterator`
which is required by `&mut B<S>: Iterator`
note: the trait `Iterator` must be implemented
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
help: consider annotating `S` with `#[derive(PartialEq)]`
|
LL + #[derive(PartialEq)]
LL | struct S;
|

error[E0277]: the trait bound `S: Eq` is not satisfied
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:31:6
|
LL | <_ as Eq>::assert_receiver_is_total_eq(&ctx.a_map["a"]);
| ^ the trait `Eq` is not implemented for `S`, which is required by `B<S>: Eq`
|
= help: the trait `Eq` is implemented for `B<A>`
note: required for `B<S>` to implement `Eq`
--> $DIR/type-or-type-param-missing-transitive-trait-contraint.rs:10:28
|
LL | #[derive(Clone, PartialEq, Eq)]
| ^^ unsatisfied trait bound introduced in this `derive` macro
= note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `S` with `#[derive(Eq)]`
|
LL + #[derive(Eq)]
LL | struct S;
|

error: aborting due to 6 previous errors

Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
Loading