Rollup merge of #120396 - estebank:method-on-unbounded-type-param, r=nnethercote
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 #120186.
This commit is contained in:
commit
a0c3b87823
3 changed files with 116 additions and 14 deletions
|
@ -555,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();
|
||||
|
||||
|
@ -1326,7 +1337,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(
|
||||
|
@ -2919,19 +2930,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)
|
||||
|
@ -2979,6 +2978,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
item.visibility(self.tcx).is_public() || info.def_id.is_local()
|
||||
})
|
||||
.is_some()
|
||||
&& (matches!(rcvr_ty.kind(), ty::Param(_))
|
||||
|| 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,
|
||||
}
|
||||
}))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
for span in &arbitrary_rcvr {
|
||||
|
|
15
tests/ui/traits/method-on-unbounded-type-param.rs
Normal file
15
tests/ui/traits/method-on-unbounded-type-param.rs
Normal file
|
@ -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
|
||||
}
|
74
tests/ui/traits/method-on-unbounded-type-param.stderr
Normal file
74
tests/ui/traits/method-on-unbounded-type-param.stderr
Normal file
|
@ -0,0 +1,74 @@
|
|||
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: 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 {
|
||||
| +++++++++++++++++++++++++
|
||||
|
||||
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: consider restricting the type parameters to satisfy the trait bounds
|
||||
|
|
||||
LL | fn h<T>(a: &T, b: T) -> std::cmp::Ordering where T: Iterator, T: Ord {
|
||||
| +++++++++++++++++++++++++
|
||||
|
||||
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`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
Loading…
Add table
Reference in a new issue