Skip to content
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

Account for non-overlapping unmet trait bounds in suggestion #120507

Merged
merged 4 commits into from
Feb 7, 2024
Merged
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
71 changes: 34 additions & 37 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let mut bound_spans: SortedMap<Span, Vec<String>> = Default::default();
let mut restrict_type_params = false;
let mut suggested_derive = false;
let mut unsatisfied_bounds = false;
if item_name.name == sym::count && self.is_slice_ty(rcvr_ty, span) {
let msg = "consider using `len` instead";
Expand All @@ -554,6 +555,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
));
}
} else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) {
// We special case the situation where we are looking for `_` in
// `<TypeParam as _>::method` because otherwise the machinery will look for blanket
// implementations that have unsatisfied trait bounds to suggest, leading us to claim
// things like "we're looking for a trait with method `cmp`, both `Iterator` and `Ord`
// have one, in order to implement `Ord` you need to restrict `TypeParam: FnPtr` so
// that `impl<T: FnPtr> Ord for T` can apply", which is not what we want. We have a type
// parameter, we want to directly say "`Ord::cmp` and `Iterator::cmp` exist, restrict
// `TypeParam: Ord` or `TypeParam: Iterator`"". That is done further down when calling
// `self.suggest_traits_to_import`, so we ignore the `unsatisfied_predicates`
// suggestions.
} else if !unsatisfied_predicates.is_empty() {
let mut type_params = FxIndexMap::default();

Expand Down Expand Up @@ -916,20 +928,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.enumerate()
.collect::<Vec<(usize, String)>>();

for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
restrict_type_params = true;
// #74886: Sort here so that the output is always the same.
let obligations = obligations.into_sorted_stable_ord();
err.span_suggestion_verbose(
span,
format!(
"consider restricting the type parameter{s} to satisfy the \
trait bound{s}",
s = pluralize!(obligations.len())
),
format!("{} {}", add_where_or_comma, obligations.join(", ")),
Applicability::MaybeIncorrect,
);
if !matches!(rcvr_ty.peel_refs().kind(), ty::Param(_)) {
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
restrict_type_params = true;
// #74886: Sort here so that the output is always the same.
let obligations = obligations.into_sorted_stable_ord();
err.span_suggestion_verbose(
span,
format!(
"consider restricting the type parameter{s} to satisfy the trait \
bound{s}",
s = pluralize!(obligations.len())
),
format!("{} {}", add_where_or_comma, obligations.join(", ")),
Applicability::MaybeIncorrect,
);
}
}

bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically.
Expand Down Expand Up @@ -977,7 +991,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
"the following trait bounds were not satisfied:\n{bound_list}"
));
}
self.suggest_derive(&mut err, unsatisfied_predicates);
suggested_derive = self.suggest_derive(&mut err, unsatisfied_predicates);

unsatisfied_bounds = true;
}
Expand Down Expand Up @@ -1200,7 +1214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}

if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive {
} else {
self.suggest_traits_to_import(
&mut err,
Expand All @@ -1210,7 +1224,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
args.map(|args| args.len() + 1),
source,
no_match_data.out_of_scope_traits.clone(),
unsatisfied_predicates,
static_candidates,
unsatisfied_bounds,
expected.only_has_type(self),
Expand Down Expand Up @@ -1325,7 +1338,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected);
return Some(err);
Some(err)
}

fn note_candidates_on_method_error(
Expand Down Expand Up @@ -2470,7 +2483,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)],
) {
) -> bool {
let mut derives = self.note_predicate_source_and_get_derives(err, unsatisfied_predicates);
derives.sort();
derives.dedup();
Expand All @@ -2495,6 +2508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MaybeIncorrect,
);
}
!derives_grouped.is_empty()
}

fn note_derefed_ty_has_method(
Expand Down Expand Up @@ -2697,11 +2711,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
inputs_len: Option<usize>,
source: SelfSource<'tcx>,
valid_out_of_scope_traits: Vec<DefId>,
unsatisfied_predicates: &[(
ty::Predicate<'tcx>,
Option<ty::Predicate<'tcx>>,
Option<ObligationCause<'tcx>>,
)],
static_candidates: &[CandidateSource],
unsatisfied_bounds: bool,
return_type: Option<Ty<'tcx>>,
Expand Down Expand Up @@ -2918,19 +2927,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// this isn't perfect (that is, there are cases when
// implementing a trait would be legal but is rejected
// here).
unsatisfied_predicates.iter().all(|(p, _, _)| {
match p.kind().skip_binder() {
// Hide traits if they are present in predicates as they can be fixed without
// having to implement them.
ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
t.def_id() == info.def_id
}
ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
p.projection_ty.def_id == info.def_id
}
_ => false,
}
}) && (type_is_local || info.def_id.is_local())
(type_is_local || info.def_id.is_local())
&& !self.tcx.trait_is_auto(info.def_id)
&& self
.associated_value(info.def_id, item_name)
Expand Down
3 changes: 3 additions & 0 deletions tests/ui/box/unit/unique-object-noncopyable.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ LL | let _z = y.clone();
which is required by `Box<dyn Foo>: Clone`
`dyn Foo: Clone`
which is required by `Box<dyn Foo>: Clone`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`

error: aborting due to 1 previous error

Expand Down
3 changes: 0 additions & 3 deletions tests/ui/box/unit/unique-pinned-nocopy.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ LL | let _j = i.clone();
= note: the following trait bounds were not satisfied:
`R: Clone`
which is required by `Box<R>: Clone`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: consider annotating `R` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
Expand Down
3 changes: 0 additions & 3 deletions tests/ui/derives/derive-assoc-type-not-impl.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ note: trait bound `NotClone: Clone` was not satisfied
|
LL | #[derive(Clone)]
| ^^^^^ unsatisfied trait bound introduced in this `derive` macro
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `clone`, perhaps you need to implement it:
candidate #1: `Clone`
help: consider annotating `NotClone` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ trait X {
type Y<T>;
}

trait M {
trait M { //~ NOTE
fn f(&self) {}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ LL | impl<T: X<Y<i32> = i32>> M for T {}
| ^^^^^^^^^^^^ - -
| |
| unsatisfied trait bound introduced here
= help: items from traits can only be used if the trait is implemented and in scope
note: `M` defines an item `f`, perhaps you need to implement it
--> $DIR/method-unsatisfied-assoc-type-predicate.rs:8:1
|
LL | trait M {
| ^^^^^^^

error: aborting due to 1 previous error

Expand Down
12 changes: 12 additions & 0 deletions tests/ui/higher-ranked/trait-bounds/issue-30786.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ note: the following trait bounds were not satisfied:
|
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
| --------- - ^^^^^^ unsatisfied trait bound introduced here
= help: items from traits can only be used if the trait is implemented and in scope
note: `StreamExt` defines an item `filterx`, perhaps you need to implement it
--> $DIR/issue-30786.rs:66:1
|
LL | pub trait StreamExt
| ^^^^^^^^^^^^^^^^^^^

error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, fn(&u64) -> &u64 {identity::<u64>}>, {closure@issue-30786.rs:131:30}>`, but its trait bounds were not satisfied
--> $DIR/issue-30786.rs:132:24
Expand All @@ -33,6 +39,12 @@ note: the following trait bounds were not satisfied:
|
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
| --------- - ^^^^^^ unsatisfied trait bound introduced here
= help: items from traits can only be used if the trait is implemented and in scope
note: `StreamExt` defines an item `countx`, perhaps you need to implement it
--> $DIR/issue-30786.rs:66:1
|
LL | pub trait StreamExt
| ^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

Expand Down
5 changes: 3 additions & 2 deletions tests/ui/methods/method-call-err-msg.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ LL | | .take()
note: the trait `Iterator` must be implemented
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `take`, perhaps you need to implement it:
candidate #1: `Iterator`
= note: the following traits define an item `take`, perhaps you need to implement one of them:
candidate #1: `std::io::Read`
candidate #2: `Iterator`

error[E0061]: this method takes 3 arguments but 0 arguments were supplied
--> $DIR/method-call-err-msg.rs:21:7
Expand Down
15 changes: 15 additions & 0 deletions tests/ui/traits/method-on-unbounded-type-param.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fn f<T>(a: T, b: T) -> std::cmp::Ordering {
a.cmp(&b) //~ ERROR E0599
}
fn g<T>(a: T, b: T) -> std::cmp::Ordering {
(&a).cmp(&b) //~ ERROR E0599
}
fn h<T>(a: &T, b: T) -> std::cmp::Ordering {
a.cmp(&b) //~ ERROR E0599
}
trait T {}
impl<X: std::cmp::Ord> T for X {}
fn main() {
let x: Box<dyn T> = Box::new(0);
x.cmp(&x); //~ ERROR E0599
}
84 changes: 84 additions & 0 deletions tests/ui/traits/method-on-unbounded-type-param.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
error[E0599]: no method named `cmp` found for type parameter `T` in the current scope
--> $DIR/method-on-unbounded-type-param.rs:2:7
|
LL | fn f<T>(a: T, b: T) -> std::cmp::Ordering {
| - method `cmp` not found for this type parameter
LL | a.cmp(&b)
| ^^^ method cannot be called on `T` due to unsatisfied trait bounds
|
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
|
LL | fn f<T: Ord>(a: T, b: T) -> std::cmp::Ordering {
| +++++
LL | fn f<T: Iterator>(a: T, b: T) -> std::cmp::Ordering {
| ++++++++++

error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied
--> $DIR/method-on-unbounded-type-param.rs:5:10
|
LL | (&a).cmp(&b)
| ^^^ method cannot be called on `&T` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`T: Ord`
which is required by `&T: Ord`
`&T: Iterator`
which is required by `&mut &T: Iterator`
`T: Iterator`
which is required by `&mut T: Iterator`
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
|
LL | fn g<T: Ord>(a: T, b: T) -> std::cmp::Ordering {
| +++++
LL | fn g<T: Iterator>(a: T, b: T) -> std::cmp::Ordering {
| ++++++++++

error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied
--> $DIR/method-on-unbounded-type-param.rs:8:7
|
LL | a.cmp(&b)
| ^^^ method cannot be called on `&T` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`T: Ord`
which is required by `&T: Ord`
`&T: Iterator`
which is required by `&mut &T: Iterator`
`T: Iterator`
which is required by `&mut T: Iterator`
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
|
LL | fn h<T: Ord>(a: &T, b: T) -> std::cmp::Ordering {
| +++++
LL | fn h<T: Iterator>(a: &T, b: T) -> std::cmp::Ordering {
| ++++++++++

error[E0599]: the method `cmp` exists for struct `Box<dyn T>`, but its trait bounds were not satisfied
--> $DIR/method-on-unbounded-type-param.rs:14:7
|
LL | trait T {}
| ------- doesn't satisfy `dyn T: Iterator` or `dyn T: Ord`
...
LL | x.cmp(&x);
| ^^^ method cannot be called on `Box<dyn T>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`dyn T: Iterator`
which is required by `Box<dyn T>: Iterator`
`dyn T: Ord`
which is required by `Box<dyn T>: Ord`
`Box<dyn T>: Iterator`
which is required by `&mut Box<dyn T>: Iterator`
`dyn T: Iterator`
which is required by `&mut dyn T: Iterator`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following traits define an item `cmp`, perhaps you need to implement one of them:
candidate #1: `Ord`
candidate #2: `Iterator`

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0599`.
Loading