From 1fcf2eaa9f5ff9336e9b43f017eaf261acfdc2d3 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Fri, 22 Mar 2024 16:35:44 -0400
Subject: [PATCH] Uniquify ReError on input mode in canonicalizer

---
 .../src/canonicalizer.rs                      |  3 +-
 .../next-solver/dont-canonicalize-re-error.rs | 28 +++++++++++++++++++
 .../dont-canonicalize-re-error.stderr         | 21 ++++++++++++++
 3 files changed, 50 insertions(+), 2 deletions(-)
 create mode 100644 tests/ui/traits/next-solver/dont-canonicalize-re-error.rs
 create mode 100644 tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr

diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
index 95b30066662..2dbb44bdee2 100644
--- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
@@ -239,7 +239,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
             // FIXME: We should investigate the perf implications of not uniquifying
             // `ReErased`. We may be able to short-circuit registering region
             // obligations if we encounter a `ReErased` on one side, for example.
-            ty::ReStatic | ty::ReErased => match self.canonicalize_mode {
+            ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode {
                 CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
                 CanonicalizeMode::Response { .. } => return r,
             },
@@ -277,7 +277,6 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
                     }
                 }
             }
-            ty::ReError(_) => return r,
         };
 
         let existing_bound_var = match self.canonicalize_mode {
diff --git a/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs b/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs
new file mode 100644
index 00000000000..57f814bc81e
--- /dev/null
+++ b/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs
@@ -0,0 +1,28 @@
+//@ compile-flags: -Znext-solver
+
+trait Tr<'a> {}
+
+// Fulfillment in the new solver relies on an invariant to hold: Either
+// `has_changed` is true, or computing a goal's certainty is idempotent.
+// This isn't true for `ReError`, which we used to pass through in the
+// canonicalizer even on input mode, which can cause a goal to go from
+// ambig => pass, but we don't consider `has_changed` when the response
+// only contains region constraints (since we usually uniquify regions).
+//
+//   In this test:
+// Implicit negative coherence tries to prove `W<?0>: Constrain<'?1>`,
+// which will then match with the impl below. This constrains `'?1` to
+// `ReError`, but still bails w/ ambiguity bc we can't prove `?0: Sized`.
+// Then, when we recompute the goal `W<?0>: Constrain<'error>`, when
+// collecting ambiguities and overflows, we end up assembling a default
+// error candidate w/o ambiguity, which causes the goal to pass, and ICE.
+impl<'a, A: ?Sized> Tr<'a> for W<A> {}
+struct W<A: ?Sized>(A);
+impl<'a, A: ?Sized> Tr<'a> for A where A: Constrain<'a> {}
+//~^ ERROR conflicting implementations of trait `Tr<'_>` for type `W<_>`
+
+trait Constrain<'a> {}
+impl<A: Sized> Constrain<'missing> for W<A> {}
+//~^ ERROR use of undeclared lifetime name `'missing`
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr b/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr
new file mode 100644
index 00000000000..cf85c52fb42
--- /dev/null
+++ b/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr
@@ -0,0 +1,21 @@
+error[E0261]: use of undeclared lifetime name `'missing`
+  --> $DIR/dont-canonicalize-re-error.rs:25:26
+   |
+LL | impl<A: Sized> Constrain<'missing> for W<A> {}
+   |      -                   ^^^^^^^^ undeclared lifetime
+   |      |
+   |      help: consider introducing lifetime `'missing` here: `'missing,`
+
+error[E0119]: conflicting implementations of trait `Tr<'_>` for type `W<_>`
+  --> $DIR/dont-canonicalize-re-error.rs:21:1
+   |
+LL | impl<'a, A: ?Sized> Tr<'a> for W<A> {}
+   | ----------------------------------- first implementation here
+LL | struct W<A: ?Sized>(A);
+LL | impl<'a, A: ?Sized> Tr<'a> for A where A: Constrain<'a> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<_>`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0119, E0261.
+For more information about an error, try `rustc --explain E0119`.