From 4f2baaa9c6b75d4267eea9e1096339d56d380615 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 25 Mar 2025 18:57:04 +0000
Subject: [PATCH] Do not mix normalized and unnormalized caller bounds when
 constructing param-env for receiver_is_dispatchable

---
 .../src/traits/dyn_compatibility.rs           | 35 ++++++++++++-------
 tests/ui/associated-types/issue-59324.stderr  | 26 +++++++-------
 ...-do-not-mix-normalized-and-unnormalized.rs | 26 ++++++++++++++
 3 files changed, 61 insertions(+), 26 deletions(-)
 create mode 100644 tests/ui/self/dyn-dispatch-do-not-mix-normalized-and-unnormalized.rs

diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index 78a452439836f..fa6bbf1d6e57b 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -583,27 +583,36 @@ fn receiver_is_dispatchable<'tcx>(
     // create a modified param env, with `Self: Unsize<U>` and `U: Trait` (and all of
     // its supertraits) added to caller bounds. `U: ?Sized` is already implied here.
     let param_env = {
-        let param_env = tcx.param_env(method.def_id);
+        // N.B. We generally want to emulate the construction of the `unnormalized_param_env`
+        // in the param-env query here. The fact that we don't just start with the clauses
+        // in the param-env of the method is because those are already normalized, and mixing
+        // normalized and unnormalized copies of predicates in `normalize_param_env_or_error`
+        // will cause ambiguity that the user can't really avoid.
+        //
+        // We leave out certain complexities of the param-env query here. Specifically, we:
+        // 1. Do not add `~const` bounds since there are no `dyn const Trait`s.
+        // 2. Do not add RPITIT self projection bounds for defaulted methods, since we
+        //    are not constructing a param-env for "inside" of the body of the defaulted
+        //    method, so we don't really care about projecting to a specific RPIT type,
+        //    and because RPITITs are not dyn compatible (yet).
+        let mut predicates = tcx.predicates_of(method.def_id).instantiate_identity(tcx).predicates;
 
         // Self: Unsize<U>
         let unsize_predicate =
-            ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]).upcast(tcx);
+            ty::TraitRef::new(tcx, unsize_did, [tcx.types.self_param, unsized_self_ty]);
+        predicates.push(unsize_predicate.upcast(tcx));
 
         // U: Trait<Arg1, ..., ArgN>
-        let trait_predicate = {
-            let trait_def_id = method.trait_container(tcx).unwrap();
-            let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| {
-                if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
-            });
-
-            ty::TraitRef::new_from_args(tcx, trait_def_id, args).upcast(tcx)
-        };
+        let trait_def_id = method.trait_container(tcx).unwrap();
+        let args = GenericArgs::for_item(tcx, trait_def_id, |param, _| {
+            if param.index == 0 { unsized_self_ty.into() } else { tcx.mk_param_from_def(param) }
+        });
+        let trait_predicate = ty::TraitRef::new_from_args(tcx, trait_def_id, args);
+        predicates.push(trait_predicate.upcast(tcx));
 
         normalize_param_env_or_error(
             tcx,
-            ty::ParamEnv::new(tcx.mk_clauses_from_iter(
-                param_env.caller_bounds().iter().chain([unsize_predicate, trait_predicate]),
-            )),
+            ty::ParamEnv::new(tcx.mk_clauses(&predicates)),
             ObligationCause::dummy_with_span(tcx.def_span(method.def_id)),
         )
     };
diff --git a/tests/ui/associated-types/issue-59324.stderr b/tests/ui/associated-types/issue-59324.stderr
index f5e696b7ac1ce..f79afc89d10fb 100644
--- a/tests/ui/associated-types/issue-59324.stderr
+++ b/tests/ui/associated-types/issue-59324.stderr
@@ -36,11 +36,18 @@ LL | |
 LL | |         &self,
 LL | |     ) -> Self::AssocType;
    | |_________________________^ the trait `Foo` is not implemented for `Bug`
+
+error[E0277]: the trait bound `(): Foo` is not satisfied
+  --> $DIR/issue-59324.rs:24:29
    |
-help: consider further restricting type parameter `Bug` with trait `Foo`
+LL | fn with_factory<H>(factory: dyn ThriftService<()>) {}
+   |                             ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
    |
-LL | pub trait ThriftService<Bug: NotFoo + Foo>:
-   |                                     +++++
+help: this trait has no implementations, consider adding one
+  --> $DIR/issue-59324.rs:3:1
+   |
+LL | pub trait Foo: NotFoo {
+   | ^^^^^^^^^^^^^^^^^^^^^
 
 error[E0277]: the trait bound `Bug: Foo` is not satisfied
   --> $DIR/issue-59324.rs:16:5
@@ -51,18 +58,11 @@ LL | |
 LL | |         &self,
 LL | |     ) -> Self::AssocType;
    | |_________________________^ the trait `Foo` is not implemented for `Bug`
-
-error[E0277]: the trait bound `(): Foo` is not satisfied
-  --> $DIR/issue-59324.rs:24:29
    |
-LL | fn with_factory<H>(factory: dyn ThriftService<()>) {}
-   |                             ^^^^^^^^^^^^^^^^^^^^^ the trait `Foo` is not implemented for `()`
-   |
-help: this trait has no implementations, consider adding one
-  --> $DIR/issue-59324.rs:3:1
+help: consider further restricting type parameter `Bug` with trait `Foo`
    |
-LL | pub trait Foo: NotFoo {
-   | ^^^^^^^^^^^^^^^^^^^^^
+LL | pub trait ThriftService<Bug: NotFoo + Foo>:
+   |                                     +++++
 
 error[E0277]: the trait bound `Bug: Foo` is not satisfied
   --> $DIR/issue-59324.rs:20:10
diff --git a/tests/ui/self/dyn-dispatch-do-not-mix-normalized-and-unnormalized.rs b/tests/ui/self/dyn-dispatch-do-not-mix-normalized-and-unnormalized.rs
new file mode 100644
index 0000000000000..2bc70de2a340d
--- /dev/null
+++ b/tests/ui/self/dyn-dispatch-do-not-mix-normalized-and-unnormalized.rs
@@ -0,0 +1,26 @@
+//@ check-pass
+
+// Regression test for <https://github.com/rust-lang/rust/issues/138937>.
+
+// Previously, we'd take the normalized param env's clauses which included
+// `<PF as TraitC>::Value = i32`, which comes from the supertraits of `TraitD`
+// after normalizing `<PF as TraitC>::Value = <PF as TraitD>::Scalar`. Since
+// `normalize_param_env_or_error` ends up re-elaborating `PF: TraitD`, we'd
+// end up with both versions of this predicate (normalized and unnormalized).
+// Since these projections preds are not equal, we'd fail with ambiguity.
+
+trait TraitB<T> {}
+
+trait TraitC: TraitB<Self::Value> {
+    type Value;
+}
+
+trait TraitD: TraitC<Value = Self::Scalar> {
+    type Scalar;
+}
+
+trait TraitE {
+    fn apply<PF: TraitD<Scalar = i32>>(&self);
+}
+
+fn main() {}