Repository: rust-lang/rust
Pull request: `FindParamInClause` handle edge-cases

<!-- homu-ignore:start -->
*[View all comments](https://triagebot.infra.rust-lang.org/gh-comments/rust-lang/rust/pull/153614)*
<!-- homu-ignore:end -->

If normalization is ambiguous, we want to treat the where-bound as non-global. The affected code should pretty much always have other errors in that case. It does make reasoning about trait solver cycles and rerunning more confusing and just feels wrong :tm: I encountered this while looking into `ml-kem` (https://github.com/rust-lang/trait-system-refactor-initiative/issues/246). I don't know whether this matters in practice and don't care to look into it too deeply.

We also want to properly handle concrete aliases which mention to something mentioning a generic parameter due to another where-bound.

r? @BoxyUwU

Changed files:
filename: compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
patch: @@ -9,7 +9,6 @@ use derive_where::derive_where;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::SolverTraitLangItem;
 use rustc_type_ir::search_graph::CandidateHeadUsages;
-use rustc_type_ir::solve::Certainty::Maybe;
 use rustc_type_ir::solve::{AliasBoundKind, SizedTraitKind};
 use rustc_type_ir::{
     self as ty, Interner, TypeFlags, TypeFoldable, TypeFolder, TypeSuperFoldable,
@@ -1286,28 +1285,34 @@ where
             return ControlFlow::Break(Err(NoSolution));
         };
 
-        if let ty::Placeholder(p) = ty.kind() {
-            if p.universe() == ty::UniverseIndex::ROOT {
-                ControlFlow::Break(Ok(Certainty::Yes))
-            } else {
-                ControlFlow::Continue(())
+        match ty.kind() {
+            ty::Placeholder(p) => {
+                if p.universe() == ty::UniverseIndex::ROOT {
+                    ControlFlow::Break(Ok(Certainty::Yes))
+                } else {
+                    ControlFlow::Continue(())
+                }
             }
-        } else if ty.has_type_flags(TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_RE_INFER) {
-            self.recursion_depth += 1;
-            if self.recursion_depth > self.ecx.cx().recursion_limit() {
-                return ControlFlow::Break(Ok(Maybe {
-                    cause: MaybeCause::Overflow {
-                        suggest_increasing_limit: true,
-                        keep_constraints: false,
-                    },
-                    opaque_types_jank: OpaqueTypesJank::AllGood,
-                }));
+            ty::Infer(_) => ControlFlow::Break(Ok(Certainty::AMBIGUOUS)),
+            _ if ty.has_type_flags(
+                TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_INFER | TypeFlags::HAS_ALIAS,
+            ) =>
+            {
+                self.recursion_depth += 1;
+                if self.recursion_depth > self.ecx.cx().recursion_limit() {
+                    return ControlFlow::Break(Ok(Certainty::Maybe {
+                        cause: MaybeCause::Overflow {
+                            suggest_increasing_limit: true,
+                            keep_constraints: false,
+                        },
+                        opaque_types_jank: OpaqueTypesJank::AllGood,
+                    }));
+                }
+                let result = ty.super_visit_with(self);
+                self.recursion_depth -= 1;
+                result
             }
-            let result = ty.super_visit_with(self);
-            self.recursion_depth -= 1;
-            result
-        } else {
-            ControlFlow::Continue(())
+            _ => ControlFlow::Continue(()),
         }
     }
 
@@ -1317,16 +1322,23 @@ where
             return ControlFlow::Break(Err(NoSolution));
         };
 
-        if let ty::ConstKind::Placeholder(p) = ct.kind() {
-            if p.universe() == ty::UniverseIndex::ROOT {
-                ControlFlow::Break(Ok(Certainty::Yes))
-            } else {
-                ControlFlow::Continue(())
+        match ct.kind() {
+            ty::ConstKind::Placeholder(p) => {
+                if p.universe() == ty::UniverseIndex::ROOT {
+                    ControlFlow::Break(Ok(Certainty::Yes))
+                } else {
+                    ControlFlow::Continue(())
+                }
             }
-        } else if ct.has_type_flags(TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_RE_INFER) {
-            ct.super_visit_with(self)
-        } else {
-            ControlFlow::Continue(())
+            ty::ConstKind::Infer(_) => ControlFlow::Break(Ok(Certainty::AMBIGUOUS)),
+            _ if ct.has_type_flags(
+                TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_INFER | TypeFlags::HAS_ALIAS,
+            ) =>
+            {
+                // FIXME(mgca): we should also check the recursion limit here
+                ct.super_visit_with(self)
+            }
+            _ => ControlFlow::Continue(()),
         }
     }
 

filename: tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.current.stderr
patch: @@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/is-global-norm-concrete-alias-to-generic.rs:26:5
+   |
+LL | fn foo<T>(x: <(*const T,) as Id>::This) -> (*const T,)
+   |                                            ----------- expected `(*const T,)` because of return type
+...
+LL |     x
+   |     ^ expected `(*const T,)`, found associated type
+   |
+   = note:        expected tuple `(*const T,)`
+           found associated type `<(*const T,) as Id>::This`
+   = help: consider constraining the associated type `<(*const T,) as Id>::This` to `(*const T,)`
+   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
+   = note: the associated type `<(*const T,) as Id>::This` is defined as `(*const T,)` in the implementation, but the where-bound `(*const T,)` shadows this definition
+           see issue #152409 <https://github.com/rust-lang/rust/issues/152409> for more information
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.

filename: tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.next.stderr
patch: @@ -0,0 +1,19 @@
+error[E0308]: mismatched types
+  --> $DIR/is-global-norm-concrete-alias-to-generic.rs:26:5
+   |
+LL | fn foo<T>(x: <(*const T,) as Id>::This) -> (*const T,)
+   |                                            ----------- expected `(*const T,)` because of return type
+...
+LL |     x
+   |     ^ types differ
+   |
+   = note:        expected tuple `(*const T,)`
+           found associated type `<(*const T,) as Id>::This`
+   = help: consider constraining the associated type `<(*const T,) as Id>::This` to `(*const T,)`
+   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
+   = note: the associated type `<(*const T,) as Id>::This` is defined as `(*const T,)` in the implementation, but the where-bound `(*const T,)` shadows this definition
+           see issue #152409 <https://github.com/rust-lang/rust/issues/152409> for more information
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.

filename: tests/ui/traits/next-solver/normalization-shadowing/is-global-norm-concrete-alias-to-generic.rs
patch: @@ -0,0 +1,29 @@
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+
+// A regression test making sure that where-bounds with concrete aliases
+// which normalize to something mentioning a generic parameters are
+// considered non-global.
+//
+// When checking this, we previously didn't recur into types if they didn't
+// mention any generic parameters, causing us to consider the `(<() as Id>::This,): Id`
+// where-bound as global, even though it normalizes to `(T,): Id`.
+
+trait Id {
+    type This;
+}
+
+impl<T> Id for T {
+    type This = T;
+}
+
+fn foo<T>(x: <(*const T,) as Id>::This) -> (*const T,)
+where
+    (): Id<This = *const T>,
+    (<() as Id>::This,): Id,
+{
+    x //~ ERROR mismatched types
+}
+
+fn main() {}

filename: tests/ui/traits/next-solver/normalize/normalize-param-env-4.next.stderr
patch: @@ -4,6 +4,24 @@ error[E0275]: overflow evaluating the requirement `<T as Trait>::Assoc: Trait`
 LL |     <T as Trait>::Assoc: Trait,
    |                          ^^^^^
 
-error: aborting due to 1 previous error
+error[E0275]: overflow evaluating whether `<T as Trait>::Assoc` is well-formed
+  --> $DIR/normalize-param-env-4.rs:19:26
+   |
+LL |     <T as Trait>::Assoc: Trait,
+   |                          ^^^^^
+
+error[E0275]: overflow evaluating the requirement `T: Trait`
+  --> $DIR/normalize-param-env-4.rs:32:19
+   |
+LL |     impls_trait::<T>();
+   |                   ^
+   |
+note: required by a bound in `impls_trait`
+  --> $DIR/normalize-param-env-4.rs:15:19
+   |
+LL | fn impls_trait<T: Trait>() {}
+   |                   ^^^^^ required by this bound in `impls_trait`
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0275`.