move normalizes_to_hack
to AliasRelate
This commit is contained in:
parent
a42873e85b
commit
33c274f658
@ -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 @@ fn try_normalize_term(
|
||||
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 @@ fn try_normalize_ty_recur(
|
||||
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))
|
||||
}) {
|
||||
|
@ -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 @@ fn with_misc_source<'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 @@ fn with_misc_source<'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,
|
||||
|
@ -419,6 +419,17 @@ pub fn probe_kind(&mut self, probe_kind: inspect::ProbeKind<'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,
|
||||
|
@ -202,12 +202,9 @@ fn compute_const_arg_has_type_goal(
|
||||
|
||||
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))]
|
||||
|
@ -16,7 +16,7 @@ pub(super) fn normalize_anon_const(
|
||||
.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)
|
||||
|
@ -16,7 +16,6 @@ pub(super) fn normalize_inherent_associated_type(
|
||||
) -> 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 @@ pub(super) fn normalize_inherent_associated_type(
|
||||
|
||||
// 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 @@ pub(super) fn normalize_inherent_associated_type(
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
@ -31,32 +31,17 @@ pub(super) fn compute_normalizes_to_goal(
|
||||
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)
|
||||
|
@ -15,10 +15,6 @@ pub(super) fn normalize_weak_type(
|
||||
) -> 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 @@ pub(super) fn normalize_weak_type(
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user