about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection/src
diff options
context:
space:
mode:
authorlcnr <rust@lcnr.de>2024-03-18 12:08:06 +0100
committerlcnr <rust@lcnr.de>2024-03-18 12:08:06 +0100
commit33c274f658f512b12b6c433e8c39b8aa1e575187 (patch)
treefffb3cf500c0c7ac0c969589907186334165c8cd /compiler/rustc_trait_selection/src
parenta42873e85bcdcc83c3161309e0408f2147778523 (diff)
downloadrust-33c274f658f512b12b6c433e8c39b8aa1e575187.tar.gz
rust-33c274f658f512b12b6c433e8c39b8aa1e575187.zip
move `normalizes_to_hack` to `AliasRelate`
Diffstat (limited to 'compiler/rustc_trait_selection/src')
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs33
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs27
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/build.rs11
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs9
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs9
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs29
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs7
8 files changed, 60 insertions, 67 deletions
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index 67657c81cf6..e081a9100e2 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -21,8 +21,9 @@
 //! However, if `?fresh_var` ends up geteting equated to another type, we retry the
 //! `NormalizesTo` goal, at which point the opaque is actually defined.
 
-use super::{EvalCtxt, GoalSource};
+use super::EvalCtxt;
 use rustc_infer::traits::query::NoSolution;
+use rustc_infer::traits::solve::GoalSource;
 use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
 use rustc_middle::ty::{self, Ty};
 
@@ -121,10 +122,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             ty::TermKind::Const(_) => {
                 if let Some(alias) = term.to_alias_ty(self.tcx()) {
                     let term = self.next_term_infer_of_kind(term);
-                    self.add_goal(
-                        GoalSource::Misc,
-                        Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }),
-                    );
+                    self.add_normalizes_to_goal(Goal::new(
+                        self.tcx(),
+                        param_env,
+                        ty::NormalizesTo { alias, term },
+                    ));
                     self.try_evaluate_added_goals()?;
                     Ok(Some(self.resolve_vars_if_possible(term)))
                 } else {
@@ -145,18 +147,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             return None;
         }
 
-        let ty::Alias(_, alias) = *ty.kind() else {
+        let ty::Alias(kind, alias) = *ty.kind() else {
             return Some(ty);
         };
 
         match self.commit_if_ok(|this| {
+            let tcx = this.tcx();
             let normalized_ty = this.next_ty_infer();
-            let normalizes_to_goal = Goal::new(
-                this.tcx(),
-                param_env,
-                ty::NormalizesTo { alias, term: normalized_ty.into() },
-            );
-            this.add_goal(GoalSource::Misc, normalizes_to_goal);
+            let normalizes_to = ty::NormalizesTo { alias, term: normalized_ty.into() };
+            match kind {
+                ty::AliasKind::Opaque => {
+                    // HACK: Unlike for associated types, `normalizes-to` for opaques
+                    // is currently not treated as a function. We do not erase the
+                    // expected term.
+                    this.add_goal(GoalSource::Misc, Goal::new(tcx, param_env, normalizes_to));
+                }
+                ty::AliasKind::Projection | ty::AliasKind::Inherent | ty::AliasKind::Weak => {
+                    this.add_normalizes_to_goal(Goal::new(tcx, param_env, normalizes_to))
+                }
+            }
             this.try_evaluate_added_goals()?;
             Ok(this.resolve_vars_if_possible(normalized_ty))
         }) {
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
index 3b858cb449f..6444f12493e 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
@@ -93,7 +93,7 @@ pub struct EvalCtxt<'a, 'tcx> {
 
 #[derive(Debug, Clone)]
 pub(super) struct NestedGoals<'tcx> {
-    /// This normalizes-to goal that is treated specially during the evaluation
+    /// These normalizes-to goals are treated specially during the evaluation
     /// loop. In each iteration we take the RHS of the projection, replace it with
     /// a fresh inference variable, and only after evaluating that goal do we
     /// equate the fresh inference variable with the actual RHS of the predicate.
@@ -101,26 +101,24 @@ pub(super) struct NestedGoals<'tcx> {
     /// This is both to improve caching, and to avoid using the RHS of the
     /// projection predicate to influence the normalizes-to candidate we select.
     ///
-    /// This is not a 'real' nested goal. We must not forget to replace the RHS
-    /// with a fresh inference variable when we evaluate this goal. That can result
-    /// in a trait solver cycle. This would currently result in overflow but can be
-    /// can be unsound with more powerful coinduction in the future.
-    pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::NormalizesTo<'tcx>>>,
+    /// Forgetting to replace the RHS with a fresh inference variable when we evaluate
+    /// this goal results in an ICE..
+    pub(super) normalizes_to_goals: Vec<Goal<'tcx, ty::NormalizesTo<'tcx>>>,
     /// The rest of the goals which have not yet processed or remain ambiguous.
     pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>,
 }
 
 impl<'tcx> NestedGoals<'tcx> {
     pub(super) fn new() -> Self {
-        Self { normalizes_to_hack_goal: None, goals: Vec::new() }
+        Self { normalizes_to_goals: Vec::new(), goals: Vec::new() }
     }
 
     pub(super) fn is_empty(&self) -> bool {
-        self.normalizes_to_hack_goal.is_none() && self.goals.is_empty()
+        self.normalizes_to_goals.is_empty() && self.goals.is_empty()
     }
 
     pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) {
-        assert_eq!(other.normalizes_to_hack_goal, None);
+        self.normalizes_to_goals.extend(other.normalizes_to_goals);
         self.goals.extend(other.goals)
     }
 }
@@ -508,7 +506,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
 
         // If this loop did not result in any progress, what's our final certainty.
         let mut unchanged_certainty = Some(Certainty::Yes);
-        if let Some(goal) = goals.normalizes_to_hack_goal.take() {
+        for goal in goals.normalizes_to_goals {
             // Replace the goal with an unconstrained infer var, so the
             // RHS does not affect projection candidate assembly.
             let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
@@ -536,22 +534,21 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             // looking at the "has changed" return from evaluate_goal,
             // because we expect the `unconstrained_rhs` part of the predicate
             // to have changed -- that means we actually normalized successfully!
-            if goal.predicate.alias != self.resolve_vars_if_possible(goal.predicate.alias) {
+            let with_resolved_vars = self.resolve_vars_if_possible(goal);
+            if goal.predicate.alias != with_resolved_vars.predicate.alias {
                 unchanged_certainty = None;
             }
 
             match certainty {
                 Certainty::Yes => {}
                 Certainty::Maybe(_) => {
-                    // We need to resolve vars here so that we correctly
-                    // deal with `has_changed` in the next iteration.
-                    self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal));
+                    self.nested_goals.normalizes_to_goals.push(with_resolved_vars);
                     unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
                 }
             }
         }
 
-        for (source, goal) in goals.goals.drain(..) {
+        for (source, goal) in goals.goals {
             let (has_changed, certainty) = self.evaluate_goal(
                 GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
                 source,
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
index f7b310a7abe..02a8585d701 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/build.rs
@@ -419,6 +419,17 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
         }
     }
 
+    pub fn add_normalizes_to_goal(
+        ecx: &mut EvalCtxt<'_, 'tcx>,
+        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
+    ) {
+        if ecx.inspect.is_noop() {
+            return;
+        }
+
+        Self::add_goal(ecx, GoalSource::Misc, goal.with(ecx.tcx(), goal.predicate));
+    }
+
     pub fn add_goal(
         ecx: &mut EvalCtxt<'_, 'tcx>,
         source: GoalSource,
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 0bf28f520a4..e40ccd4cbce 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -202,12 +202,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
     #[instrument(level = "debug", skip(self))]
-    fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
-        assert!(
-            self.nested_goals.normalizes_to_hack_goal.is_none(),
-            "attempted to set the projection eq hack goal when one already exists"
-        );
-        self.nested_goals.normalizes_to_hack_goal = Some(goal);
+    fn add_normalizes_to_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
+        inspect::ProofTreeBuilder::add_normalizes_to_goal(self, goal);
+        self.nested_goals.normalizes_to_goals.push(goal);
     }
 
     #[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
index 911462f4b9a..37d56452893 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
@@ -16,7 +16,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                 .no_bound_vars()
                 .expect("const ty should not rely on other generics"),
         ) {
-            self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
+            self.instantiate_normalizes_to_term(goal, normalized_const.into());
             self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         } else {
             self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
index 52d2fe1e3ec..d60490bce44 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
@@ -16,7 +16,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     ) -> QueryResult<'tcx> {
         let tcx = self.tcx();
         let inherent = goal.predicate.alias;
-        let expected = goal.predicate.term.ty().expect("inherent consts are treated separately");
 
         let impl_def_id = tcx.parent(inherent.def_id);
         let impl_args = self.fresh_args_for_item(impl_def_id);
@@ -30,12 +29,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
         // Equate IAT with the RHS of the project goal
         let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, tcx);
-        self.eq(
-            goal.param_env,
-            expected,
-            tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args),
-        )
-        .expect("expected goal term to be fully unconstrained");
 
         // Check both where clauses on the impl and IAT
         //
@@ -51,6 +44,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                 .map(|(pred, _)| goal.with(tcx, pred)),
         );
 
+        let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args);
+        self.instantiate_normalizes_to_term(goal, normalized.into());
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
 }
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 a45c1c34410..4ef54dcf21a 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -31,32 +31,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         goal: Goal<'tcx, NormalizesTo<'tcx>>,
     ) -> QueryResult<'tcx> {
         let def_id = goal.predicate.def_id();
+        let def_kind = self.tcx().def_kind(def_id);
+        if cfg!(debug_assertions) && !matches!(def_kind, DefKind::OpaqueTy) {
+            assert!(self.term_is_fully_unconstrained(goal));
+        }
+
         match self.tcx().def_kind(def_id) {
             DefKind::AssocTy | DefKind::AssocConst => {
                 match self.tcx().associated_item(def_id).container {
                     ty::AssocItemContainer::TraitContainer => {
-                        // To only compute normalization once for each projection we only
-                        // assemble normalization candidates if the expected term is an
-                        // unconstrained inference variable.
-                        //
-                        // Why: For better cache hits, since if we have an unconstrained RHS then
-                        // there are only as many cache keys as there are (canonicalized) alias
-                        // types in each normalizes-to goal. This also weakens inference in a
-                        // forwards-compatible way so we don't use the value of the RHS term to
-                        // affect candidate assembly for projections.
-                        //
-                        // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
-                        // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
-                        // `U` and equate it with `u32`. This means that we don't need a separate
-                        // projection cache in the solver, since we're piggybacking off of regular
-                        // goal caching.
-                        if self.term_is_fully_unconstrained(goal) {
-                            let candidates = self.assemble_and_evaluate_candidates(goal);
-                            self.merge_candidates(candidates)
-                        } else {
-                            self.set_normalizes_to_hack_goal(goal);
-                            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                        }
+                        let candidates = self.assemble_and_evaluate_candidates(goal);
+                        self.merge_candidates(candidates)
                     }
                     ty::AssocItemContainer::ImplContainer => {
                         self.normalize_inherent_associated_type(goal)
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
index 9f91c02c1ab..13af5068b6c 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
@@ -15,10 +15,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     ) -> QueryResult<'tcx> {
         let tcx = self.tcx();
         let weak_ty = goal.predicate.alias;
-        let expected = goal.predicate.term.ty().expect("no such thing as a const alias");
-
-        let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
-        self.eq(goal.param_env, expected, actual)?;
 
         // Check where clauses
         self.add_goals(
@@ -30,6 +26,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                 .map(|pred| goal.with(tcx, pred)),
         );
 
+        let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
+        self.instantiate_normalizes_to_term(goal, actual.into());
+
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
 }