Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

split NormalizesTo out of Projection 2 #116262

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ fn trait_predicate_kind<'tcx>(
| ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(_))
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(_))
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
| ty::PredicateKind::NormalizesTo(..)
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_))
| ty::PredicateKind::Subtype(_)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
| ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..))
| ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
| ty::PredicateKind::ObjectSafe(..)
| ty::PredicateKind::NormalizesTo(..)
| ty::PredicateKind::AliasRelate(..)
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
| ty::PredicateKind::ConstEquate(..)
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_infer/src/traits/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,9 @@ impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => {
// Nothing to elaborate in a projection predicate.
}
ty::PredicateKind::NormalizesTo(..) => {
// Nothing to elabroate in normalizes-to predicates.
lcnr marked this conversation as resolved.
Show resolved Hide resolved
}
ty::PredicateKind::ClosureKind(..) => {
// Nothing to elaborate when waiting for a closure's kind to be inferred.
}
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_middle/src/ty/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,11 +289,15 @@ impl FlagComputation {
self.add_const(expected);
self.add_const(found);
}
ty::PredicateKind::Ambiguous => {}
ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term }) => {
self.add_alias_ty(alias);
self.add_term(term);
}
ty::PredicateKind::AliasRelate(t1, t2, _) => {
self.add_term(t1);
self.add_term(t2);
}
ty::PredicateKind::Ambiguous => {}
}
}

Expand Down
54 changes: 52 additions & 2 deletions compiler/rustc_middle/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,12 @@ impl<'tcx> Predicate<'tcx> {
#[inline]
pub fn allow_normalization(self) -> bool {
match self.kind().skip_binder() {
PredicateKind::Clause(ClauseKind::WellFormed(_)) => false,
// `NormalizesTo` is only used in the new solver, so this shouldn't
// matter. Normalizing `term` would be 'wrong' however, as it changes whether
// `normalizes-to(<T as Trait>::Assoc, <T as Trait>::Assoc)` holds.
PredicateKind::Clause(ClauseKind::WellFormed(_)) | PredicateKind::NormalizesTo(_) => {
false
}
PredicateKind::Clause(ClauseKind::Trait(_))
| PredicateKind::Clause(ClauseKind::RegionOutlives(_))
| PredicateKind::Clause(ClauseKind::TypeOutlives(_))
Expand Down Expand Up @@ -700,10 +705,19 @@ pub enum PredicateKind<'tcx> {
/// Used for coherence to mark opaque types as possibly equal to each other but ambiguous.
Ambiguous,

/// The alias normalizes to `term`. Unlike `Projection`, this always fails if the alias
/// cannot be normalized in the current context.
///
/// `Projection(<T as Trait>::Assoc, ?x)` results in `?x == <T as Trait>::Assoc` while
/// `NormalizesTo(<T as Trait>::Assoc, ?x)` results in `NoSolution`.
///
/// Only used in the new solver.
NormalizesTo(NormalizesTo<'tcx>),

/// Separate from `ClauseKind::Projection` which is used for normalization in new solver.
/// This predicate requires two terms to be equal to eachother.
///
/// Only used for new solver
/// Only used for the new solver and for opaque type inference.
AliasRelate(Term<'tcx>, Term<'tcx>, AliasRelationDirection),
}

Expand Down Expand Up @@ -1179,6 +1193,33 @@ impl<'tcx> PolyProjectionPredicate<'tcx> {
}
}

/// Used by the new solver. Unlike a `ProjectionPredicate` this can only be
/// proven by actually normalizing `alias`.
#[derive(Copy, Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
pub struct NormalizesTo<'tcx> {
pub alias: AliasTy<'tcx>,
pub term: Term<'tcx>,
}

impl<'tcx> NormalizesTo<'tcx> {
pub fn self_ty(self) -> Ty<'tcx> {
self.alias.self_ty()
}

pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> NormalizesTo<'tcx> {
Self { alias: self.alias.with_self_ty(tcx, self_ty), ..self }
}

pub fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
self.alias.trait_def_id(tcx)
}

pub fn def_id(self) -> DefId {
self.alias.def_id
}
}

pub trait ToPolyTraitRef<'tcx> {
fn to_poly_trait_ref(&self) -> PolyTraitRef<'tcx>;
}
Expand Down Expand Up @@ -1340,6 +1381,12 @@ impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for ProjectionPredicate<'tcx> {
}
}

impl<'tcx> ToPredicate<'tcx> for NormalizesTo<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
PredicateKind::NormalizesTo(self).to_predicate(tcx)
}
}

impl<'tcx> ToPredicate<'tcx> for TraitPredicate<'tcx> {
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
PredicateKind::Clause(ClauseKind::Trait(self)).to_predicate(tcx)
Expand All @@ -1353,6 +1400,7 @@ impl<'tcx> Predicate<'tcx> {
PredicateKind::Clause(ClauseKind::Trait(t)) => Some(predicate.rebind(t)),
PredicateKind::Clause(ClauseKind::Projection(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
Expand All @@ -1373,6 +1421,7 @@ impl<'tcx> Predicate<'tcx> {
PredicateKind::Clause(ClauseKind::Projection(t)) => Some(predicate.rebind(t)),
PredicateKind::Clause(ClauseKind::Trait(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
Expand All @@ -1394,6 +1443,7 @@ impl<'tcx> Predicate<'tcx> {
PredicateKind::Clause(ClauseKind::Trait(..))
| PredicateKind::Clause(ClauseKind::ConstArgHasType(..))
| PredicateKind::Clause(ClauseKind::Projection(..))
| PredicateKind::NormalizesTo(..)
| PredicateKind::AliasRelate(..)
| PredicateKind::Subtype(..)
| PredicateKind::Coerce(..)
Expand Down
7 changes: 6 additions & 1 deletion compiler/rustc_middle/src/ty/print/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2873,6 +2873,10 @@ define_print_and_forward_display! {
p!(print(self.term))
}

ty::NormalizesTo<'tcx> {
p!(print(self.alias), " normalizes-to ", print(self.term))
}

ty::Term<'tcx> {
match self.unpack() {
ty::TermKind::Ty(ty) => p!(print(ty)),
Expand Down Expand Up @@ -2937,8 +2941,9 @@ define_print_and_forward_display! {
ty::PredicateKind::ConstEquate(c1, c2) => {
p!("the constant `", print(c1), "` equals `", print(c2), "`")
}
ty::PredicateKind::Ambiguous => p!("ambiguous"),
ty::PredicateKind::NormalizesTo(predicate) => p!(print(predicate)),
ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)),
ty::PredicateKind::Ambiguous => p!("ambiguous"),
}
}

Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_middle/src/ty/structural_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ impl<'tcx> fmt::Debug for ty::ProjectionPredicate<'tcx> {
}
}

impl<'tcx> fmt::Debug for ty::NormalizesTo<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NormalizesTo({:?}, {:?})", self.alias, self.term)
}
}

impl<'tcx> fmt::Debug for ty::Predicate<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.kind())
Expand Down Expand Up @@ -229,6 +235,7 @@ impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> {
}
ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({c1:?}, {c2:?})"),
ty::PredicateKind::Ambiguous => write!(f, "Ambiguous"),
ty::PredicateKind::NormalizesTo(predicate) => predicate.fmt(f),
ty::PredicateKind::AliasRelate(t1, t2, dir) => {
write!(f, "AliasRelate({t1:?}, {dir:?}, {t2:?})")
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_smir/src/rustc_smir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1359,6 +1359,7 @@ impl<'tcx> Stable<'tcx> for ty::PredicateKind<'tcx> {
stable_mir::ty::PredicateKind::ConstEquate(a.stable(tables), b.stable(tables))
}
PredicateKind::Ambiguous => stable_mir::ty::PredicateKind::Ambiguous,
PredicateKind::NormalizesTo(..) => unimplemented!(),
PredicateKind::AliasRelate(a, b, alias_relation_direction) => {
stable_mir::ty::PredicateKind::AliasRelate(
a.unpack().stable(tables),
Expand Down
8 changes: 2 additions & 6 deletions compiler/rustc_trait_selection/src/solve/alias_relate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
direction: ty::AliasRelationDirection,
invert: Invert,
) -> Result<(), NoSolution> {
let other = match direction {
let term = match direction {
// This is purely an optimization. No need to instantiate a new
// infer var and equate the RHS to it.
ty::AliasRelationDirection::Equate => other,
Expand All @@ -159,11 +159,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
fresh
}
};
self.add_goal(Goal::new(
self.tcx(),
param_env,
ty::ProjectionPredicate { projection_ty: alias, term: other },
));
self.add_goal(Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }));

Ok(())
}
Expand Down
8 changes: 3 additions & 5 deletions compiler/rustc_trait_selection/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,15 +339,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
num_steps: usize,
) {
let tcx = self.tcx();
let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return };
let &ty::Alias(_, alias) = goal.predicate.self_ty().kind() else { return };

candidates.extend(self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
if num_steps < ecx.local_overflow_limit() {
let normalized_ty = ecx.next_ty_infer();
let normalizes_to_goal = goal.with(
tcx,
ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() },
);
let normalizes_to_goal =
goal.with(tcx, ty::NormalizesTo { alias, term: normalized_ty.into() });
ecx.add_goal(normalizes_to_goal);
if let Err(NoSolution) = ecx.try_evaluate_added_goals() {
debug!("self type normalization failed");
Expand Down
18 changes: 8 additions & 10 deletions compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub(super) struct NestedGoals<'tcx> {
/// 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::ProjectionPredicate<'tcx>>>,
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.
pub(super) goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
}
Expand Down Expand Up @@ -461,6 +461,9 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
ty::PredicateKind::Clause(ty::ClauseKind::Projection(predicate)) => {
self.compute_projection_goal(Goal { param_env, predicate })
}
ty::PredicateKind::NormalizesTo(predicate) => {
self.compute_normalizes_to_goal(Goal { param_env, predicate })
}
ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(predicate)) => {
self.compute_type_outlives_goal(Goal { param_env, predicate })
}
Expand Down Expand Up @@ -559,10 +562,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term);
let unconstrained_goal = goal.with(
tcx,
ty::ProjectionPredicate {
projection_ty: goal.predicate.projection_ty,
term: unconstrained_rhs,
},
ty::NormalizesTo { alias: goal.predicate.alias, term: unconstrained_rhs },
);

let (_, certainty, instantiate_goals) = self.evaluate_goal(
Expand All @@ -584,9 +584,7 @@ impl<'a, 'tcx> EvalCtxt<'a, '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.projection_ty
!= self.resolve_vars_if_possible(goal.predicate.projection_ty)
{
if goal.predicate.alias != self.resolve_vars_if_possible(goal.predicate.alias) {
unchanged_certainty = None;
}

Expand Down Expand Up @@ -658,7 +656,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
/// and does not occur in any other part of the predicate.
pub(super) fn term_is_fully_unconstrained(
&self,
goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
) -> bool {
let term_is_infer = match goal.predicate.term.unpack() {
ty::TermKind::Ty(ty) => {
Expand Down Expand Up @@ -722,7 +720,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
let mut visitor = ContainsTerm { infcx: self.infcx, term: goal.predicate.term };

term_is_infer
&& goal.predicate.projection_ty.visit_with(&mut visitor).is_continue()
&& goal.predicate.alias.visit_with(&mut visitor).is_continue()
&& goal.param_env.visit_with(&mut visitor).is_continue()
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_trait_selection/src/solve/fulfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
errors.push(FulfillmentError {
obligation: obligation.clone(),
code: match goal.predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::ClauseKind::Projection(_)) => {
ty::PredicateKind::Clause(ty::ClauseKind::Projection(_))
| ty::PredicateKind::NormalizesTo(_) => {
FulfillmentErrorCode::CodeProjectionError(
// FIXME: This could be a `Sorts` if the term is a type
MismatchedProjectionTypes { err: TypeError::Mismatch },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ use super::EvalCtxt;
impl<'tcx> EvalCtxt<'_, 'tcx> {
pub(super) fn normalize_inherent_associated_type(
&mut self,
goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>,
goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
) -> QueryResult<'tcx> {
let tcx = self.tcx();
let inherent = goal.predicate.projection_ty;
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);
Expand Down
7 changes: 4 additions & 3 deletions compiler/rustc_trait_selection/src/solve/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ mod fulfill;
mod inherent_projection;
pub mod inspect;
mod normalize;
mod normalizes_to;
mod opaques;
mod project_goals;
mod search_graph;
Expand Down Expand Up @@ -225,7 +226,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {

impl<'tcx> EvalCtxt<'_, 'tcx> {
#[instrument(level = "debug", skip(self))]
fn set_normalizes_to_hack_goal(&mut self, goal: Goal<'tcx, ty::ProjectionPredicate<'tcx>>) {
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"
Expand Down Expand Up @@ -305,15 +306,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
mut ty: Ty<'tcx>,
) -> Result<Option<Ty<'tcx>>, NoSolution> {
for _ in 0..self.local_overflow_limit() {
let ty::Alias(_, projection_ty) = *ty.kind() else {
let ty::Alias(_, alias) = *ty.kind() else {
return Ok(Some(ty));
};

let normalized_ty = self.next_ty_infer();
let normalizes_to_goal = Goal::new(
self.tcx(),
param_env,
ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() },
ty::NormalizesTo { alias, term: normalized_ty.into() },
);
self.add_goal(normalizes_to_goal);
self.try_evaluate_added_goals()?;
Expand Down
7 changes: 2 additions & 5 deletions compiler/rustc_trait_selection/src/solve/normalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
tcx,
self.at.cause.clone(),
self.at.param_env,
ty::ProjectionPredicate { projection_ty: alias, term: new_infer_ty.into() },
ty::NormalizesTo { alias, term: new_infer_ty.into() },
);

// Do not emit an error if normalization is known to fail but instead
Expand Down Expand Up @@ -128,10 +128,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
tcx,
self.at.cause.clone(),
self.at.param_env,
ty::ProjectionPredicate {
projection_ty: tcx.mk_alias_ty(uv.def, uv.args),
term: new_infer_ct.into(),
},
ty::NormalizesTo { alias: tcx.mk_alias_ty(uv.def, uv.args), term: new_infer_ct.into() },
);

let result = if infcx.predicate_may_hold(&obligation) {
Expand Down
Loading
Loading