From 0700ec078f1bd7d29ffecd842cb7ca51d23207c3 Mon Sep 17 00:00:00 2001
From: lcnr <rust@lcnr.de>
Date: Fri, 1 Mar 2024 15:12:20 +0100
Subject: [PATCH] normalizes-to: handle negative impls

---
 .../src/solve/normalizes_to/mod.rs            | 19 +++++++++++++---
 .../negative-impl-normalizes-to.rs            | 22 +++++++++++++++++++
 2 files changed, 38 insertions(+), 3 deletions(-)
 create mode 100644 tests/ui/traits/negative-impls/negative-impl-normalizes-to.rs

diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
index 3aba5c85abc3a..248985715c267 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -162,15 +162,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         let tcx = ecx.tcx();
 
         let goal_trait_ref = goal.predicate.alias.trait_ref(tcx);
-        let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+        let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
         let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
-        if !drcx.args_may_unify(goal_trait_ref.args, impl_trait_ref.skip_binder().args) {
+        if !drcx.args_may_unify(
+            goal.predicate.trait_ref(tcx).args,
+            impl_trait_header.skip_binder().trait_ref.args,
+        ) {
             return Err(NoSolution);
         }
 
+        // We have to ignore negative impls when projecting.
+        let impl_polarity = impl_trait_header.skip_binder().polarity;
+        match impl_polarity {
+            ty::ImplPolarity::Negative => return Err(NoSolution),
+            ty::ImplPolarity::Reservation => {
+                unimplemented!("reservation impl for trait with assoc item: {:?}", goal)
+            }
+            ty::ImplPolarity::Positive => {}
+        };
+
         ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
             let impl_args = ecx.fresh_args_for_item(impl_def_id);
-            let impl_trait_ref = impl_trait_ref.instantiate(tcx, impl_args);
+            let impl_trait_ref = impl_trait_header.instantiate(tcx, impl_args).trait_ref;
 
             ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
 
diff --git a/tests/ui/traits/negative-impls/negative-impl-normalizes-to.rs b/tests/ui/traits/negative-impls/negative-impl-normalizes-to.rs
new file mode 100644
index 0000000000000..998b0d0c458a4
--- /dev/null
+++ b/tests/ui/traits/negative-impls/negative-impl-normalizes-to.rs
@@ -0,0 +1,22 @@
+//@ revisions: current next
+//@[next] compile-flags: -Znext-solver
+//@ check-pass
+
+// Check that negative impls for traits with associated types
+// do not result in an ICE when trying to normalize.
+#![feature(negative_impls)]
+trait Trait {
+    type Assoc;
+}
+
+struct Local<T>(T);
+impl !Trait for Local<u32> {}
+impl Trait for Local<i32> {
+    type Assoc = i32;
+}
+
+trait NoOverlap {}
+impl<T: Trait<Assoc = u32>> NoOverlap for T {}
+impl<T> NoOverlap for Local<T> {}
+
+fn main() {}