Skip to content

Commit 12cdb43

Browse files
committed
-Znext-solver Remove special handling of NormalizesTo goal
1 parent 3a933e5 commit 12cdb43

19 files changed

Lines changed: 229 additions & 298 deletions

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

Lines changed: 27 additions & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::mem;
2-
use std::ops::ControlFlow;
32

43
#[cfg(feature = "nightly")]
54
use rustc_macros::HashStable_NoContext;
@@ -11,8 +10,7 @@ use rustc_type_ir::search_graph::{CandidateHeadUsages, PathKind};
1110
use rustc_type_ir::solve::OpaqueTypesJank;
1211
use rustc_type_ir::{
1312
self as ty, CanonicalVarValues, InferCtxtLike, Interner, TypeFoldable, TypeFolder,
14-
TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
15-
TypingMode,
13+
TypeSuperFoldable, TypeVisitableExt, TypingMode,
1614
};
1715
use tracing::{debug, instrument, trace};
1816

@@ -47,13 +45,13 @@ enum CurrentGoalKind {
4745
/// These are currently the only goals whose impl where-clauses are considered to be
4846
/// productive steps.
4947
CoinductiveTrait,
50-
/// Unlike other goals, `NormalizesTo` goals act like functions with the expected term
51-
/// always being fully unconstrained. This would weaken inference however, as the nested
48+
/// When probing and selecting `NormalizesTo` goal's projection candidates, the expected
49+
/// term is always fully unconstrained. This would weaken inference however, as the nested
5250
/// goals never get the inference constraints from the actual normalized-to type.
5351
///
54-
/// Because of this we return any ambiguous nested goals from `NormalizesTo` to the
55-
/// caller when then adds these to its own context. The caller is always an `AliasRelate`
56-
/// goal so this never leaks out of the solver.
52+
/// Because of this we return any ambiguous nested goals from the candidate probe to the
53+
/// root of the `NormalizesTo` goal then adds these to its own context. So this never leaks
54+
/// out of the solver.
5755
NormalizesTo,
5856
}
5957

@@ -67,7 +65,6 @@ impl CurrentGoalKind {
6765
CurrentGoalKind::Misc
6866
}
6967
}
70-
ty::PredicateKind::NormalizesTo(_) => CurrentGoalKind::NormalizesTo,
7168
_ => CurrentGoalKind::Misc,
7269
}
7370
}
@@ -509,17 +506,6 @@ where
509506
HasChanged::No => {
510507
let mut stalled_vars = orig_values;
511508

512-
// Remove the unconstrained RHS arg, which is expected to have changed.
513-
if let Some(normalizes_to) = goal.predicate.as_normalizes_to() {
514-
let normalizes_to = normalizes_to.skip_binder();
515-
let rhs_arg: I::GenericArg = normalizes_to.term.into();
516-
let idx = stalled_vars
517-
.iter()
518-
.rposition(|arg| *arg == rhs_arg)
519-
.expect("expected unconstrained arg");
520-
stalled_vars.swap_remove(idx);
521-
}
522-
523509
// Remove the canonicalized universal vars, since we only care about stalled existentials.
524510
let mut sub_roots = Vec::new();
525511
stalled_vars.retain(|arg| match arg.kind() {
@@ -564,7 +550,12 @@ where
564550
pub(super) fn compute_goal(&mut self, goal: Goal<I, I::Predicate>) -> QueryResult<I> {
565551
let Goal { param_env, predicate } = goal;
566552
let kind = predicate.kind();
567-
self.enter_forall(kind, |ecx, kind| match kind {
553+
554+
if let Some(normalizes_to) = predicate.as_normalizes_to() {
555+
assert!(!normalizes_to.skip_binder().has_escaping_bound_vars());
556+
}
557+
558+
let resp = self.enter_forall(kind, |ecx, kind| match kind {
568559
ty::PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {
569560
ecx.compute_trait_goal(Goal { param_env, predicate }).map(|(r, _via)| r)
570561
}
@@ -613,7 +604,11 @@ where
613604
ty::PredicateKind::Ambiguous => {
614605
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
615606
}
616-
})
607+
})?;
608+
609+
assert!(resp.value.external_constraints.normalization_nested_goals.is_empty());
610+
611+
Ok(resp)
617612
}
618613

619614
// Recursively evaluates all the goals added to this `EvalCtxt` to completion, returning
@@ -639,7 +634,6 @@ where
639634
///
640635
/// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
641636
fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
642-
let cx = self.cx();
643637
// If this loop did not result in any progress, what's our final certainty.
644638
let mut unchanged_certainty = Some(Certainty::Yes);
645639
for (source, goal, stalled_on) in mem::take(&mut self.nested_goals) {
@@ -654,92 +648,17 @@ where
654648
continue;
655649
}
656650

657-
// We treat normalizes-to goals specially here. In each iteration we take the
658-
// RHS of the projection, replace it with a fresh inference variable, and only
659-
// after evaluating that goal do we equate the fresh inference variable with the
660-
// actual RHS of the predicate.
661-
//
662-
// This is both to improve caching, and to avoid using the RHS of the
663-
// projection predicate to influence the normalizes-to candidate we select.
664-
//
665-
// Forgetting to replace the RHS with a fresh inference variable when we evaluate
666-
// this goal results in an ICE.
667-
if let Some(pred) = goal.predicate.as_normalizes_to() {
668-
// We should never encounter higher-ranked normalizes-to goals.
669-
let pred = pred.no_bound_vars().unwrap();
670-
// Replace the goal with an unconstrained infer var, so the
671-
// RHS does not affect projection candidate assembly.
672-
let unconstrained_rhs = self.next_term_infer_of_kind(pred.term);
673-
let unconstrained_goal =
674-
goal.with(cx, ty::NormalizesTo { alias: pred.alias, term: unconstrained_rhs });
675-
676-
let (
677-
NestedNormalizationGoals(nested_goals),
678-
GoalEvaluation { goal, certainty, stalled_on, has_changed: _ },
679-
) = self.evaluate_goal_raw(source, unconstrained_goal, stalled_on)?;
680-
// Add the nested goals from normalization to our own nested goals.
681-
trace!(?nested_goals);
682-
self.nested_goals.extend(nested_goals.into_iter().map(|(s, g)| (s, g, None)));
683-
684-
// Finally, equate the goal's RHS with the unconstrained var.
685-
//
686-
// SUBTLE:
687-
// We structurally relate aliases here. This is necessary
688-
// as we otherwise emit a nested `AliasRelate` goal in case the
689-
// returned term is a rigid alias, resulting in overflow.
690-
//
691-
// It is correct as both `goal.predicate.term` and `unconstrained_rhs`
692-
// start out as an unconstrained inference variable so any aliases get
693-
// fully normalized when instantiating it.
694-
//
695-
// FIXME: Strictly speaking this may be incomplete if the normalized-to
696-
// type contains an ambiguous alias referencing bound regions. We should
697-
// consider changing this to only use "shallow structural equality".
698-
self.eq_structurally_relating_aliases(
699-
goal.param_env,
700-
pred.term,
701-
unconstrained_rhs,
702-
)?;
703-
704-
// We only look at the `projection_ty` part here rather than
705-
// looking at the "has changed" return from evaluate_goal,
706-
// because we expect the `unconstrained_rhs` part of the predicate
707-
// to have changed -- that means we actually normalized successfully!
708-
// FIXME: Do we need to eagerly resolve here? Or should we check
709-
// if the cache key has any changed vars?
710-
let with_resolved_vars = self.resolve_vars_if_possible(goal);
711-
if pred.alias
712-
!= with_resolved_vars
713-
.predicate
714-
.as_normalizes_to()
715-
.unwrap()
716-
.no_bound_vars()
717-
.unwrap()
718-
.alias
719-
{
720-
unchanged_certainty = None;
721-
}
722-
723-
match certainty {
724-
Certainty::Yes => {}
725-
Certainty::Maybe { .. } => {
726-
self.nested_goals.push((source, with_resolved_vars, stalled_on));
727-
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
728-
}
729-
}
730-
} else {
731-
let GoalEvaluation { goal, certainty, has_changed, stalled_on } =
732-
self.evaluate_goal(source, goal, stalled_on)?;
733-
if has_changed == HasChanged::Yes {
734-
unchanged_certainty = None;
735-
}
651+
let GoalEvaluation { goal, certainty, has_changed, stalled_on } =
652+
self.evaluate_goal(source, goal, stalled_on)?;
653+
if has_changed == HasChanged::Yes {
654+
unchanged_certainty = None;
655+
}
736656

737-
match certainty {
738-
Certainty::Yes => {}
739-
Certainty::Maybe { .. } => {
740-
self.nested_goals.push((source, goal, stalled_on));
741-
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
742-
}
657+
match certainty {
658+
Certainty::Yes => {}
659+
Certainty::Maybe { .. } => {
660+
self.nested_goals.push((source, goal, stalled_on));
661+
unchanged_certainty = unchanged_certainty.map(|c| c.and(certainty));
743662
}
744663
}
745664
}
@@ -802,129 +721,6 @@ where
802721
}
803722
}
804723

805-
/// Is the projection predicate is of the form `exists<T> <Ty as Trait>::Assoc = T`.
806-
///
807-
/// This is the case if the `term` does not occur in any other part of the predicate
808-
/// and is able to name all other placeholder and inference variables.
809-
#[instrument(level = "trace", skip(self), ret)]
810-
pub(super) fn term_is_fully_unconstrained(&self, goal: Goal<I, ty::NormalizesTo<I>>) -> bool {
811-
let universe_of_term = match goal.predicate.term.kind() {
812-
ty::TermKind::Ty(ty) => {
813-
if let ty::Infer(ty::TyVar(vid)) = ty.kind() {
814-
self.delegate.universe_of_ty(vid).unwrap()
815-
} else {
816-
return false;
817-
}
818-
}
819-
ty::TermKind::Const(ct) => {
820-
if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() {
821-
self.delegate.universe_of_ct(vid).unwrap()
822-
} else {
823-
return false;
824-
}
825-
}
826-
};
827-
828-
struct ContainsTermOrNotNameable<'a, D: SolverDelegate<Interner = I>, I: Interner> {
829-
term: I::Term,
830-
universe_of_term: ty::UniverseIndex,
831-
delegate: &'a D,
832-
cache: HashSet<I::Ty>,
833-
}
834-
835-
impl<D: SolverDelegate<Interner = I>, I: Interner> ContainsTermOrNotNameable<'_, D, I> {
836-
fn check_nameable(&self, universe: ty::UniverseIndex) -> ControlFlow<()> {
837-
if self.universe_of_term.can_name(universe) {
838-
ControlFlow::Continue(())
839-
} else {
840-
ControlFlow::Break(())
841-
}
842-
}
843-
}
844-
845-
impl<D: SolverDelegate<Interner = I>, I: Interner> TypeVisitor<I>
846-
for ContainsTermOrNotNameable<'_, D, I>
847-
{
848-
type Result = ControlFlow<()>;
849-
fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
850-
if self.cache.contains(&t) {
851-
return ControlFlow::Continue(());
852-
}
853-
854-
match t.kind() {
855-
ty::Infer(ty::TyVar(vid)) => {
856-
if let ty::TermKind::Ty(term) = self.term.kind()
857-
&& let ty::Infer(ty::TyVar(term_vid)) = term.kind()
858-
&& self.delegate.root_ty_var(vid) == self.delegate.root_ty_var(term_vid)
859-
{
860-
return ControlFlow::Break(());
861-
}
862-
863-
self.check_nameable(self.delegate.universe_of_ty(vid).unwrap())?;
864-
}
865-
ty::Placeholder(p) => self.check_nameable(p.universe())?,
866-
_ => {
867-
if t.has_non_region_infer() || t.has_placeholders() {
868-
t.super_visit_with(self)?
869-
}
870-
}
871-
}
872-
873-
assert!(self.cache.insert(t));
874-
ControlFlow::Continue(())
875-
}
876-
877-
fn visit_const(&mut self, c: I::Const) -> Self::Result {
878-
match c.kind() {
879-
ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
880-
if let ty::TermKind::Const(term) = self.term.kind()
881-
&& let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind()
882-
&& self.delegate.root_const_var(vid)
883-
== self.delegate.root_const_var(term_vid)
884-
{
885-
return ControlFlow::Break(());
886-
}
887-
888-
self.check_nameable(self.delegate.universe_of_ct(vid).unwrap())
889-
}
890-
ty::ConstKind::Placeholder(p) => self.check_nameable(p.universe()),
891-
_ => {
892-
if c.has_non_region_infer() || c.has_placeholders() {
893-
c.super_visit_with(self)
894-
} else {
895-
ControlFlow::Continue(())
896-
}
897-
}
898-
}
899-
}
900-
901-
fn visit_predicate(&mut self, p: I::Predicate) -> Self::Result {
902-
if p.has_non_region_infer() || p.has_placeholders() {
903-
p.super_visit_with(self)
904-
} else {
905-
ControlFlow::Continue(())
906-
}
907-
}
908-
909-
fn visit_clauses(&mut self, c: I::Clauses) -> Self::Result {
910-
if c.has_non_region_infer() || c.has_placeholders() {
911-
c.super_visit_with(self)
912-
} else {
913-
ControlFlow::Continue(())
914-
}
915-
}
916-
}
917-
918-
let mut visitor = ContainsTermOrNotNameable {
919-
delegate: self.delegate,
920-
universe_of_term,
921-
term: goal.predicate.term,
922-
cache: Default::default(),
923-
};
924-
goal.predicate.alias.visit_with(&mut visitor).is_continue()
925-
&& goal.param_env.visit_with(&mut visitor).is_continue()
926-
}
927-
928724
pub(super) fn sub_unify_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) {
929725
self.delegate.sub_unify_ty_vids_raw(a, b)
930726
}

0 commit comments

Comments
 (0)