about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-08-11 16:57:40 +0000
committerMichael Goulet <michael@errs.io>2025-08-24 20:30:52 +0000
commitc2b9a8afa9674f473ac78bc0fad76b18c78b7667 (patch)
tree955eae5f1ebb587fc51472fb3022cde05c1764e0
parent3776358beb5747d938bdefaf47a1c76723e6a372 (diff)
downloadrust-c2b9a8afa9674f473ac78bc0fad76b18c78b7667.tar.gz
rust-c2b9a8afa9674f473ac78bc0fad76b18c78b7667.zip
Make sure to treat only param where clauses as inherent
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs27
-rw-r--r--tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs17
-rw-r--r--tests/ui/methods/rigid-alias-bound-is-not-inherent-2.stderr17
-rw-r--r--tests/ui/methods/rigid-alias-bound-is-not-inherent-3.rs26
-rw-r--r--tests/ui/methods/rigid-alias-bound-is-not-inherent-3.stderr30
-rw-r--r--tests/ui/methods/rigid-alias-bound-is-not-inherent.current.stderr30
-rw-r--r--tests/ui/methods/rigid-alias-bound-is-not-inherent.next.stderr30
-rw-r--r--tests/ui/methods/rigid-alias-bound-is-not-inherent.rs46
8 files changed, 223 insertions, 0 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 3ca3b56b87e..f5c3ebd8375 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 00000000000..8363ec1b3fb
--- /dev/null
+++ b/tests/ui/methods/rigid-alias-bound-is-not-inherent-2.rs
@@ -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() {}
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 00000000000..433cab9cf9e
--- /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<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`.
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 00000000000..bb316eed34d
--- /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<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() {}
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 00000000000..466ad4d2abc
--- /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 00000000000..4652bf5e3c5
--- /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 00000000000..afacb3a7d52
--- /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 00000000000..3dd63df3f39
--- /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 `<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() {}