about summary refs log tree commit diff
path: root/compiler/rustc_next_trait_solver/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_next_trait_solver/src')
-rw-r--r--compiler/rustc_next_trait_solver/src/canonicalizer.rs35
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/effect_goals.rs18
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs30
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs174
5 files changed, 68 insertions, 191 deletions
diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
index 3e1f48610ff..da05c49756f 100644
--- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
@@ -25,12 +25,8 @@ enum CanonicalizeInputKind {
     /// trait candidates relies on it when deciding whether a where-bound
     /// is trivial.
     ParamEnv,
-    /// When canonicalizing predicates, we don't keep `'static`. If we're
-    /// currently outside of the trait solver and canonicalize the root goal
-    /// during HIR typeck, we replace each occurrence of a region with a
-    /// unique region variable. See the comment on `InferCtxt::in_hir_typeck`
-    /// for more details.
-    Predicate { is_hir_typeck_root_goal: bool },
+    /// When canonicalizing predicates, we don't keep `'static`.
+    Predicate,
 }
 
 /// Whether we're canonicalizing a query input or the query response.
@@ -191,7 +187,6 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
     pub fn canonicalize_input<P: TypeFoldable<I>>(
         delegate: &'a D,
         variables: &'a mut Vec<I::GenericArg>,
-        is_hir_typeck_root_goal: bool,
         input: QueryInput<I, P>,
     ) -> ty::Canonical<I, QueryInput<I, P>> {
         // First canonicalize the `param_env` while keeping `'static`
@@ -201,9 +196,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
         // while *mostly* reusing the canonicalizer from above.
         let mut rest_canonicalizer = Canonicalizer {
             delegate,
-            canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate {
-                is_hir_typeck_root_goal,
-            }),
+            canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate),
 
             variables,
             variable_lookup_table,
@@ -481,31 +474,13 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
             }
         };
 
-        let var = if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate {
-            is_hir_typeck_root_goal: true,
-        }) = self.canonicalize_mode
-        {
-            let var = ty::BoundVar::from(self.variables.len());
-            self.variables.push(r.into());
-            self.var_kinds.push(kind);
-            var
-        } else {
-            self.get_or_insert_bound_var(r, kind)
-        };
+        let var = self.get_or_insert_bound_var(r, kind);
 
         Region::new_anon_bound(self.cx(), self.binder_index, var)
     }
 
     fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
-        if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate {
-            is_hir_typeck_root_goal: true,
-        }) = self.canonicalize_mode
-        {
-            // If we're canonicalizing a root goal during HIR typeck, we
-            // must not use the `cache` as we want to map each occurrence
-            // of a region to a unique existential variable.
-            self.inner_fold_ty(t)
-        } else if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
+        if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
             ty
         } else {
             let res = self.inner_fold_ty(t);
diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
index 9a22bf58c03..b7ae9994c62 100644
--- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
@@ -6,7 +6,7 @@ use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
 use rustc_type_ir::solve::SizedTraitKind;
 use rustc_type_ir::solve::inspect::ProbeKind;
-use rustc_type_ir::{self as ty, Interner, elaborate};
+use rustc_type_ir::{self as ty, Interner, TypingMode, elaborate};
 use tracing::instrument;
 
 use super::assembly::{Candidate, structural_traits};
@@ -135,12 +135,16 @@ where
         }
 
         let impl_polarity = cx.impl_polarity(impl_def_id);
-        match impl_polarity {
+        let certainty = match impl_polarity {
             ty::ImplPolarity::Negative => return Err(NoSolution),
-            ty::ImplPolarity::Reservation => {
-                unimplemented!("reservation impl for const trait: {:?}", goal)
-            }
-            ty::ImplPolarity::Positive => {}
+            ty::ImplPolarity::Reservation => match ecx.typing_mode() {
+                TypingMode::Coherence => Certainty::AMBIGUOUS,
+                TypingMode::Analysis { .. }
+                | TypingMode::Borrowck { .. }
+                | TypingMode::PostBorrowckAnalysis { .. }
+                | TypingMode::PostAnalysis => return Err(NoSolution),
+            },
+            ty::ImplPolarity::Positive => Certainty::Yes,
         };
 
         if !cx.impl_is_const(impl_def_id) {
@@ -171,7 +175,7 @@ where
                 });
             ecx.add_goals(GoalSource::ImplWhereBound, const_conditions);
 
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+            ecx.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 74c5b49ea92..4644b145b18 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
@@ -57,7 +57,6 @@ where
     /// This expects `goal` and `opaque_types` to be eager resolved.
     pub(super) fn canonicalize_goal(
         &self,
-        is_hir_typeck_root_goal: bool,
         goal: Goal<I, I::Predicate>,
         opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>,
     ) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) {
@@ -65,7 +64,6 @@ where
         let canonical = Canonicalizer::canonicalize_input(
             self.delegate,
             &mut orig_values,
-            is_hir_typeck_root_goal,
             QueryInput {
                 goal,
                 predefined_opaques_in_body: self
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 a4738306181..4f87902e46e 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
@@ -4,7 +4,6 @@ use std::ops::ControlFlow;
 #[cfg(feature = "nightly")]
 use rustc_macros::HashStable_NoContext;
 use rustc_type_ir::data_structures::{HashMap, HashSet};
-use rustc_type_ir::fast_reject::DeepRejectCtxt;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::relate::Relate;
 use rustc_type_ir::relate::solver_relating::RelateExt;
@@ -459,10 +458,7 @@ where
         let opaque_types = self.delegate.clone_opaque_types_lookup_table();
         let (goal, opaque_types) = eager_resolve_vars(self.delegate, (goal, opaque_types));
 
-        let is_hir_typeck_root_goal = matches!(goal_evaluation_kind, GoalEvaluationKind::Root)
-            && self.delegate.in_hir_typeck();
-        let (orig_values, canonical_goal) =
-            self.canonicalize_goal(is_hir_typeck_root_goal, goal, opaque_types);
+        let (orig_values, canonical_goal) = self.canonicalize_goal(goal, opaque_types);
         let mut goal_evaluation =
             self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
         let canonical_result = self.search_graph.evaluate_goal(
@@ -1131,6 +1127,7 @@ where
         self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id)
     }
 
+    #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn register_hidden_type_in_storage(
         &mut self,
         opaque_type_key: ty::OpaqueTypeKey<I>,
@@ -1157,29 +1154,6 @@ where
         self.add_goals(GoalSource::AliasWellFormed, goals);
     }
 
-    // Do something for each opaque/hidden pair defined with `def_id` in the
-    // current inference context.
-    pub(super) fn probe_existing_opaque_ty(
-        &mut self,
-        key: ty::OpaqueTypeKey<I>,
-    ) -> Option<(ty::OpaqueTypeKey<I>, I::Ty)> {
-        // We shouldn't have any duplicate entries when using
-        // this function during `TypingMode::Analysis`.
-        let duplicate_entries = self.delegate.clone_duplicate_opaque_types();
-        assert!(duplicate_entries.is_empty(), "unexpected duplicates: {duplicate_entries:?}");
-        let mut matching = self.delegate.clone_opaque_types_lookup_table().into_iter().filter(
-            |(candidate_key, _)| {
-                candidate_key.def_id == key.def_id
-                    && DeepRejectCtxt::relate_rigid_rigid(self.cx())
-                        .args_may_unify(candidate_key.args, key.args)
-            },
-        );
-        let first = matching.next();
-        let second = matching.next();
-        assert_eq!(second, None);
-        first
-    }
-
     // Try to evaluate a const, or return `None` if the const is too generic.
     // This doesn't mean the const isn't evaluatable, though, and should be treated
     // as an ambiguity rather than no-solution.
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
index df3ad1e468b..a5f857a1dd8 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
@@ -1,13 +1,12 @@
 //! Computes a normalizes-to (projection) goal for opaque types. This goal
 //! behaves differently depending on the current `TypingMode`.
 
-use rustc_index::bit_set::GrowableBitSet;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::solve::GoalSource;
 use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions};
 
 use crate::delegate::SolverDelegate;
-use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, inspect};
+use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
 
 impl<D, I> EvalCtxt<'_, D>
 where
@@ -39,100 +38,68 @@ where
                 self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous));
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             }
-            TypingMode::Analysis { defining_opaque_types_and_generators } => {
+            TypingMode::Analysis {
+                defining_opaque_types_and_generators: defining_opaque_types,
+            }
+            | TypingMode::Borrowck { defining_opaque_types } => {
                 let Some(def_id) = opaque_ty
                     .def_id
                     .as_local()
-                    .filter(|&def_id| defining_opaque_types_and_generators.contains(&def_id))
+                    .filter(|&def_id| defining_opaque_types.contains(&def_id))
                 else {
+                    // If we're not in the defining scope, treat the alias as rigid.
                     self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
                     return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
                 };
 
-                // FIXME: This may have issues when the args contain aliases...
-                match uses_unique_placeholders_ignoring_regions(self.cx(), opaque_ty.args) {
-                    Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
-                        return self.evaluate_added_goals_and_make_canonical_response(
-                            Certainty::AMBIGUOUS,
-                        );
-                    }
-                    Err(_) => {
-                        return Err(NoSolution);
-                    }
-                    Ok(()) => {}
-                }
-                // Prefer opaques registered already.
-                let opaque_type_key = ty::OpaqueTypeKey { def_id, args: opaque_ty.args };
-                // FIXME: This also unifies the previous hidden type with the expected.
+                // We structurally normalize the args so that we're able to detect defining uses
+                // later on.
                 //
-                // If that fails, we insert `expected` as a new hidden type instead of
-                // eagerly emitting an error.
-                let existing = self.probe_existing_opaque_ty(opaque_type_key);
-                if let Some((candidate_key, candidate_ty)) = existing {
-                    return self
-                        .probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup {
-                            result: *result,
-                        })
-                        .enter(|ecx| {
-                            for (a, b) in std::iter::zip(
-                                candidate_key.args.iter(),
-                                opaque_type_key.args.iter(),
-                            ) {
-                                ecx.eq(goal.param_env, a, b)?;
-                            }
-                            ecx.eq(goal.param_env, candidate_ty, expected)?;
-                            ecx.add_item_bounds_for_hidden_type(
-                                def_id.into(),
-                                candidate_key.args,
-                                goal.param_env,
-                                candidate_ty,
-                            );
-                            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                        });
-                }
+                // This reduces the amount of duplicate definitions in the `opaque_type_storage` and
+                // strengthens inference. This causes us to subtly depend on the normalization behavior
+                // when inferring the hidden type of opaques.
+                //
+                // E.g. it's observable that we don't normalize nested aliases with bound vars in
+                // `structurally_normalize` and because we use structural lookup, we also don't
+                // reuse an entry for `Tait<for<'a> fn(&'a ())>` for `Tait<for<'b> fn(&'b ())>`.
+                let normalized_args =
+                    cx.mk_args_from_iter(opaque_ty.args.iter().map(|arg| match arg.kind() {
+                        ty::GenericArgKind::Lifetime(lt) => Ok(lt.into()),
+                        ty::GenericArgKind::Type(ty) => {
+                            self.structurally_normalize_ty(goal.param_env, ty).map(Into::into)
+                        }
+                        ty::GenericArgKind::Const(ct) => {
+                            self.structurally_normalize_const(goal.param_env, ct).map(Into::into)
+                        }
+                    }))?;
 
-                // Otherwise, define a new opaque type
-                let prev = self.register_hidden_type_in_storage(opaque_type_key, expected);
-                assert_eq!(prev, None);
-                self.add_item_bounds_for_hidden_type(
-                    def_id.into(),
-                    opaque_ty.args,
-                    goal.param_env,
-                    expected,
-                );
-                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-            }
-            // Very similar to `TypingMode::Analysis` with some notably differences:
-            // - we accept opaque types even if they have non-universal arguments
-            // - we do a structural lookup instead of semantically unifying regions
-            // - the hidden type starts out as the type from HIR typeck with fresh region
-            //   variables instead of a fully unconstrained inference variable
-            TypingMode::Borrowck { defining_opaque_types } => {
-                let Some(def_id) = opaque_ty
-                    .def_id
-                    .as_local()
-                    .filter(|&def_id| defining_opaque_types.contains(&def_id))
-                else {
-                    self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
-                    return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
-                };
+                let opaque_type_key = ty::OpaqueTypeKey { def_id, args: normalized_args };
+                if let Some(prev) = self.register_hidden_type_in_storage(opaque_type_key, expected)
+                {
+                    self.eq(goal.param_env, expected, prev)?;
+                } else {
+                    // During HIR typeck, opaque types start out as unconstrained
+                    // inference variables. In borrowck we instead use the type
+                    // computed in HIR typeck as the initial value.
+                    match self.typing_mode() {
+                        TypingMode::Analysis { .. } => {}
+                        TypingMode::Borrowck { .. } => {
+                            let actual = cx
+                                .type_of_opaque_hir_typeck(def_id)
+                                .instantiate(cx, opaque_ty.args);
+                            let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() {
+                                ty::ReErased => self.next_region_var(),
+                                _ => re,
+                            });
+                            self.eq(goal.param_env, expected, actual)?;
+                        }
+                        _ => unreachable!(),
+                    }
+                }
 
-                let opaque_type_key = ty::OpaqueTypeKey { def_id, args: opaque_ty.args };
-                let actual = self
-                    .register_hidden_type_in_storage(opaque_type_key, expected)
-                    .unwrap_or_else(|| {
-                        let actual =
-                            cx.type_of_opaque_hir_typeck(def_id).instantiate(cx, opaque_ty.args);
-                        let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() {
-                            ty::ReErased => self.next_region_var(),
-                            _ => re,
-                        });
-                        actual
-                    });
-                self.eq(goal.param_env, expected, actual)?;
                 self.add_item_bounds_for_hidden_type(
                     def_id.into(),
-                    opaque_ty.args,
+                    normalized_args,
                     goal.param_env,
                     expected,
                 );
@@ -168,44 +135,3 @@ where
         }
     }
 }
-
-/// Checks whether each generic argument is simply a unique generic placeholder.
-///
-/// FIXME: Interner argument is needed to constrain the `I` parameter.
-fn uses_unique_placeholders_ignoring_regions<I: Interner>(
-    _cx: I,
-    args: I::GenericArgs,
-) -> Result<(), NotUniqueParam<I>> {
-    let mut seen = GrowableBitSet::default();
-    for arg in args.iter() {
-        match arg.kind() {
-            // Ignore regions, since we can't resolve those in a canonicalized
-            // query in the trait solver.
-            ty::GenericArgKind::Lifetime(_) => {}
-            ty::GenericArgKind::Type(t) => match t.kind() {
-                ty::Placeholder(p) => {
-                    if !seen.insert(p.var()) {
-                        return Err(NotUniqueParam::DuplicateParam(t.into()));
-                    }
-                }
-                _ => return Err(NotUniqueParam::NotParam(t.into())),
-            },
-            ty::GenericArgKind::Const(c) => match c.kind() {
-                ty::ConstKind::Placeholder(p) => {
-                    if !seen.insert(p.var()) {
-                        return Err(NotUniqueParam::DuplicateParam(c.into()));
-                    }
-                }
-                _ => return Err(NotUniqueParam::NotParam(c.into())),
-            },
-        }
-    }
-
-    Ok(())
-}
-
-// FIXME: This should check for dupes and non-params first, then infer vars.
-enum NotUniqueParam<I: Interner> {
-    DuplicateParam(I::GenericArg),
-    NotParam(I::GenericArg),
-}