Skip to content

Commit dda2bdf

Browse files
committed
Account for unbounded type param receiver in suggestions
When encountering ```rust fn f<T>(a: T, b: T) -> std::cmp::Ordering { a.cmp(&b) //~ ERROR E0599 } ``` output ``` 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 { | ++++++++++ ``` Fix rust-lang#120186.
1 parent 3a4c08c commit dda2bdf

File tree

2 files changed

+35
-22
lines changed

2 files changed

+35
-22
lines changed

compiler/rustc_hir_typeck/src/method/suggest.rs

+27-14
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
563563
"`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
564564
));
565565
}
566+
} else if !unsatisfied_predicates.is_empty() && matches!(rcvr_ty.kind(), ty::Param(_)) {
567+
// We special case the situation where we are looking for `_` in
568+
// `<TypeParam as _>::method` because otherwise the machinery will look for blanket
569+
// implementations that have unsatisfied trait bounds to suggest, leading us to claim
570+
// things like "we're looking for a trait with method `cmp`, both `Iterator` and `Ord`
571+
// have one, in order to implement `Ord` you need to restrict `TypeParam: FnPtr` so
572+
// that `impl<T: FnPtr> Ord for T` can apply", which is not what we want. We have a type
573+
// parameter, we want to directly say "`Ord::cmp` and `Iterator::cmp` exist, restrict
574+
// `TypeParam: Ord` or `TypeParam: Iterator". That is done further down when calling
575+
// `self.suggest_traits_to_import`, so we ignore the `unsatisfied_predicates`
576+
// suggestions.
566577
} else if !unsatisfied_predicates.is_empty() {
567578
let mut type_params = FxIndexMap::default();
568579

@@ -1301,7 +1312,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13011312
}
13021313
}
13031314
self.note_derefed_ty_has_method(&mut err, source, rcvr_ty, item_name, expected);
1304-
return Some(err);
1315+
Some(err)
13051316
}
13061317

13071318
fn note_candidates_on_method_error(
@@ -2884,19 +2895,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
28842895
// this isn't perfect (that is, there are cases when
28852896
// implementing a trait would be legal but is rejected
28862897
// here).
2887-
unsatisfied_predicates.iter().all(|(p, _, _)| {
2888-
match p.kind().skip_binder() {
2889-
// Hide traits if they are present in predicates as they can be fixed without
2890-
// having to implement them.
2891-
ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
2892-
t.def_id() == info.def_id
2893-
}
2894-
ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
2895-
p.projection_ty.def_id == info.def_id
2896-
}
2897-
_ => false,
2898-
}
2899-
}) && (type_is_local || info.def_id.is_local())
2898+
(type_is_local || info.def_id.is_local())
29002899
&& !self.tcx.trait_is_auto(info.def_id)
29012900
&& self
29022901
.associated_value(info.def_id, item_name)
@@ -2944,6 +2943,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
29442943
item.visibility(self.tcx).is_public() || info.def_id.is_local()
29452944
})
29462945
.is_some()
2946+
&& (matches!(rcvr_ty.kind(), ty::Param(_))
2947+
|| unsatisfied_predicates.iter().all(|(p, _, _)| {
2948+
match p.kind().skip_binder() {
2949+
// Hide traits if they are present in predicates as they can be fixed without
2950+
// having to implement them.
2951+
ty::PredicateKind::Clause(ty::ClauseKind::Trait(t)) => {
2952+
t.def_id() == info.def_id
2953+
}
2954+
ty::PredicateKind::Clause(ty::ClauseKind::Projection(p)) => {
2955+
p.projection_ty.def_id == info.def_id
2956+
}
2957+
_ => false,
2958+
}
2959+
}))
29472960
})
29482961
.collect::<Vec<_>>();
29492962
for span in &arbitrary_rcvr {

tests/ui/traits/method-on-unbounded-type-param.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
error[E0599]: `T` is not an iterator
1+
error[E0599]: no method named `cmp` found for type parameter `T` in the current scope
22
--> $DIR/method-on-unbounded-type-param.rs:2:7
33
|
44
LL | fn f<T>(a: T, b: T) -> std::cmp::Ordering {
55
| - method `cmp` not found for this type parameter
66
LL | a.cmp(&b)
7-
| ^^^ `T` is not an iterator
7+
| ^^^ method cannot be called on `T` due to unsatisfied trait bounds
88
|
9-
= note: the following trait bounds were not satisfied:
10-
`T: Iterator`
11-
which is required by `&mut T: Iterator`
12-
help: consider restricting the type parameter to satisfy the trait bound
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:
1311
|
14-
LL | fn f<T>(a: T, b: T) -> std::cmp::Ordering where T: Iterator {
15-
| +++++++++++++++++
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+
| ++++++++++
1616

1717
error[E0599]: the method `cmp` exists for reference `&T`, but its trait bounds were not satisfied
1818
--> $DIR/method-on-unbounded-type-param.rs:5:10

0 commit comments

Comments
 (0)