From c2b9a8afa9674f473ac78bc0fad76b18c78b7667 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 11 Aug 2025 16:57:40 +0000 Subject: [PATCH] Make sure to treat only param where clauses as inherent --- compiler/rustc_hir_typeck/src/method/probe.rs | 27 +++++++++++ .../rigid-alias-bound-is-not-inherent-2.rs | 17 +++++++ ...rigid-alias-bound-is-not-inherent-2.stderr | 17 +++++++ .../rigid-alias-bound-is-not-inherent-3.rs | 26 +++++++++++ ...rigid-alias-bound-is-not-inherent-3.stderr | 30 ++++++++++++ ...alias-bound-is-not-inherent.current.stderr | 30 ++++++++++++ ...id-alias-bound-is-not-inherent.next.stderr | 30 ++++++++++++ .../rigid-alias-bound-is-not-inherent.rs | 46 +++++++++++++++++++ 8 files changed, 223 insertions(+) create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent.current.stderr create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr create mode 100644 tests/ui/methods/rigid-alias-bound-is-not-inherent.rs diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 3ca3b56b87e01..f5c3ebd837583 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -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) { diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs b/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs new file mode 100644 index 0000000000000..8363ec1b3fb54 --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs @@ -0,0 +1,17 @@ +// Regression test for . + +mod module { + pub trait Trait { + fn method(&self); + } +} + +// Note that we do not import Trait +use std::ops::Deref; + +fn foo(x: impl Deref) { + x.method(); + //~^ ERROR no method named `method` found for type parameter +} + +fn main() {} diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr new file mode 100644 index 0000000000000..433cab9cf9e4f --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr @@ -0,0 +1,17 @@ +error[E0599]: no method named `method` found for type parameter `impl Deref` in the current scope + --> $DIR/rigid-alias-bound-is-not-inherent-2.rs:13:7 + | +LL | fn foo(x: impl Deref) { + | --------------------------------- method `method` not found for this type parameter +LL | x.method(); + | ^^^^^^ method not found in `impl Deref` + | + = 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`. diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs b/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs new file mode 100644 index 0000000000000..bb316eed34de7 --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs @@ -0,0 +1,26 @@ +use std::ops::Deref; + +trait Trait1 { + fn call_me(&self) {} +} + +impl Trait1 for T {} + +trait Trait2 { + fn call_me(&self) {} +} + +impl Trait2 for T {} + +pub fn foo(x: T) +where + T: Deref, + 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() {} diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr new file mode 100644 index 0000000000000..466ad4d2abc96 --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr @@ -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`. diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent.current.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent.current.stderr new file mode 100644 index 0000000000000..4652bf5e3c586 --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent.current.stderr @@ -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`. diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr b/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr new file mode 100644 index 0000000000000..afacb3a7d5213 --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr @@ -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`. diff --git a/tests/ui/methods/rigid-alias-bound-is-not-inherent.rs b/tests/ui/methods/rigid-alias-bound-is-not-inherent.rs new file mode 100644 index 0000000000000..3dd63df3f39b9 --- /dev/null +++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent.rs @@ -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 `::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 `::Target`, meaning that it would be preferred over +// an "extension" candidate like `::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 Trait2 for T {} + +trait Other {} + +fn foo(x: T) +where + T: 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() {}