about summary refs log tree commit diff
path: root/compiler/rustc_next_trait_solver
diff options
context:
space:
mode:
authorlcnr <rust@lcnr.de>2025-09-11 13:08:36 +0200
committerlcnr <rust@lcnr.de>2025-09-18 12:58:38 +0200
commitf4e19c68786211f3c3cf2593442629599678800a (patch)
treee4eafc26e6dad3c7fb9bf418ab476ac64b555fab /compiler/rustc_next_trait_solver
parentd1ed52b1f5b78bf66127b670af813b84d57aeedb (diff)
downloadrust-f4e19c68786211f3c3cf2593442629599678800a.tar.gz
rust-f4e19c68786211f3c3cf2593442629599678800a.zip
support calls on opaque types :<
Diffstat (limited to 'compiler/rustc_next_trait_solver')
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs40
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs25
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs76
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs35
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/search_graph.rs2
5 files changed, 113 insertions, 65 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 fb777496e31..62dea547890 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -23,7 +23,8 @@ use crate::delegate::SolverDelegate;
 use crate::solve::inspect::ProbeKind;
 use crate::solve::{
     BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource,
-    MaybeCause, NoSolution, ParamEnvSource, QueryResult, has_no_inference_or_external_constraints,
+    MaybeCause, NoSolution, OpaqueTypesJank, ParamEnvSource, QueryResult,
+    has_no_inference_or_external_constraints,
 };
 
 enum AliasBoundKind {
@@ -474,7 +475,7 @@ where
         //
         // cc trait-system-refactor-initiative#105
         let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
-        let certainty = Certainty::Maybe(cause);
+        let certainty = Certainty::Maybe { cause, opaque_types_jank: OpaqueTypesJank::AllGood };
         self.probe_trait_candidate(source)
             .enter(|this| this.evaluate_added_goals_and_make_canonical_response(certainty))
     }
@@ -974,11 +975,21 @@ where
         candidates: &mut Vec<Candidate<I>>,
     ) {
         let self_ty = goal.predicate.self_ty();
-        // If the self type is sub unified with any opaque type, we
-        // also look at blanket impls for it.
-        let mut assemble_blanket_impls = false;
-        for alias_ty in self.opaques_with_sub_unified_hidden_type(self_ty) {
-            assemble_blanket_impls = true;
+        // We only use this hack during HIR typeck.
+        let opaque_types = match self.typing_mode() {
+            TypingMode::Analysis { .. } => self.opaques_with_sub_unified_hidden_type(self_ty),
+            TypingMode::Coherence
+            | TypingMode::Borrowck { .. }
+            | TypingMode::PostBorrowckAnalysis { .. }
+            | TypingMode::PostAnalysis => vec![],
+        };
+
+        if opaque_types.is_empty() {
+            candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity));
+            return;
+        }
+
+        for &alias_ty in &opaque_types {
             debug!("self ty is sub unified with {alias_ty:?}");
 
             struct ReplaceOpaque<I: Interner> {
@@ -1028,10 +1039,11 @@ where
             }
         }
 
-        // We also need to consider blanket impls for not-yet-defined opaque types.
+        // If the self type is sub unified with any opaque type, we also look at blanket
+        // impls for it.
         //
         // See tests/ui/impl-trait/non-defining-uses/use-blanket-impl.rs for an example.
-        if assemble_blanket_impls && assemble_from.should_assemble_impl_candidates() {
+        if assemble_from.should_assemble_impl_candidates() {
             let cx = self.cx();
             cx.for_each_blanket_impl(goal.predicate.trait_def_id(cx), |impl_def_id| {
                 // For every `default impl`, there's always a non-default `impl`
@@ -1062,7 +1074,15 @@ where
         }
 
         if candidates.is_empty() {
-            candidates.extend(self.forced_ambiguity(MaybeCause::Ambiguity));
+            let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
+            let certainty = Certainty::Maybe {
+                cause: MaybeCause::Ambiguity,
+                opaque_types_jank: OpaqueTypesJank::ErrorIfRigidSelfTy,
+            };
+            candidates
+                .extend(self.probe_trait_candidate(source).enter(|this| {
+                    this.evaluate_added_goals_and_make_canonical_response(certainty)
+                }));
         }
     }
 
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index 169832ca5fb..889588afe61 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -15,6 +15,7 @@ use rustc_index::IndexVec;
 use rustc_type_ir::data_structures::HashSet;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::relate::solver_relating::RelateExt;
+use rustc_type_ir::solve::OpaqueTypesJank;
 use rustc_type_ir::{
     self as ty, Canonical, CanonicalVarKind, CanonicalVarValues, InferCtxtLike, Interner,
     TypeFoldable,
@@ -141,8 +142,10 @@ where
                 }
             };
 
-        if let Certainty::Maybe(cause @ MaybeCause::Overflow { keep_constraints: false, .. }) =
-            certainty
+        if let Certainty::Maybe {
+            cause: cause @ MaybeCause::Overflow { keep_constraints: false, .. },
+            opaque_types_jank,
+        } = certainty
         {
             // If we have overflow, it's probable that we're substituting a type
             // into itself infinitely and any partial substitutions in the query
@@ -155,7 +158,7 @@ where
             //
             // Changing this to retain some constraints in the future
             // won't be a breaking change, so this is good enough for now.
-            return Ok(self.make_ambiguous_response_no_constraints(cause));
+            return Ok(self.make_ambiguous_response_no_constraints(cause, opaque_types_jank));
         }
 
         let external_constraints =
@@ -199,10 +202,13 @@ where
                     .count();
                 if num_non_region_vars > self.cx().recursion_limit() {
                     debug!(?num_non_region_vars, "too many inference variables -> overflow");
-                    return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow {
-                        suggest_increasing_limit: true,
-                        keep_constraints: false,
-                    }));
+                    return Ok(self.make_ambiguous_response_no_constraints(
+                        MaybeCause::Overflow {
+                            suggest_increasing_limit: true,
+                            keep_constraints: false,
+                        },
+                        OpaqueTypesJank::AllGood,
+                    ));
                 }
             }
         }
@@ -216,13 +222,14 @@ where
     /// ambiguity but return constrained variables to guide inference.
     pub(in crate::solve) fn make_ambiguous_response_no_constraints(
         &self,
-        maybe_cause: MaybeCause,
+        cause: MaybeCause,
+        opaque_types_jank: OpaqueTypesJank,
     ) -> CanonicalResponse<I> {
         response_no_constraints_raw(
             self.cx(),
             self.max_input_universe,
             self.variables,
-            Certainty::Maybe(maybe_cause),
+            Certainty::Maybe { cause, opaque_types_jank },
         )
     }
 
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 3e3a5246f3d..5df7c92d881 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
@@ -8,6 +8,7 @@ use rustc_type_ir::inherent::*;
 use rustc_type_ir::relate::Relate;
 use rustc_type_ir::relate::solver_relating::RelateExt;
 use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind};
+use rustc_type_ir::solve::OpaqueTypesJank;
 use rustc_type_ir::{
     self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder,
     TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
@@ -151,6 +152,15 @@ pub trait SolverDelegateEvalExt: SolverDelegate {
         stalled_on: Option<GoalStalledOn<Self::Interner>>,
     ) -> Result<GoalEvaluation<Self::Interner>, NoSolution>;
 
+    /// Checks whether evaluating `goal` may hold while treating not-yet-defined
+    /// opaque types as being kind of rigid.
+    ///
+    /// See the comment on [OpaqueTypesJank] for more details.
+    fn root_goal_may_hold_opaque_types_jank(
+        &self,
+        goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
+    ) -> bool;
+
     /// Check whether evaluating `goal` with a depth of `root_depth` may
     /// succeed. This only returns `false` if the goal is guaranteed to
     /// not hold. In case evaluation overflows and fails with ambiguity this
@@ -193,6 +203,24 @@ where
         })
     }
 
+    fn root_goal_may_hold_opaque_types_jank(
+        &self,
+        goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
+    ) -> bool {
+        self.probe(|| {
+            EvalCtxt::enter_root(self, self.cx().recursion_limit(), I::Span::dummy(), |ecx| {
+                ecx.evaluate_goal(GoalSource::Misc, goal, None)
+            })
+            .is_ok_and(|r| match r.certainty {
+                Certainty::Yes => true,
+                Certainty::Maybe { cause: _, opaque_types_jank } => match opaque_types_jank {
+                    OpaqueTypesJank::AllGood => true,
+                    OpaqueTypesJank::ErrorIfRigidSelfTy => false,
+                },
+            })
+        })
+    }
+
     fn root_goal_may_hold_with_depth(
         &self,
         root_depth: usize,
@@ -407,8 +435,12 @@ where
         // If we have run this goal before, and it was stalled, check that any of the goal's
         // args have changed. Otherwise, we don't need to re-run the goal because it'll remain
         // stalled, since it'll canonicalize the same way and evaluation is pure.
-        if let Some(GoalStalledOn { num_opaques, ref stalled_vars, ref sub_roots, stalled_cause }) =
-            stalled_on
+        if let Some(GoalStalledOn {
+            num_opaques,
+            ref stalled_vars,
+            ref sub_roots,
+            stalled_certainty,
+        }) = stalled_on
             && !stalled_vars.iter().any(|value| self.delegate.is_changed_arg(*value))
             && !sub_roots
                 .iter()
@@ -419,7 +451,7 @@ where
                 NestedNormalizationGoals::empty(),
                 GoalEvaluation {
                     goal,
-                    certainty: Certainty::Maybe(stalled_cause),
+                    certainty: stalled_certainty,
                     has_changed: HasChanged::No,
                     stalled_on,
                 },
@@ -468,7 +500,7 @@ where
 
         let stalled_on = match certainty {
             Certainty::Yes => None,
-            Certainty::Maybe(stalled_cause) => match has_changed {
+            Certainty::Maybe { .. } => match has_changed {
                 // FIXME: We could recompute a *new* set of stalled variables by walking
                 // through the orig values, resolving, and computing the root vars of anything
                 // that is not resolved. Only when *these* have changed is it meaningful
@@ -518,7 +550,7 @@ where
                             .len(),
                         stalled_vars,
                         sub_roots,
-                        stalled_cause,
+                        stalled_certainty: certainty,
                     })
                 }
             },
@@ -634,7 +666,7 @@ where
             if let Some(certainty) = self.delegate.compute_goal_fast_path(goal, self.origin_span) {
                 match certainty {
                     Certainty::Yes => {}
-                    Certainty::Maybe(_) => {
+                    Certainty::Maybe { .. } => {
                         self.nested_goals.push((source, goal, None));
                         unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
                     }
@@ -710,7 +742,7 @@ where
 
                 match certainty {
                     Certainty::Yes => {}
-                    Certainty::Maybe(_) => {
+                    Certainty::Maybe { .. } => {
                         self.nested_goals.push((source, with_resolved_vars, stalled_on));
                         unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
                     }
@@ -724,7 +756,7 @@ where
 
                 match certainty {
                     Certainty::Yes => {}
-                    Certainty::Maybe(_) => {
+                    Certainty::Maybe { .. } => {
                         self.nested_goals.push((source, goal, stalled_on));
                         unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
                     }
@@ -1184,28 +1216,12 @@ where
     pub(crate) fn opaques_with_sub_unified_hidden_type(
         &self,
         self_ty: I::Ty,
-    ) -> impl Iterator<Item = ty::AliasTy<I>> + use<'a, D, I> {
-        let delegate = self.delegate;
-        delegate
-            .clone_opaque_types_lookup_table()
-            .into_iter()
-            .chain(delegate.clone_duplicate_opaque_types())
-            .filter_map(move |(key, hidden_ty)| {
-                if let ty::Infer(ty::TyVar(self_vid)) = self_ty.kind() {
-                    if let ty::Infer(ty::TyVar(hidden_vid)) = hidden_ty.kind() {
-                        if delegate.sub_unification_table_root_var(self_vid)
-                            == delegate.sub_unification_table_root_var(hidden_vid)
-                        {
-                            return Some(ty::AliasTy::new_from_args(
-                                delegate.cx(),
-                                key.def_id.into(),
-                                key.args,
-                            ));
-                        }
-                    }
-                }
-                None
-            })
+    ) -> Vec<ty::AliasTy<I>> {
+        if let ty::Infer(ty::TyVar(vid)) = self_ty.kind() {
+            self.delegate.opaques_with_sub_unified_hidden_type(vid)
+        } else {
+            vec![]
+        }
     }
 }
 
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index cd27c9c26c1..fb900b592d1 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -158,9 +158,10 @@ where
         if self.may_use_unstable_feature(param_env, symbol) {
             self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         } else {
-            self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe(
-                MaybeCause::Ambiguity,
-            ))
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe {
+                cause: MaybeCause::Ambiguity,
+                opaque_types_jank: OpaqueTypesJank::AllGood,
+            })
         }
     }
 
@@ -278,18 +279,21 @@ where
 
     fn bail_with_ambiguity(&mut self, candidates: &[Candidate<I>]) -> CanonicalResponse<I> {
         debug_assert!(candidates.len() > 1);
-        let maybe_cause =
-            candidates.iter().fold(MaybeCause::Ambiguity, |maybe_cause, candidates| {
-                // Pull down the certainty of `Certainty::Yes` to ambiguity when combining
+        let (cause, opaque_types_jank) = candidates.iter().fold(
+            (MaybeCause::Ambiguity, OpaqueTypesJank::AllGood),
+            |(c, jank), candidates| {
+                // We pull down the certainty of `Certainty::Yes` to ambiguity when combining
                 // these responses, b/c we're combining more than one response and this we
                 // don't know which one applies.
-                let candidate = match candidates.result.value.certainty {
-                    Certainty::Yes => MaybeCause::Ambiguity,
-                    Certainty::Maybe(candidate) => candidate,
-                };
-                maybe_cause.or(candidate)
-            });
-        self.make_ambiguous_response_no_constraints(maybe_cause)
+                match candidates.result.value.certainty {
+                    Certainty::Yes => (c, jank),
+                    Certainty::Maybe { cause, opaque_types_jank } => {
+                        (c.or(cause), jank.or(opaque_types_jank))
+                    }
+                }
+            },
+        );
+        self.make_ambiguous_response_no_constraints(cause, opaque_types_jank)
     }
 
     /// If we fail to merge responses we flounder and return overflow or ambiguity.
@@ -427,6 +431,7 @@ pub struct GoalStalledOn<I: Interner> {
     pub num_opaques: usize,
     pub stalled_vars: Vec<I::GenericArg>,
     pub sub_roots: Vec<TyVid>,
-    /// The cause that will be returned on subsequent evaluations if this goal remains stalled.
-    pub stalled_cause: MaybeCause,
+    /// The certainty that will be returned on subsequent evaluations if this
+    /// goal remains stalled.
+    pub stalled_certainty: Certainty,
 }
diff --git a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
index f0342e0523f..289325d7055 100644
--- a/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
@@ -93,7 +93,7 @@ where
     fn is_ambiguous_result(result: QueryResult<I>) -> bool {
         result.is_ok_and(|response| {
             has_no_inference_or_external_constraints(response)
-                && matches!(response.value.certainty, Certainty::Maybe(_))
+                && matches!(response.value.certainty, Certainty::Maybe { .. })
         })
     }