Skip to content

Commit 33c274f

Browse files
committed
move normalizes_to_hack to AliasRelate
1 parent a42873e commit 33c274f

File tree

8 files changed

+60
-67
lines changed

8 files changed

+60
-67
lines changed

compiler/rustc_trait_selection/src/solve/alias_relate.rs

+21-12
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@
2121
//! However, if `?fresh_var` ends up geteting equated to another type, we retry the
2222
//! `NormalizesTo` goal, at which point the opaque is actually defined.
2323
24-
use super::{EvalCtxt, GoalSource};
24+
use super::EvalCtxt;
2525
use rustc_infer::traits::query::NoSolution;
26+
use rustc_infer::traits::solve::GoalSource;
2627
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
2728
use rustc_middle::ty::{self, Ty};
2829

@@ -121,10 +122,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
121122
ty::TermKind::Const(_) => {
122123
if let Some(alias) = term.to_alias_ty(self.tcx()) {
123124
let term = self.next_term_infer_of_kind(term);
124-
self.add_goal(
125-
GoalSource::Misc,
126-
Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }),
127-
);
125+
self.add_normalizes_to_goal(Goal::new(
126+
self.tcx(),
127+
param_env,
128+
ty::NormalizesTo { alias, term },
129+
));
128130
self.try_evaluate_added_goals()?;
129131
Ok(Some(self.resolve_vars_if_possible(term)))
130132
} else {
@@ -145,18 +147,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
145147
return None;
146148
}
147149

148-
let ty::Alias(_, alias) = *ty.kind() else {
150+
let ty::Alias(kind, alias) = *ty.kind() else {
149151
return Some(ty);
150152
};
151153

152154
match self.commit_if_ok(|this| {
155+
let tcx = this.tcx();
153156
let normalized_ty = this.next_ty_infer();
154-
let normalizes_to_goal = Goal::new(
155-
this.tcx(),
156-
param_env,
157-
ty::NormalizesTo { alias, term: normalized_ty.into() },
158-
);
159-
this.add_goal(GoalSource::Misc, normalizes_to_goal);
157+
let normalizes_to = ty::NormalizesTo { alias, term: normalized_ty.into() };
158+
match kind {
159+
ty::AliasKind::Opaque => {
160+
// HACK: Unlike for associated types, `normalizes-to` for opaques
161+
// is currently not treated as a function. We do not erase the
162+
// expected term.
163+
this.add_goal(GoalSource::Misc, Goal::new(tcx, param_env, normalizes_to));
164+
}
165+
ty::AliasKind::Projection | ty::AliasKind::Inherent | ty::AliasKind::Weak => {
166+
this.add_normalizes_to_goal(Goal::new(tcx, param_env, normalizes_to))
167+
}
168+
}
160169
this.try_evaluate_added_goals()?;
161170
Ok(this.resolve_vars_if_possible(normalized_ty))
162171
}) {

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

+12-15
Original file line numberDiff line numberDiff line change
@@ -93,34 +93,32 @@ pub struct EvalCtxt<'a, 'tcx> {
9393

9494
#[derive(Debug, Clone)]
9595
pub(super) struct NestedGoals<'tcx> {
96-
/// This normalizes-to goal that is treated specially during the evaluation
96+
/// These normalizes-to goals are treated specially during the evaluation
9797
/// loop. In each iteration we take the RHS of the projection, replace it with
9898
/// a fresh inference variable, and only after evaluating that goal do we
9999
/// equate the fresh inference variable with the actual RHS of the predicate.
100100
///
101101
/// This is both to improve caching, and to avoid using the RHS of the
102102
/// projection predicate to influence the normalizes-to candidate we select.
103103
///
104-
/// This is not a 'real' nested goal. We must not forget to replace the RHS
105-
/// with a fresh inference variable when we evaluate this goal. That can result
106-
/// in a trait solver cycle. This would currently result in overflow but can be
107-
/// can be unsound with more powerful coinduction in the future.
108-
pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::NormalizesTo<'tcx>>>,
104+
/// Forgetting to replace the RHS with a fresh inference variable when we evaluate
105+
/// this goal results in an ICE..
106+
pub(super) normalizes_to_goals: Vec<Goal<'tcx, ty::NormalizesTo<'tcx>>>,
109107
/// The rest of the goals which have not yet processed or remain ambiguous.
110108
pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>,
111109
}
112110

113111
impl<'tcx> NestedGoals<'tcx> {
114112
pub(super) fn new() -> Self {
115-
Self { normalizes_to_hack_goal: None, goals: Vec::new() }
113+
Self { normalizes_to_goals: Vec::new(), goals: Vec::new() }
116114
}
117115

118116
pub(super) fn is_empty(&self) -> bool {
119-
self.normalizes_to_hack_goal.is_none() && self.goals.is_empty()
117+
self.normalizes_to_goals.is_empty() && self.goals.is_empty()
120118
}
121119

122120
pub(super) fn extend(&mut self, other: NestedGoals<'tcx>) {
123-
assert_eq!(other.normalizes_to_hack_goal, None);
121+
self.normalizes_to_goals.extend(other.normalizes_to_goals);
124122
self.goals.extend(other.goals)
125123
}
126124
}
@@ -508,7 +506,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
508506

509507
// If this loop did not result in any progress, what's our final certainty.
510508
let mut unchanged_certainty = Some(Certainty::Yes);
511-
if let Some(goal) = goals.normalizes_to_hack_goal.take() {
509+
for goal in goals.normalizes_to_goals {
512510
// Replace the goal with an unconstrained infer var, so the
513511
// RHS does not affect projection candidate assembly.
514512
let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
@@ -536,22 +534,21 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
536534
// looking at the "has changed" return from evaluate_goal,
537535
// because we expect the `unconstrained_rhs` part of the predicate
538536
// to have changed -- that means we actually normalized successfully!
539-
if goal.predicate.alias != self.resolve_vars_if_possible(goal.predicate.alias) {
537+
let with_resolved_vars = self.resolve_vars_if_possible(goal);
538+
if goal.predicate.alias != with_resolved_vars.predicate.alias {
540539
unchanged_certainty = None;
541540
}
542541

543542
match certainty {
544543
Certainty::Yes => {}
545544
Certainty::Maybe(_) => {
546-
// We need to resolve vars here so that we correctly
547-
// deal with `has_changed` in the next iteration.
548-
self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal));
545+
self.nested_goals.normalizes_to_goals.push(with_resolved_vars);
549546
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
550547
}
551548
}
552549
}
553550

554-
for (source, goal) in goals.goals.drain(..) {
551+
for (source, goal) in goals.goals {
555552
let (has_changed, certainty) = self.evaluate_goal(
556553
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
557554
source,

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

+11
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,17 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
419419
}
420420
}
421421

422+
pub fn add_normalizes_to_goal(
423+
ecx: &mut EvalCtxt<'_, 'tcx>,
424+
goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
425+
) {
426+
if ecx.inspect.is_noop() {
427+
return;
428+
}
429+
430+
Self::add_goal(ecx, GoalSource::Misc, goal.with(ecx.tcx(), goal.predicate));
431+
}
432+
422433
pub fn add_goal(
423434
ecx: &mut EvalCtxt<'_, 'tcx>,
424435
source: GoalSource,

compiler/rustc_trait_selection/src/solve/mod.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
202202

203203
impl<'tcx> EvalCtxt<'_, 'tcx> {
204204
#[instrument(level = "debug", skip(self))]
205-
fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
206-
assert!(
207-
self.nested_goals.normalizes_to_hack_goal.is_none(),
208-
"attempted to set the projection eq hack goal when one already exists"
209-
);
210-
self.nested_goals.normalizes_to_hack_goal = Some(goal);
205+
fn add_normalizes_to_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
206+
inspect::ProofTreeBuilder::add_normalizes_to_goal(self, goal);
207+
self.nested_goals.normalizes_to_goals.push(goal);
211208
}
212209

213210
#[instrument(level = "debug", skip(self))]

compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
1616
.no_bound_vars()
1717
.expect("const ty should not rely on other generics"),
1818
) {
19-
self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
19+
self.instantiate_normalizes_to_term(goal, normalized_const.into());
2020
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
2121
} else {
2222
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)

compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs

+2-7
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
1616
) -> QueryResult<'tcx> {
1717
let tcx = self.tcx();
1818
let inherent = goal.predicate.alias;
19-
let expected = goal.predicate.term.ty().expect("inherent consts are treated separately");
2019

2120
let impl_def_id = tcx.parent(inherent.def_id);
2221
let impl_args = self.fresh_args_for_item(impl_def_id);
@@ -30,12 +29,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
3029

3130
// Equate IAT with the RHS of the project goal
3231
let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, tcx);
33-
self.eq(
34-
goal.param_env,
35-
expected,
36-
tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args),
37-
)
38-
.expect("expected goal term to be fully unconstrained");
3932

4033
// Check both where clauses on the impl and IAT
4134
//
@@ -51,6 +44,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
5144
.map(|(pred, _)| goal.with(tcx, pred)),
5245
);
5346

47+
let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args);
48+
self.instantiate_normalizes_to_term(goal, normalized.into());
5449
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
5550
}
5651
}

compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs

+7-22
Original file line numberDiff line numberDiff line change
@@ -31,32 +31,17 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
3131
goal: Goal<'tcx, NormalizesTo<'tcx>>,
3232
) -> QueryResult<'tcx> {
3333
let def_id = goal.predicate.def_id();
34+
let def_kind = self.tcx().def_kind(def_id);
35+
if cfg!(debug_assertions) && !matches!(def_kind, DefKind::OpaqueTy) {
36+
assert!(self.term_is_fully_unconstrained(goal));
37+
}
38+
3439
match self.tcx().def_kind(def_id) {
3540
DefKind::AssocTy | DefKind::AssocConst => {
3641
match self.tcx().associated_item(def_id).container {
3742
ty::AssocItemContainer::TraitContainer => {
38-
// To only compute normalization once for each projection we only
39-
// assemble normalization candidates if the expected term is an
40-
// unconstrained inference variable.
41-
//
42-
// Why: For better cache hits, since if we have an unconstrained RHS then
43-
// there are only as many cache keys as there are (canonicalized) alias
44-
// types in each normalizes-to goal. This also weakens inference in a
45-
// forwards-compatible way so we don't use the value of the RHS term to
46-
// affect candidate assembly for projections.
47-
//
48-
// E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
49-
// `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
50-
// `U` and equate it with `u32`. This means that we don't need a separate
51-
// projection cache in the solver, since we're piggybacking off of regular
52-
// goal caching.
53-
if self.term_is_fully_unconstrained(goal) {
54-
let candidates = self.assemble_and_evaluate_candidates(goal);
55-
self.merge_candidates(candidates)
56-
} else {
57-
self.set_normalizes_to_hack_goal(goal);
58-
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
59-
}
43+
let candidates = self.assemble_and_evaluate_candidates(goal);
44+
self.merge_candidates(candidates)
6045
}
6146
ty::AssocItemContainer::ImplContainer => {
6247
self.normalize_inherent_associated_type(goal)

compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
1515
) -> QueryResult<'tcx> {
1616
let tcx = self.tcx();
1717
let weak_ty = goal.predicate.alias;
18-
let expected = goal.predicate.term.ty().expect("no such thing as a const alias");
19-
20-
let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
21-
self.eq(goal.param_env, expected, actual)?;
2218

2319
// Check where clauses
2420
self.add_goals(
@@ -30,6 +26,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
3026
.map(|pred| goal.with(tcx, pred)),
3127
);
3228

29+
let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
30+
self.instantiate_normalizes_to_term(goal, actual.into());
31+
3332
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
3433
}
3534
}

0 commit comments

Comments
 (0)