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
|
//! However, if `?fresh_var` ends up geteting equated to another type, we retry the
|
||||||
//! `NormalizesTo` goal, at which point the opaque is actually defined.
|
//! `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::query::NoSolution;
|
||||||
|
use rustc_infer::traits::solve::GoalSource;
|
||||||
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
|
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
|
|
||||||
@ -121,10 +122,11 @@ fn try_normalize_term(
|
|||||||
ty::TermKind::Const(_) => {
|
ty::TermKind::Const(_) => {
|
||||||
if let Some(alias) = term.to_alias_ty(self.tcx()) {
|
if let Some(alias) = term.to_alias_ty(self.tcx()) {
|
||||||
let term = self.next_term_infer_of_kind(term);
|
let term = self.next_term_infer_of_kind(term);
|
||||||
self.add_goal(
|
self.add_normalizes_to_goal(Goal::new(
|
||||||
GoalSource::Misc,
|
self.tcx(),
|
||||||
Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }),
|
param_env,
|
||||||
);
|
ty::NormalizesTo { alias, term },
|
||||||
|
));
|
||||||
self.try_evaluate_added_goals()?;
|
self.try_evaluate_added_goals()?;
|
||||||
Ok(Some(self.resolve_vars_if_possible(term)))
|
Ok(Some(self.resolve_vars_if_possible(term)))
|
||||||
} else {
|
} else {
|
||||||
@ -145,18 +147,25 @@ fn try_normalize_ty_recur(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ty::Alias(_, alias) = *ty.kind() else {
|
let ty::Alias(kind, alias) = *ty.kind() else {
|
||||||
return Some(ty);
|
return Some(ty);
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.commit_if_ok(|this| {
|
match self.commit_if_ok(|this| {
|
||||||
|
let tcx = this.tcx();
|
||||||
let normalized_ty = this.next_ty_infer();
|
let normalized_ty = this.next_ty_infer();
|
||||||
let normalizes_to_goal = Goal::new(
|
let normalizes_to = ty::NormalizesTo { alias, term: normalized_ty.into() };
|
||||||
this.tcx(),
|
match kind {
|
||||||
param_env,
|
ty::AliasKind::Opaque => {
|
||||||
ty::NormalizesTo { alias, term: normalized_ty.into() },
|
// HACK: Unlike for associated types, `normalizes-to` for opaques
|
||||||
);
|
// is currently not treated as a function. We do not erase the
|
||||||
this.add_goal(GoalSource::Misc, normalizes_to_goal);
|
// 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()?;
|
this.try_evaluate_added_goals()?;
|
||||||
Ok(this.resolve_vars_if_possible(normalized_ty))
|
Ok(this.resolve_vars_if_possible(normalized_ty))
|
||||||
}) {
|
}) {
|
||||||
|
@ -93,7 +93,7 @@ pub struct EvalCtxt<'a, 'tcx> {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct NestedGoals<'tcx> {
|
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
|
/// 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
|
/// a fresh inference variable, and only after evaluating that goal do we
|
||||||
/// equate the fresh inference variable with the actual RHS of the predicate.
|
/// 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
|
/// This is both to improve caching, and to avoid using the RHS of the
|
||||||
/// projection predicate to influence the normalizes-to candidate we select.
|
/// 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
|
/// Forgetting to replace the RHS with a fresh inference variable when we evaluate
|
||||||
/// with a fresh inference variable when we evaluate this goal. That can result
|
/// this goal results in an ICE..
|
||||||
/// in a trait solver cycle. This would currently result in overflow but can be
|
pub(super) normalizes_to_goals: Vec<Goal<'tcx, ty::NormalizesTo<'tcx>>>,
|
||||||
/// can be unsound with more powerful coinduction in the future.
|
|
||||||
pub(super) normalizes_to_hack_goal: Option<Goal<'tcx, ty::NormalizesTo<'tcx>>>,
|
|
||||||
/// The rest of the goals which have not yet processed or remain ambiguous.
|
/// The rest of the goals which have not yet processed or remain ambiguous.
|
||||||
pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>,
|
pub(super) goals: Vec<(GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> NestedGoals<'tcx> {
|
impl<'tcx> NestedGoals<'tcx> {
|
||||||
pub(super) fn new() -> Self {
|
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 {
|
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>) {
|
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)
|
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.
|
// If this loop did not result in any progress, what's our final certainty.
|
||||||
let mut unchanged_certainty = Some(Certainty::Yes);
|
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
|
// Replace the goal with an unconstrained infer var, so the
|
||||||
// RHS does not affect projection candidate assembly.
|
// RHS does not affect projection candidate assembly.
|
||||||
let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
|
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,
|
// looking at the "has changed" return from evaluate_goal,
|
||||||
// because we expect the `unconstrained_rhs` part of the predicate
|
// because we expect the `unconstrained_rhs` part of the predicate
|
||||||
// to have changed -- that means we actually normalized successfully!
|
// 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;
|
unchanged_certainty = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match certainty {
|
match certainty {
|
||||||
Certainty::Yes => {}
|
Certainty::Yes => {}
|
||||||
Certainty::Maybe(_) => {
|
Certainty::Maybe(_) => {
|
||||||
// We need to resolve vars here so that we correctly
|
self.nested_goals.normalizes_to_goals.push(with_resolved_vars);
|
||||||
// deal with `has_changed` in the next iteration.
|
|
||||||
self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal));
|
|
||||||
unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty));
|
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(
|
let (has_changed, certainty) = self.evaluate_goal(
|
||||||
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
|
GoalEvaluationKind::Nested { is_normalizes_to_hack: IsNormalizesToHack::No },
|
||||||
source,
|
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(
|
pub fn add_goal(
|
||||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
source: GoalSource,
|
source: GoalSource,
|
||||||
|
@ -202,12 +202,9 @@ fn compute_const_arg_has_type_goal(
|
|||||||
|
|
||||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
|
fn add_normalizes_to_goal(&mut self, goal: Goal<'tcx, ty::NormalizesTo<'tcx>>) {
|
||||||
assert!(
|
inspect::ProofTreeBuilder::add_normalizes_to_goal(self, goal);
|
||||||
self.nested_goals.normalizes_to_hack_goal.is_none(),
|
self.nested_goals.normalizes_to_goals.push(goal);
|
||||||
"attempted to set the projection eq hack goal when one already exists"
|
|
||||||
);
|
|
||||||
self.nested_goals.normalizes_to_hack_goal = Some(goal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
|
@ -16,7 +16,7 @@ pub(super) fn normalize_anon_const(
|
|||||||
.no_bound_vars()
|
.no_bound_vars()
|
||||||
.expect("const ty should not rely on other generics"),
|
.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)
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
} else {
|
} else {
|
||||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
|
||||||
|
@ -16,7 +16,6 @@ pub(super) fn normalize_inherent_associated_type(
|
|||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
let inherent = goal.predicate.alias;
|
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_def_id = tcx.parent(inherent.def_id);
|
||||||
let impl_args = self.fresh_args_for_item(impl_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
|
// Equate IAT with the RHS of the project goal
|
||||||
let inherent_args = inherent.rebase_inherent_args_onto_impl(impl_args, tcx);
|
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
|
// 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)),
|
.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)
|
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>>,
|
goal: Goal<'tcx, NormalizesTo<'tcx>>,
|
||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
let def_id = goal.predicate.def_id();
|
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) {
|
match self.tcx().def_kind(def_id) {
|
||||||
DefKind::AssocTy | DefKind::AssocConst => {
|
DefKind::AssocTy | DefKind::AssocConst => {
|
||||||
match self.tcx().associated_item(def_id).container {
|
match self.tcx().associated_item(def_id).container {
|
||||||
ty::AssocItemContainer::TraitContainer => {
|
ty::AssocItemContainer::TraitContainer => {
|
||||||
// To only compute normalization once for each projection we only
|
let candidates = self.assemble_and_evaluate_candidates(goal);
|
||||||
// assemble normalization candidates if the expected term is an
|
self.merge_candidates(candidates)
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ty::AssocItemContainer::ImplContainer => {
|
ty::AssocItemContainer::ImplContainer => {
|
||||||
self.normalize_inherent_associated_type(goal)
|
self.normalize_inherent_associated_type(goal)
|
||||||
|
@ -15,10 +15,6 @@ pub(super) fn normalize_weak_type(
|
|||||||
) -> QueryResult<'tcx> {
|
) -> QueryResult<'tcx> {
|
||||||
let tcx = self.tcx();
|
let tcx = self.tcx();
|
||||||
let weak_ty = goal.predicate.alias;
|
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
|
// Check where clauses
|
||||||
self.add_goals(
|
self.add_goals(
|
||||||
@ -30,6 +26,9 @@ pub(super) fn normalize_weak_type(
|
|||||||
.map(|pred| goal.with(tcx, pred)),
|
.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)
|
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user