Auto merge of #145262 - compiler-errors:prefer-only-param, r=lcnr
Make sure to treat only param where clauses as inherent See the description in the test file. This PR fixes a bug introduced by rust-lang/rust#141333, where we considered non-`Param` where clauses to be "inherent" for the purpose of method probing, which leads to both changes in method ambiguity (see test) and also import usage linting (and thus fixes https://github.com/rust-lang/rust/issues/145185). r? `@lcnr`
This commit is contained in:
commit
a1dbb44352
8 changed files with 223 additions and 0 deletions
|
|
@ -909,6 +909,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
debug_assert_matches!(param_ty.kind(), ty::Param(_));
|
||||
|
||||
let tcx = self.tcx;
|
||||
|
||||
// We use `DeepRejectCtxt` here which may return false positive on where clauses
|
||||
// with alias self types. We need to later on reject these as inherent candidates
|
||||
// in `consider_probe`.
|
||||
let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| {
|
||||
let bound_predicate = predicate.kind();
|
||||
match bound_predicate.skip_binder() {
|
||||
|
|
@ -1945,6 +1949,29 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
|||
);
|
||||
(xform_self_ty, xform_ret_ty) =
|
||||
self.xform_self_ty(probe.item, trait_ref.self_ty(), trait_ref.args);
|
||||
|
||||
if matches!(probe.kind, WhereClauseCandidate(_)) {
|
||||
// `WhereClauseCandidate` requires that the self type is a param,
|
||||
// because it has special behavior with candidate preference as an
|
||||
// inherent pick.
|
||||
match ocx.structurally_normalize_ty(
|
||||
cause,
|
||||
self.param_env,
|
||||
trait_ref.self_ty(),
|
||||
) {
|
||||
Ok(ty) => {
|
||||
if !matches!(ty.kind(), ty::Param(_)) {
|
||||
debug!("--> not a param ty: {xform_self_ty:?}");
|
||||
return ProbeResult::NoMatch;
|
||||
}
|
||||
}
|
||||
Err(errors) => {
|
||||
debug!("--> cannot relate self-types {:?}", errors);
|
||||
return ProbeResult::NoMatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xform_self_ty = ocx.normalize(cause, self.param_env, xform_self_ty);
|
||||
match ocx.relate(cause, self.param_env, self.variance(), self_ty, xform_self_ty)
|
||||
{
|
||||
|
|
|
|||
17
tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs
Normal file
17
tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Regression test for <github.com/rust-lang/rust/issues/145185>.
|
||||
|
||||
mod module {
|
||||
pub trait Trait {
|
||||
fn method(&self);
|
||||
}
|
||||
}
|
||||
|
||||
// Note that we do not import Trait
|
||||
use std::ops::Deref;
|
||||
|
||||
fn foo(x: impl Deref<Target: module::Trait>) {
|
||||
x.method();
|
||||
//~^ ERROR no method named `method` found for type parameter
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
17
tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr
Normal file
17
tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
error[E0599]: no method named `method` found for type parameter `impl Deref<Target : module::Trait>` in the current scope
|
||||
--> $DIR/rigid-alias-bound-is-not-inherent-2.rs:13:7
|
||||
|
|
||||
LL | fn foo(x: impl Deref<Target: module::Trait>) {
|
||||
| --------------------------------- method `method` not found for this type parameter
|
||||
LL | x.method();
|
||||
| ^^^^^^ method not found in `impl Deref<Target : module::Trait>`
|
||||
|
|
||||
= help: items from traits can only be used if the trait is in scope
|
||||
help: trait `Trait` which provides `method` is implemented but not in scope; perhaps you want to import it
|
||||
|
|
||||
LL + use module::Trait;
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0599`.
|
||||
26
tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs
Normal file
26
tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
trait Trait1 {
|
||||
fn call_me(&self) {}
|
||||
}
|
||||
|
||||
impl<T> Trait1 for T {}
|
||||
|
||||
trait Trait2 {
|
||||
fn call_me(&self) {}
|
||||
}
|
||||
|
||||
impl<T> Trait2 for T {}
|
||||
|
||||
pub fn foo<T, U>(x: T)
|
||||
where
|
||||
T: Deref<Target = U>,
|
||||
U: Trait1,
|
||||
{
|
||||
// This should be ambiguous. The fact that there's an inherent where-bound
|
||||
// candidate for `U` should not impact the candidates for `T`
|
||||
x.call_me();
|
||||
//~^ ERROR multiple applicable items in scope
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
30
tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr
Normal file
30
tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
error[E0034]: multiple applicable items in scope
|
||||
--> $DIR/rigid-alias-bound-is-not-inherent-3.rs:22:7
|
||||
|
|
||||
LL | x.call_me();
|
||||
| ^^^^^^^ multiple `call_me` found
|
||||
|
|
||||
note: candidate #1 is defined in an impl of the trait `Trait1` for the type `T`
|
||||
--> $DIR/rigid-alias-bound-is-not-inherent-3.rs:4:5
|
||||
|
|
||||
LL | fn call_me(&self) {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
note: candidate #2 is defined in an impl of the trait `Trait2` for the type `T`
|
||||
--> $DIR/rigid-alias-bound-is-not-inherent-3.rs:10:5
|
||||
|
|
||||
LL | fn call_me(&self) {}
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
help: disambiguate the method for candidate #1
|
||||
|
|
||||
LL - x.call_me();
|
||||
LL + Trait1::call_me(&x);
|
||||
|
|
||||
help: disambiguate the method for candidate #2
|
||||
|
|
||||
LL - x.call_me();
|
||||
LL + Trait2::call_me(&x);
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0034`.
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
error[E0034]: multiple applicable items in scope
|
||||
--> $DIR/rigid-alias-bound-is-not-inherent.rs:42:7
|
||||
|
|
||||
LL | x.method();
|
||||
| ^^^^^^ multiple `method` found
|
||||
|
|
||||
note: candidate #1 is defined in the trait `Trait1`
|
||||
--> $DIR/rigid-alias-bound-is-not-inherent.rs:21:5
|
||||
|
|
||||
LL | fn method(&self) {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
note: candidate #2 is defined in an impl of the trait `Trait2` for the type `T`
|
||||
--> $DIR/rigid-alias-bound-is-not-inherent.rs:27:5
|
||||
|
|
||||
LL | fn method(&self) {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
help: disambiguate the method for candidate #1
|
||||
|
|
||||
LL - x.method();
|
||||
LL + Trait1::method(&x);
|
||||
|
|
||||
help: disambiguate the method for candidate #2
|
||||
|
|
||||
LL - x.method();
|
||||
LL + Trait2::method(&x);
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0034`.
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
error[E0034]: multiple applicable items in scope
|
||||
--> $DIR/rigid-alias-bound-is-not-inherent.rs:42:7
|
||||
|
|
||||
LL | x.method();
|
||||
| ^^^^^^ multiple `method` found
|
||||
|
|
||||
note: candidate #1 is defined in the trait `Trait1`
|
||||
--> $DIR/rigid-alias-bound-is-not-inherent.rs:21:5
|
||||
|
|
||||
LL | fn method(&self) {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
note: candidate #2 is defined in the trait `Trait2`
|
||||
--> $DIR/rigid-alias-bound-is-not-inherent.rs:27:5
|
||||
|
|
||||
LL | fn method(&self) {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
help: disambiguate the method for candidate #1
|
||||
|
|
||||
LL - x.method();
|
||||
LL + Trait1::method(&x);
|
||||
|
|
||||
help: disambiguate the method for candidate #2
|
||||
|
|
||||
LL - x.method();
|
||||
LL + Trait2::method(&x);
|
||||
|
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0034`.
|
||||
46
tests/ui/methods/rigid-alias-bound-is-not-inherent.rs
Normal file
46
tests/ui/methods/rigid-alias-bound-is-not-inherent.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
//@ revisions: current next
|
||||
//@ ignore-compare-mode-next-solver (explicit revisions)
|
||||
//@[next] compile-flags: -Znext-solver
|
||||
|
||||
// See the code below.
|
||||
//
|
||||
// We were using `DeepRejectCtxt` to ensure that `assemble_inherent_candidates_from_param`
|
||||
// did not rely on the param-env being eagerly normalized. Since aliases unify with all
|
||||
// types, this meant that a rigid param-env candidate like `<T as Deref>::Target: Trait1`
|
||||
// would be registered as a "WhereClauseCandidate", which is treated as inherent. Since
|
||||
// we evaluate these candidates for all self types in the deref chain, this candidate
|
||||
// would be satisfied for `<T as Deref>::Target`, meaning that it would be preferred over
|
||||
// an "extension" candidate like `<T as Deref>::Target: Trait2` even though it holds.
|
||||
// This is problematic, since it causes ambiguities to be broken somewhat arbitrarily.
|
||||
// And as a side-effect, it also caused our computation of "used" traits to be miscalculated
|
||||
// since inherent candidates don't count as an import usage.
|
||||
|
||||
use std::ops::Deref;
|
||||
|
||||
trait Trait1 {
|
||||
fn method(&self) {
|
||||
println!("1");
|
||||
}
|
||||
}
|
||||
|
||||
trait Trait2 {
|
||||
fn method(&self) {
|
||||
println!("2");
|
||||
}
|
||||
}
|
||||
impl<T: Other + ?Sized> Trait2 for T {}
|
||||
|
||||
trait Other {}
|
||||
|
||||
fn foo<T>(x: T)
|
||||
where
|
||||
T: Deref,
|
||||
<T as Deref>::Target: Trait1 + Other,
|
||||
{
|
||||
// Make sure that we don't prefer methods from where clauses for rigid aliases,
|
||||
// just for params. We could revisit this behavior, but it would be a lang change.
|
||||
x.method();
|
||||
//~^ ERROR multiple applicable items in scope
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue