Skip to content

Commit 4d3da90

Browse files
authored
Rollup merge of rust-lang#120507 - estebank:issue-108428, r=davidtwco
Account for non-overlapping unmet trait bounds in suggestion When a method not found on a type parameter could have been provided by any of multiple traits, suggest each trait individually, instead of a single suggestion to restrict the type parameter with *all* of them. Before: ``` 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: consider restricting the type parameters to satisfy the trait bounds | LL | fn g<T>(a: T, b: T) -> std::cmp::Ordering where T: Iterator, T: Ord { | +++++++++++++++++++++++++ ``` After: ``` 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 { | ++++++++++ ``` Fix rust-lang#108428. Follow up to rust-lang#120396, only last commit is relevant.
2 parents b11fbfb + 5c41409 commit 4d3da90

10 files changed

+158
-46
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+34-37
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
540540

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

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

919-
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
920-
restrict_type_params = true;
921-
// #74886: Sort here so that the output is always the same.
922-
let obligations = obligations.into_sorted_stable_ord();
923-
err.span_suggestion_verbose(
924-
span,
925-
format!(
926-
"consider restricting the type parameter{s} to satisfy the \
927-
trait bound{s}",
928-
s = pluralize!(obligations.len())
929-
),
930-
format!("{} {}", add_where_or_comma, obligations.join(", ")),
931-
Applicability::MaybeIncorrect,
932-
);
931+
if !matches!(rcvr_ty.peel_refs().kind(), ty::Param(_)) {
932+
for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
933+
restrict_type_params = true;
934+
// #74886: Sort here so that the output is always the same.
935+
let obligations = obligations.into_sorted_stable_ord();
936+
err.span_suggestion_verbose(
937+
span,
938+
format!(
939+
"consider restricting the type parameter{s} to satisfy the trait \
940+
bound{s}",
941+
s = pluralize!(obligations.len())
942+
),
943+
format!("{} {}", add_where_or_comma, obligations.join(", ")),
944+
Applicability::MaybeIncorrect,
945+
);
946+
}
933947
}
934948

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

982996
unsatisfied_bounds = true;
983997
}
@@ -1200,7 +1214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12001214
);
12011215
}
12021216

1203-
if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params {
1217+
if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive {
12041218
} else {
12051219
self.suggest_traits_to_import(
12061220
&mut err,
@@ -1210,7 +1224,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12101224
args.map(|args| args.len() + 1),
12111225
source,
12121226
no_match_data.out_of_scope_traits.clone(),
1213-
unsatisfied_predicates,
12141227
static_candidates,
12151228
unsatisfied_bounds,
12161229
expected.only_has_type(self),
@@ -1325,7 +1338,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13251338
}
13261339
}
13271340
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected);
1328-
return Some(err);
1341+
Some(err)
13291342
}
13301343

13311344
fn note_candidates_on_method_error(
@@ -2470,7 +2483,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24702483
Option<ty::Predicate<'tcx>>,
24712484
Option<ObligationCause<'tcx>>,
24722485
)],
2473-
) {
2486+
) -> bool {
24742487
let mut derives = self.note_predicate_source_and_get_derives(err, unsatisfied_predicates);
24752488
derives.sort();
24762489
derives.dedup();
@@ -2495,6 +2508,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
24952508
Applicability::MaybeIncorrect,
24962509
);
24972510
}
2511+
!derives_grouped.is_empty()
24982512
}
24992513

25002514
fn note_derefed_ty_has_method(
@@ -2697,11 +2711,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
26972711
inputs_len: Option<usize>,
26982712
source: SelfSource<'tcx>,
26992713
valid_out_of_scope_traits: Vec<DefId>,
2700-
unsatisfied_predicates: &[(
2701-
ty::Predicate<'tcx>,
2702-
Option<ty::Predicate<'tcx>>,
2703-
Option<ObligationCause<'tcx>>,
2704-
)],
27052714
static_candidates: &[CandidateSource],
27062715
unsatisfied_bounds: bool,
27072716
return_type: Option<Ty<'tcx>>,
@@ -2918,19 +2927,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
29182927
// this isn't perfect (that is, there are cases when
29192928
// implementing a trait would be legal but is rejected
29202929
// here).
2921-
unsatisfied_predicates.iter().all(|(p, _, _)| {
2922-
match p.kind().skip_binder() {
2923-
// Hide traits if they are present in predicates as they can be fixed without
2924-
// having to implement them.
2925-
ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
2926-
t.def_id() == info.def_id
2927-
}
2928-
ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
2929-
p.projection_ty.def_id == info.def_id
2930-
}
2931-
_ => false,
2932-
}
2933-
}) && (type_is_local || info.def_id.is_local())
2930+
(type_is_local || info.def_id.is_local())
29342931
&& !self.tcx.trait_is_auto(info.def_id)
29352932
&& self
29362933
.associated_value(info.def_id, item_name)

tests/ui/box/unit/unique-object-noncopyable.stderr

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ LL | let _z = y.clone();
1212
which is required by `Box<dyn Foo>: Clone`
1313
`dyn Foo: Clone`
1414
which is required by `Box<dyn Foo>: Clone`
15+
= help: items from traits can only be used if the trait is implemented and in scope
16+
= note: the following trait defines an item `clone`, perhaps you need to implement it:
17+
candidate #1: `Clone`
1518

1619
error: aborting due to 1 previous error
1720

tests/ui/box/unit/unique-pinned-nocopy.stderr

-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ LL | let _j = i.clone();
1010
= note: the following trait bounds were not satisfied:
1111
`R: Clone`
1212
which is required by `Box<R>: Clone`
13-
= help: items from traits can only be used if the trait is implemented and in scope
14-
= note: the following trait defines an item `clone`, perhaps you need to implement it:
15-
candidate #1: `Clone`
1613
help: consider annotating `R` with `#[derive(Clone)]`
1714
|
1815
LL + #[derive(Clone)]

tests/ui/derives/derive-assoc-type-not-impl.stderr

-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ note: trait bound `NotClone: Clone` was not satisfied
1515
|
1616
LL | #[derive(Clone)]
1717
| ^^^^^ unsatisfied trait bound introduced in this `derive` macro
18-
= help: items from traits can only be used if the trait is implemented and in scope
19-
= note: the following trait defines an item `clone`, perhaps you need to implement it:
20-
candidate #1: `Clone`
2118
help: consider annotating `NotClone` with `#[derive(Clone)]`
2219
|
2320
LL + #[derive(Clone)]

tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ trait X {
55
type Y<T>;
66
}
77

8-
trait M {
8+
trait M { //~ NOTE
99
fn f(&self) {}
1010
}
1111

tests/ui/generic-associated-types/method-unsatisfied-assoc-type-predicate.stderr

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

1824
error: aborting due to 1 previous error
1925

tests/ui/higher-ranked/trait-bounds/issue-30786.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ note: the following trait bounds were not satisfied:
1515
|
1616
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
1717
| --------- - ^^^^^^ unsatisfied trait bound introduced here
18+
= help: items from traits can only be used if the trait is implemented and in scope
19+
note: `StreamExt` defines an item `filterx`, perhaps you need to implement it
20+
--> $DIR/issue-30786.rs:66:1
21+
|
22+
LL | pub trait StreamExt
23+
| ^^^^^^^^^^^^^^^^^^^
1824

1925
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
2026
--> $DIR/issue-30786.rs:132:24
@@ -33,6 +39,12 @@ note: the following trait bounds were not satisfied:
3339
|
3440
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
3541
| --------- - ^^^^^^ unsatisfied trait bound introduced here
42+
= help: items from traits can only be used if the trait is implemented and in scope
43+
note: `StreamExt` defines an item `countx`, perhaps you need to implement it
44+
--> $DIR/issue-30786.rs:66:1
45+
|
46+
LL | pub trait StreamExt
47+
| ^^^^^^^^^^^^^^^^^^^
3648

3749
error: aborting due to 2 previous errors
3850

tests/ui/methods/method-call-err-msg.stderr

+3-2
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ LL | | .take()
6363
note: the trait `Iterator` must be implemented
6464
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
6565
= help: items from traits can only be used if the trait is implemented and in scope
66-
= note: the following trait defines an item `take`, perhaps you need to implement it:
67-
candidate #1: `Iterator`
66+
= note: the following traits define an item `take`, perhaps you need to implement one of them:
67+
candidate #1: `std::io::Read`
68+
candidate #2: `Iterator`
6869

6970
error[E0061]: this method takes 3 arguments but 0 arguments were supplied
7071
--> $DIR/method-call-err-msg.rs:21:7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fn f<T>(a: T, b: T) -> std::cmp::Ordering {
2+
a.cmp(&b) //~ ERROR E0599
3+
}
4+
fn g<T>(a: T, b: T) -> std::cmp::Ordering {
5+
(&a).cmp(&b) //~ ERROR E0599
6+
}
7+
fn h<T>(a: &T, b: T) -> std::cmp::Ordering {
8+
a.cmp(&b) //~ ERROR E0599
9+
}
10+
trait T {}
11+
impl<X: std::cmp::Ord> T for X {}
12+
fn main() {
13+
let x: Box<dyn T> = Box::new(0);
14+
x.cmp(&x); //~ ERROR E0599
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
error[E0599]: no method named `cmp` found for type parameter `T` in the current scope
2+
--> $DIR/method-on-unbounded-type-param.rs:2:7
3+
|
4+
LL | fn f<T>(a: T, b: T) -> std::cmp::Ordering {
5+
| - method `cmp` not found for this type parameter
6+
LL | a.cmp(&b)
7+
| ^^^ method cannot be called on `T` due to unsatisfied trait bounds
8+
|
9+
= help: items from traits can only be used if the type parameter is bounded by the trait
10+
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
11+
|
12+
LL | fn f<T: Ord>(a: T, b: T) -> std::cmp::Ordering {
13+
| +++++
14+
LL | fn f<T: Iterator>(a: T, b: T) -> std::cmp::Ordering {
15+
| ++++++++++
16+
17+
error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied
18+
--> $DIR/method-on-unbounded-type-param.rs:5:10
19+
|
20+
LL | (&a).cmp(&b)
21+
| ^^^ method cannot be called on `&T` due to unsatisfied trait bounds
22+
|
23+
= note: the following trait bounds were not satisfied:
24+
`T: Ord`
25+
which is required by `&T: Ord`
26+
`&T: Iterator`
27+
which is required by `&mut &T: Iterator`
28+
`T: Iterator`
29+
which is required by `&mut T: Iterator`
30+
= help: items from traits can only be used if the type parameter is bounded by the trait
31+
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
32+
|
33+
LL | fn g<T: Ord>(a: T, b: T) -> std::cmp::Ordering {
34+
| +++++
35+
LL | fn g<T: Iterator>(a: T, b: T) -> std::cmp::Ordering {
36+
| ++++++++++
37+
38+
error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied
39+
--> $DIR/method-on-unbounded-type-param.rs:8:7
40+
|
41+
LL | a.cmp(&b)
42+
| ^^^ method cannot be called on `&T` due to unsatisfied trait bounds
43+
|
44+
= note: the following trait bounds were not satisfied:
45+
`T: Ord`
46+
which is required by `&T: Ord`
47+
`&T: Iterator`
48+
which is required by `&mut &T: Iterator`
49+
`T: Iterator`
50+
which is required by `&mut T: Iterator`
51+
= help: items from traits can only be used if the type parameter is bounded by the trait
52+
help: the following traits define an item `cmp`, perhaps you need to restrict type parameter `T` with one of them:
53+
|
54+
LL | fn h<T: Ord>(a: &T, b: T) -> std::cmp::Ordering {
55+
| +++++
56+
LL | fn h<T: Iterator>(a: &T, b: T) -> std::cmp::Ordering {
57+
| ++++++++++
58+
59+
error[E0599]: the method `cmp` exists for struct `Box<dyn T>`, but its trait bounds were not satisfied
60+
--> $DIR/method-on-unbounded-type-param.rs:14:7
61+
|
62+
LL | trait T {}
63+
| ------- doesn't satisfy `dyn T: Iterator` or `dyn T: Ord`
64+
...
65+
LL | x.cmp(&x);
66+
| ^^^ method cannot be called on `Box<dyn T>` due to unsatisfied trait bounds
67+
|
68+
= note: the following trait bounds were not satisfied:
69+
`dyn T: Iterator`
70+
which is required by `Box<dyn T>: Iterator`
71+
`dyn T: Ord`
72+
which is required by `Box<dyn T>: Ord`
73+
`Box<dyn T>: Iterator`
74+
which is required by `&mut Box<dyn T>: Iterator`
75+
`dyn T: Iterator`
76+
which is required by `&mut dyn T: Iterator`
77+
= help: items from traits can only be used if the trait is implemented and in scope
78+
= note: the following traits define an item `cmp`, perhaps you need to implement one of them:
79+
candidate #1: `Ord`
80+
candidate #2: `Iterator`
81+
82+
error: aborting due to 4 previous errors
83+
84+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)