about summary refs log tree commit diff
path: root/compiler/rustc_next_trait_solver
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_next_trait_solver')
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs73
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs10
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs11
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs27
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs4
5 files changed, 111 insertions, 14 deletions
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index ecb57cc0ad7..381a732b8de 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -10,6 +10,7 @@ use rustc_type_ir::{
 };
 use tracing::{debug, instrument};
 
+use super::has_only_region_constraints;
 use super::trait_goals::TraitGoalProvenVia;
 use crate::delegate::SolverDelegate;
 use crate::solve::inspect::ProbeKind;
@@ -771,6 +772,69 @@ where
             }
         })
     }
+}
+
+pub(super) enum AllowInferenceConstraints {
+    Yes,
+    No,
+}
+
+impl<D, I> EvalCtxt<'_, D>
+where
+    D: SolverDelegate<Interner = I>,
+    I: Interner,
+{
+    /// Check whether we can ignore impl candidates due to specialization.
+    ///
+    /// This is only necessary for `feature(specialization)` and seems quite ugly.
+    pub(super) fn filter_specialized_impls(
+        &mut self,
+        allow_inference_constraints: AllowInferenceConstraints,
+        candidates: &mut Vec<Candidate<I>>,
+    ) {
+        match self.typing_mode() {
+            TypingMode::Coherence => return,
+            TypingMode::Analysis { .. }
+            | TypingMode::Borrowck { .. }
+            | TypingMode::PostBorrowckAnalysis { .. }
+            | TypingMode::PostAnalysis => {}
+        }
+
+        let mut i = 0;
+        'outer: while i < candidates.len() {
+            let CandidateSource::Impl(victim_def_id) = candidates[i].source else {
+                i += 1;
+                continue;
+            };
+
+            for (j, c) in candidates.iter().enumerate() {
+                if i == j {
+                    continue;
+                }
+
+                let CandidateSource::Impl(other_def_id) = c.source else {
+                    continue;
+                };
+
+                // See if we can toss out `victim` based on specialization.
+                //
+                // While this requires us to know *for sure* that the `lhs` impl applies
+                // we still use modulo regions here. This is fine as specialization currently
+                // assumes that specializing impls have to be always applicable, meaning that
+                // the only allowed region constraints may be constraints also present on the default impl.
+                if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes)
+                    || has_only_region_constraints(c.result)
+                {
+                    if self.cx().impl_specializes(other_def_id, victim_def_id) {
+                        candidates.remove(i);
+                        continue 'outer;
+                    }
+                }
+            }
+
+            i += 1;
+        }
+    }
 
     /// Assemble and merge candidates for goals which are related to an underlying trait
     /// goal. Right now, this is normalizes-to and host effect goals.
@@ -857,7 +921,7 @@ where
                 }
             }
             TraitGoalProvenVia::Misc => {
-                let candidates =
+                let mut candidates =
                     self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
 
                 // Prefer "orphaned" param-env normalization predicates, which are used
@@ -871,6 +935,13 @@ where
                     return Ok(response);
                 }
 
+                // We drop specialized impls to allow normalization via a final impl here. In case
+                // the specializing impl has different inference constraints from the specialized
+                // impl, proving the trait goal is already ambiguous, so we never get here. This
+                // means we can just ignore inference constraints and don't have to special-case
+                // constraining the normalized-to `term`.
+                self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates);
+
                 let responses: Vec<_> = candidates.iter().map(|c| c.result).collect();
                 if let Some(response) = self.try_merge_responses(&responses) {
                     Ok(response)
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 27ca8787db5..19fb2b00fcb 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -16,6 +16,7 @@ use rustc_type_ir::{
 };
 use tracing::{instrument, trace};
 
+use super::has_only_region_constraints;
 use crate::coherence;
 use crate::delegate::SolverDelegate;
 use crate::solve::inspect::{self, ProofTreeBuilder};
@@ -476,13 +477,8 @@ where
             Ok(response) => response,
         };
 
-        let has_changed = if !response.value.var_values.is_identity_modulo_regions()
-            || !response.value.external_constraints.opaque_types.is_empty()
-        {
-            HasChanged::Yes
-        } else {
-            HasChanged::No
-        };
+        let has_changed =
+            if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No };
 
         let (normalization_nested_goals, certainty) =
             self.instantiate_and_apply_query_response(goal.param_env, orig_values, response);
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index 0695c5acdca..d9cf84787a0 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -70,6 +70,17 @@ fn has_no_inference_or_external_constraints<I: Interner>(
         && normalization_nested_goals.is_empty()
 }
 
+fn has_only_region_constraints<I: Interner>(response: ty::Canonical<I, Response<I>>) -> bool {
+    let ExternalConstraintsData {
+        region_constraints: _,
+        ref opaque_types,
+        ref normalization_nested_goals,
+    } = *response.value.external_constraints;
+    response.value.var_values.is_identity_modulo_regions()
+        && opaque_types.is_empty()
+        && normalization_nested_goals.is_empty()
+}
+
 impl<'a, D, I> EvalCtxt<'a, D>
 where
     D: SolverDelegate<Interner = I>,
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index 9466901683e..119d197de13 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -213,9 +213,6 @@ where
                 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             };
 
-            // In case the associated item is hidden due to specialization, we have to
-            // return ambiguity this would otherwise be incomplete, resulting in
-            // unsoundness during coherence (#105782).
             let target_item_def_id = match ecx.fetch_eligible_assoc_item(
                 goal_trait_ref,
                 goal.predicate.def_id(),
@@ -223,8 +220,28 @@ where
             ) {
                 Ok(Some(target_item_def_id)) => target_item_def_id,
                 Ok(None) => {
-                    return ecx
-                        .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+                    match ecx.typing_mode() {
+                        // In case the associated item is hidden due to specialization, we have to
+                        // return ambiguity this would otherwise be incomplete, resulting in
+                        // unsoundness during coherence (#105782).
+                        ty::TypingMode::Coherence => {
+                            return ecx.evaluate_added_goals_and_make_canonical_response(
+                                Certainty::AMBIGUOUS,
+                            );
+                        }
+                        // Outside of coherence, we treat the associated item as rigid instead.
+                        ty::TypingMode::Analysis { .. }
+                        | ty::TypingMode::Borrowck { .. }
+                        | ty::TypingMode::PostBorrowckAnalysis { .. }
+                        | ty::TypingMode::PostAnalysis => {
+                            ecx.structurally_instantiate_normalizes_to_term(
+                                goal,
+                                goal.predicate.alias,
+                            );
+                            return ecx
+                                .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+                        }
+                    };
                 }
                 Err(guar) => return error_response(ecx, guar),
             };
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 3404c47bba2..24657496df6 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -13,7 +13,7 @@ use tracing::{instrument, trace};
 
 use crate::delegate::SolverDelegate;
 use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
-use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate};
+use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate};
 use crate::solve::inspect::ProbeKind;
 use crate::solve::{
     BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
@@ -1338,6 +1338,8 @@ where
             };
         }
 
+        self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
+
         // If there are *only* global where bounds, then make sure to return that this
         // is still reported as being proven-via the param-env so that rigid projections
         // operate correctly. Otherwise, drop all global where-bounds before merging the