Skip to content

Commit e5e3a95

Browse files
committed
norm nested aliases before evaluating the parent goal
1 parent 3c877f6 commit e5e3a95

File tree

13 files changed

+155
-152
lines changed

13 files changed

+155
-152
lines changed

Cargo.lock

-1
Original file line numberDiff line numberDiff line change
@@ -4158,7 +4158,6 @@ dependencies = [
41584158
"rustc_data_structures",
41594159
"rustc_index",
41604160
"rustc_macros",
4161-
"rustc_serialize",
41624161
"rustc_type_ir",
41634162
"rustc_type_ir_macros",
41644163
"tracing",

compiler/rustc_middle/src/ty/predicate.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,10 @@ impl<'tcx> Predicate<'tcx> {
121121
/// unsoundly accept some programs. See #91068.
122122
#[inline]
123123
pub fn allow_normalization(self) -> bool {
124-
// Keep this in sync with the one in `rustc_type_ir::inherent`!
125124
match self.kind().skip_binder() {
126-
PredicateKind::Clause(ClauseKind::WellFormed(_))
127-
| PredicateKind::AliasRelate(..)
128-
| PredicateKind::NormalizesTo(..) => false,
125+
PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::AliasRelate(..) => {
126+
false
127+
}
129128
PredicateKind::Clause(ClauseKind::Trait(_))
130129
| PredicateKind::Clause(ClauseKind::HostEffect(..))
131130
| PredicateKind::Clause(ClauseKind::RegionOutlives(_))
@@ -137,6 +136,7 @@ impl<'tcx> Predicate<'tcx> {
137136
| PredicateKind::Coerce(_)
138137
| PredicateKind::Clause(ClauseKind::ConstEvaluatable(_))
139138
| PredicateKind::ConstEquate(_, _)
139+
| PredicateKind::NormalizesTo(..)
140140
| PredicateKind::Ambiguous => true,
141141
}
142142
}

compiler/rustc_next_trait_solver/Cargo.toml

-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ derive-where = "1.2.7"
99
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
1010
rustc_index = { path = "../rustc_index", default-features = false }
1111
rustc_macros = { path = "../rustc_macros", optional = true }
12-
rustc_serialize = { path = "../rustc_serialize", optional = true }
1312
rustc_type_ir = { path = "../rustc_type_ir", default-features = false }
1413
rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
1514
tracing = "0.1"
@@ -20,7 +19,6 @@ default = ["nightly"]
2019
nightly = [
2120
"dep:rustc_data_structures",
2221
"dep:rustc_macros",
23-
"dep:rustc_serialize",
2422
"rustc_index/nightly",
2523
"rustc_type_ir/nightly",
2624
]

compiler/rustc_next_trait_solver/src/solve/alias_relate.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//! relate them structurally.
1717
1818
use rustc_type_ir::inherent::*;
19+
use rustc_type_ir::solve::GoalSource;
1920
use rustc_type_ir::{self as ty, Interner};
2021
use tracing::{instrument, trace};
2122

@@ -49,7 +50,10 @@ where
4950
// Structurally normalize the lhs.
5051
let lhs = if let Some(alias) = lhs.to_alias_term() {
5152
let term = self.next_term_infer_of_kind(lhs);
52-
self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term }));
53+
self.add_goal(
54+
GoalSource::TypeRelating,
55+
goal.with(cx, ty::NormalizesTo { alias, term }),
56+
);
5357
term
5458
} else {
5559
lhs
@@ -58,7 +62,10 @@ where
5862
// Structurally normalize the rhs.
5963
let rhs = if let Some(alias) = rhs.to_alias_term() {
6064
let term = self.next_term_infer_of_kind(rhs);
61-
self.add_normalizes_to_goal(goal.with(cx, ty::NormalizesTo { alias, term }));
65+
self.add_goal(
66+
GoalSource::TypeRelating,
67+
goal.with(cx, ty::NormalizesTo { alias, term }),
68+
);
6269
term
6370
} else {
6471
rhs

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

+4-8
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use tracing::{debug, instrument, trace};
2222
use crate::canonicalizer::Canonicalizer;
2323
use crate::delegate::SolverDelegate;
2424
use crate::resolve::EagerResolver;
25-
use crate::solve::eval_ctxt::{CurrentGoalKind, NestedGoals};
25+
use crate::solve::eval_ctxt::CurrentGoalKind;
2626
use crate::solve::{
2727
CanonicalInput, CanonicalResponse, Certainty, EvalCtxt, ExternalConstraintsData, Goal,
2828
MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData, QueryInput,
@@ -112,13 +112,9 @@ where
112112
// by `try_evaluate_added_goals()`.
113113
let (certainty, normalization_nested_goals) = match self.current_goal_kind {
114114
CurrentGoalKind::NormalizesTo => {
115-
let NestedGoals { normalizes_to_goals, goals } =
116-
std::mem::take(&mut self.nested_goals);
117-
if cfg!(debug_assertions) {
118-
assert!(normalizes_to_goals.is_empty());
119-
if goals.is_empty() {
120-
assert!(matches!(goals_certainty, Certainty::Yes));
121-
}
115+
let goals = std::mem::take(&mut self.nested_goals);
116+
if goals.is_empty() {
117+
assert!(matches!(goals_certainty, Certainty::Yes));
122118
}
123119
(certainty, NestedNormalizationGoals(goals))
124120
}

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

+76-115
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1+
use std::mem;
12
use std::ops::ControlFlow;
23

3-
use derive_where::derive_where;
44
#[cfg(feature = "nightly")]
5-
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, HashStable_NoContext};
5+
use rustc_macros::HashStable_NoContext;
66
use rustc_type_ir::data_structures::{HashMap, HashSet, ensure_sufficient_stack};
77
use rustc_type_ir::fast_reject::DeepRejectCtxt;
88
use rustc_type_ir::inherent::*;
@@ -14,7 +14,6 @@ use rustc_type_ir::{
1414
TypeSuperFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
1515
TypingMode,
1616
};
17-
use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
1817
use tracing::{instrument, trace};
1918

2019
use crate::coherence;
@@ -114,7 +113,7 @@ where
114113

115114
pub(super) search_graph: &'a mut SearchGraph<D>,
116115

117-
nested_goals: NestedGoals<I>,
116+
nested_goals: Vec<(GoalSource, Goal<I, I::Predicate>)>,
118117

119118
pub(super) origin_span: I::Span,
120119

@@ -129,38 +128,6 @@ where
129128
pub(super) inspect: ProofTreeBuilder<D>,
130129
}
131130

132-
#[derive_where(Clone, Debug, Default; I: Interner)]
133-
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic)]
134-
#[cfg_attr(
135-
feature = "nightly",
136-
derive(Decodable_NoContext, Encodable_NoContext, HashStable_NoContext)
137-
)]
138-
struct NestedGoals<I: Interner> {
139-
/// These normalizes-to goals are treated specially during the evaluation
140-
/// loop. In each iteration we take the RHS of the projection, replace it with
141-
/// a fresh inference variable, and only after evaluating that goal do we
142-
/// equate the fresh inference variable with the actual RHS of the predicate.
143-
///
144-
/// This is both to improve caching, and to avoid using the RHS of the
145-
/// projection predicate to influence the normalizes-to candidate we select.
146-
///
147-
/// Forgetting to replace the RHS with a fresh inference variable when we evaluate
148-
/// this goal results in an ICE..
149-
pub normalizes_to_goals: Vec<Goal<I, ty::NormalizesTo<I>>>,
150-
/// The rest of the goals which have not yet processed or remain ambiguous.
151-
pub goals: Vec<(GoalSource, Goal<I, I::Predicate>)>,
152-
}
153-
154-
impl<I: Interner> NestedGoals<I> {
155-
fn new() -> Self {
156-
Self { normalizes_to_goals: Vec::new(), goals: Vec::new() }
157-
}
158-
159-
fn is_empty(&self) -> bool {
160-
self.normalizes_to_goals.is_empty() && self.goals.is_empty()
161-
}
162-
}
163-
164131
#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)]
165132
#[cfg_attr(feature = "nightly", derive(HashStable_NoContext))]
166133
pub enum GenerateProofTree {
@@ -332,7 +299,7 @@ where
332299
let mut ecx = EvalCtxt {
333300
delegate,
334301
search_graph: &mut search_graph,
335-
nested_goals: NestedGoals::new(),
302+
nested_goals: Default::default(),
336303
inspect: ProofTreeBuilder::new_maybe_root(generate_proof_tree),
337304

338305
// Only relevant when canonicalizing the response,
@@ -385,7 +352,7 @@ where
385352
predefined_opaques_in_body: input.predefined_opaques_in_body,
386353
max_input_universe: canonical_input.canonical.max_universe,
387354
search_graph,
388-
nested_goals: NestedGoals::new(),
355+
nested_goals: Default::default(),
389356
origin_span: I::Span::dummy(),
390357
tainted: Ok(()),
391358
inspect: canonical_goal_evaluation.new_goal_evaluation_step(var_values),
@@ -629,78 +596,83 @@ where
629596
/// Goals for the next step get directly added to the nested goals of the `EvalCtxt`.
630597
fn evaluate_added_goals_step(&mut self) -> Result<Option<Certainty>, NoSolution> {
631598
let cx = self.cx();
632-
let mut goals = core::mem::take(&mut self.nested_goals);
633-
634599
// If this loop did not result in any progress, what's our final certainty.
635600
let mut unchanged_certainty = Some(Certainty::Yes);
636-
for goal in goals.normalizes_to_goals {
637-
// Replace the goal with an unconstrained infer var, so the
638-
// RHS does not affect projection candidate assembly.
639-
let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
640-
let unconstrained_goal = goal.with(
641-
cx,
642-
ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs },
643-
);
644-
645-
let (NestedNormalizationGoals(nested_goals), _, certainty) = self.evaluate_goal_raw(
646-
GoalEvaluationKind::Nested,
647-
GoalSource::TypeRelating,
648-
unconstrained_goal,
649-
)?;
650-
// Add the nested goals from normalization to our own nested goals.
651-
trace!(?nested_goals);
652-
goals.goals.extend(nested_goals);
653-
654-
// Finally, equate the goal's RHS with the unconstrained var.
601+
for (source, goal) in mem::take(&mut self.nested_goals) {
602+
// We treat normalizes-to goals specially here. In each iteration we take the
603+
// RHS of the projection, replace it with a fresh inference variable, and only
604+
// after evaluating that goal do we equate the fresh inference variable with the
605+
// actual RHS of the predicate.
655606
//
656-
// SUBTLE:
657-
// We structurally relate aliases here. This is necessary
658-
// as we otherwise emit a nested `AliasRelate` goal in case the
659-
// returned term is a rigid alias, resulting in overflow.
607+
// This is both to improve caching, and to avoid using the RHS of the
608+
// projection predicate to influence the normalizes-to candidate we select.
660609
//
661-
// It is correct as both `goal.predicate.term` and `unconstrained_rhs`
662-
// start out as an unconstrained inference variable so any aliases get
663-
// fully normalized when instantiating it.
664-
//
665-
// FIXME: Strictly speaking this may be incomplete if the normalized-to
666-
// type contains an ambiguous alias referencing bound regions. We should
667-
// consider changing this to only use "shallow structural equality".
668-
self.eq_structurally_relating_aliases(
669-
goal.param_env,
670-
goal.predicate.term,
671-
unconstrained_rhs,
672-
)?;
673-
674-
// We only look at the `projection_ty` part here rather than
675-
// looking at the "has changed" return from evaluate_goal,
676-
// because we expect the `unconstrained_rhs` part of the predicate
677-
// to have changed -- that means we actually normalized successfully!
678-
let with_resolved_vars = self.resolve_vars_if_possible(goal);
679-
if goal.predicate.alias != with_resolved_vars.predicate.alias {
680-
unchanged_certainty = None;
681-
}
682-
683-
match certainty {
684-
Certainty::Yes => {}
685-
Certainty::Maybe(_) => {
686-
self.nested_goals.normalizes_to_goals.push(with_resolved_vars);
687-
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
610+
// Forgetting to replace the RHS with a fresh inference variable when we evaluate
611+
// this goal results in an ICE.
612+
if let Some(pred) = goal.predicate.as_normalizes_to() {
613+
// We should never encounter higher-ranked normalizes-to goals.
614+
let pred = pred.no_bound_vars().unwrap();
615+
// Replace the goal with an unconstrained infer var, so the
616+
// RHS does not affect projection candidate assembly.
617+
let unconstrained_rhs = self.next_term_infer_of_kind(pred.term);
618+
let unconstrained_goal =
619+
goal.with(cx, ty::NormalizesTo { alias: pred.alias, term: unconstrained_rhs });
620+
621+
let (NestedNormalizationGoals(nested_goals), _, certainty) =
622+
self.evaluate_goal_raw(GoalEvaluationKind::Nested, source, unconstrained_goal)?;
623+
// Add the nested goals from normalization to our own nested goals.
624+
trace!(?nested_goals);
625+
self.nested_goals.extend(nested_goals);
626+
627+
// Finally, equate the goal's RHS with the unconstrained var.
628+
//
629+
// SUBTLE:
630+
// We structurally relate aliases here. This is necessary
631+
// as we otherwise emit a nested `AliasRelate` goal in case the
632+
// returned term is a rigid alias, resulting in overflow.
633+
//
634+
// It is correct as both `goal.predicate.term` and `unconstrained_rhs`
635+
// start out as an unconstrained inference variable so any aliases get
636+
// fully normalized when instantiating it.
637+
//
638+
// FIXME: Strictly speaking this may be incomplete if the normalized-to
639+
// type contains an ambiguous alias referencing bound regions. We should
640+
// consider changing this to only use "shallow structural equality".
641+
self.eq_structurally_relating_aliases(
642+
goal.param_env,
643+
pred.term,
644+
unconstrained_rhs,
645+
)?;
646+
647+
// We only look at the `projection_ty` part here rather than
648+
// looking at the "has changed" return from evaluate_goal,
649+
// because we expect the `unconstrained_rhs` part of the predicate
650+
// to have changed -- that means we actually normalized successfully!
651+
let with_resolved_vars = self.resolve_vars_if_possible(goal);
652+
if pred.alias != goal.predicate.as_normalizes_to().unwrap().skip_binder().alias {
653+
unchanged_certainty = None;
688654
}
689-
}
690-
}
691655

692-
for (source, goal) in goals.goals {
693-
let (has_changed, certainty) =
694-
self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?;
695-
if has_changed == HasChanged::Yes {
696-
unchanged_certainty = None;
697-
}
656+
match certainty {
657+
Certainty::Yes => {}
658+
Certainty::Maybe(_) => {
659+
self.nested_goals.push((source, with_resolved_vars));
660+
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
661+
}
662+
}
663+
} else {
664+
let (has_changed, certainty) =
665+
self.evaluate_goal(GoalEvaluationKind::Nested, source, goal)?;
666+
if has_changed == HasChanged::Yes {
667+
unchanged_certainty = None;
668+
}
698669

699-
match certainty {
700-
Certainty::Yes => {}
701-
Certainty::Maybe(_) => {
702-
self.nested_goals.goals.push((source, goal));
703-
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
670+
match certainty {
671+
Certainty::Yes => {}
672+
Certainty::Maybe(_) => {
673+
self.nested_goals.push((source, goal));
674+
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
675+
}
704676
}
705677
}
706678
}
@@ -717,23 +689,12 @@ where
717689
self.delegate.cx()
718690
}
719691

720-
#[instrument(level = "trace", skip(self))]
721-
pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal<I, ty::NormalizesTo<I>>) {
722-
goal.predicate = goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(
723-
self,
724-
GoalSource::TypeRelating,
725-
goal.param_env,
726-
));
727-
self.inspect.add_normalizes_to_goal(self.delegate, self.max_input_universe, goal);
728-
self.nested_goals.normalizes_to_goals.push(goal);
729-
}
730-
731692
#[instrument(level = "debug", skip(self))]
732693
pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal<I, I::Predicate>) {
733694
goal.predicate =
734695
goal.predicate.fold_with(&mut ReplaceAliasWithInfer::new(self, source, goal.param_env));
735696
self.inspect.add_goal(self.delegate, self.max_input_universe, source, goal);
736-
self.nested_goals.goals.push((source, goal));
697+
self.nested_goals.push((source, goal));
737698
}
738699

739700
#[instrument(level = "trace", skip(self, goals))]

compiler/rustc_next_trait_solver/src/solve/inspect/build.rs

-14
Original file line numberDiff line numberDiff line change
@@ -412,20 +412,6 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<D> {
412412
}
413413
}
414414

415-
pub(crate) fn add_normalizes_to_goal(
416-
&mut self,
417-
delegate: &D,
418-
max_input_universe: ty::UniverseIndex,
419-
goal: Goal<I, ty::NormalizesTo<I>>,
420-
) {
421-
self.add_goal(
422-
delegate,
423-
max_input_universe,
424-
GoalSource::TypeRelating,
425-
goal.with(delegate.cx(), goal.predicate),
426-
);
427-
}
428-
429415
pub(crate) fn add_goal(
430416
&mut self,
431417
delegate: &D,

compiler/rustc_type_ir/src/inherent.rs

+8
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,14 @@ pub trait Predicate<I: Interner<Predicate = Self>>:
442442
{
443443
fn as_clause(self) -> Option<I::Clause>;
444444

445+
fn as_normalizes_to(self) -> Option<ty::Binder<I, ty::NormalizesTo<I>>> {
446+
let kind = self.kind();
447+
match kind.skip_binder() {
448+
ty::PredicateKind::NormalizesTo(pred) => Some(kind.rebind(pred)),
449+
_ => None,
450+
}
451+
}
452+
445453
// FIXME: Eventually uplift the impl out of rustc and make this defaulted.
446454
fn allow_normalization(self) -> bool;
447455
}

0 commit comments

Comments
 (0)