Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 @@ -13,10 +13,16 @@ where
#[instrument(level = "trace", skip(self), ret)]
pub(super) fn normalize_anon_const(
&mut self,
goal: Goal<I, ty::NormalizesTo<I>>,
def_id: I::UnevaluatedConstId,
goal: Goal<I, ty::NormalizesTo<I, I::UnevaluatedConstId>>,
) -> QueryResultOrRerunNonErased<I> {
let uv = goal.predicate.alias.expect_ct();
let cx = self.cx();
let uv = ty::UnevaluatedConst::new(
cx,
ty::UnevaluatedConstKind::Anon { def_id: goal.predicate.alias.kind },
goal.predicate.alias.args,
);
let alias = ty::AliasTerm::from(uv);
let goal = goal.with(cx, ty::NormalizesTo { alias, term: goal.predicate.term });

@khyperia khyperia Jun 10, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm extremely on board with the style of thing that lcnr said with

with this we can now update NormalizesTo to also be generic over the kind, and e.g. for normalize_anon_const have the argument be Goal<I, ty::NormalizesTo<I, I::UnevaluatedConstId>>

there's a lot of places in the code that could hugely benefit from this (e.g. there's a large number of unwraps in #157653 that could be avoided), but this particular case with normalize_anon_const is a bit gross, and I would prefer keeping this as NormalizesTo<I, K = AliasTermKind<I>> (at least until additional refactorings happen that would e.g. allow evaluate_const_and_instantiate_normalizes_to_term to be more generic)

(this is just my code style opinion though, up to you!)


edit: just want to clarify, that PR has a lot of unwraps of the form:

let def_id = match inherent.kind {
    ty::AliasTermKind::InherentTy { def_id } => def_id.into(),
    ty::AliasTermKind::InherentConst { def_id } => def_id.into(),
    kind => panic!("expected inherent alias, found {kind:?}"),
};

having the Alias have a more specific Kind in all the cases that PR does that style of unwrap (it's so common that projections already have a helper method for it, expect_projection_def_id) would be super nice, but yeah

self.evaluate_const_and_instantiate_normalizes_to_term(goal, uv)
}
}
18 changes: 12 additions & 6 deletions compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rustc_type_ir::solve::{
RerunNonErased, RerunReason, RerunResultExt,
};
use rustc_type_ir::{
self as ty, FieldInfo, Interner, NormalizesTo, PredicateKind, Unnormalized, Upcast as _,
self as ty, Alias, FieldInfo, Interner, NormalizesTo, PredicateKind, Unnormalized, Upcast as _,
};
use tracing::instrument;

Expand Down Expand Up @@ -50,7 +50,13 @@ where
self.normalize_free_alias(goal).map_err(Into::into)
}
ty::AliasTermKind::AnonConst { def_id } => {
self.normalize_anon_const(goal, def_id).map_err(Into::into)
let cx = self.cx();
let alias =
Alias::new_unevaluated_const_from_args(cx, def_id, goal.predicate.alias.args);
self.normalize_anon_const(
goal.with(cx, ty::NormalizesTo { alias, term: goal.predicate.term }),
)
.map_err(Into::into)
}
}
}
Expand Down Expand Up @@ -121,9 +127,9 @@ where
/// We know `term` to always be a fully unconstrained inference variable, so
/// `eq` should never fail here. However, in case `term` contains aliases, we
/// emit nested `AliasRelate` goals to structurally normalize the alias.
pub fn instantiate_normalizes_to_term(
pub fn instantiate_normalizes_to_term<K>(
&mut self,
goal: Goal<I, NormalizesTo<I>>,
goal: Goal<I, NormalizesTo<I, K>>,
term: I::Term,
) {
self.eq(goal.param_env, goal.predicate.term, term)
Expand All @@ -132,9 +138,9 @@ where

/// Unlike `instantiate_normalizes_to_term` this instantiates the expected term
/// with a rigid alias. Using this is pretty much always wrong.
pub fn structurally_instantiate_normalizes_to_term(
pub fn structurally_instantiate_normalizes_to_term<K>(
&mut self,
goal: Goal<I, NormalizesTo<I>>,
goal: Goal<I, NormalizesTo<I, K>>,
term: ty::AliasTerm<I>,
) {
self.relate_rigid_alias_non_alias(goal.param_env, term, ty::Invariant, goal.predicate.term)
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_type_ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ mod pattern;
mod predicate;
mod predicate_kind;
mod region_kind;
mod ty;
mod ty_info;
mod ty_kind;
mod unnormalized;
Expand Down Expand Up @@ -80,6 +81,7 @@ pub use predicate_kind::*;
pub use region_kind::*;
pub use rustc_ast_ir::{FloatTy, IntTy, Movability, Mutability, Pinnedness, UintTy};
use rustc_type_ir_macros::GenericTypeVisitable;
pub use ty::{Alias, AliasTerm, AliasTy};
pub use ty_info::*;
pub use ty_kind::*;
pub use unnormalized::Unnormalized;
Expand Down
89 changes: 33 additions & 56 deletions compiler/rustc_type_ir/src/predicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ use rustc_type_ir_macros::{
GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic,
};

use crate::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder};
use crate::inherent::*;
use crate::ty::AliasTerm;
use crate::upcast::{Upcast, UpcastFrom};
use crate::visit::TypeVisitableExt as _;
use crate::{self as ty, AliasTyKind, Interner, UnevaluatedConstKind};
use crate::visit::{TypeVisitable, TypeVisitableExt as _, TypeVisitor};
use crate::{self as ty, Alias, AliasTyKind, Interner, UnevaluatedConstKind, try_visit};

/// `A: 'region`
#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner, A)]
Expand Down Expand Up @@ -544,7 +546,7 @@ impl<I: Interner> ty::Binder<I, ExistentialProjection<I>> {
}

#[derive_where(Clone, Copy, PartialEq, Eq, Hash, Debug; I: Interner)]
#[derive(Lift_Generic, GenericTypeVisitable)]
#[derive(TypeVisitable_Generic, TypeFoldable_Generic, Lift_Generic, GenericTypeVisitable)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext)
Expand Down Expand Up @@ -661,49 +663,14 @@ impl<I: Interner> From<ty::UnevaluatedConstKind<I>> for AliasTermKind<I> {
}
}

/// Represents the unprojected term of a projection goal.
///
/// * For a projection, this would be `<Ty as Trait<...>>::N<...>`.
/// * For an inherent projection, this would be `Ty::N<...>`.
/// * For an opaque type, there is no explicit syntax.
#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)]
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)]
#[cfg_attr(
feature = "nightly",
derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext)
)]
pub struct AliasTerm<I: Interner> {
/// The parameters of the associated or opaque item.
///
/// For a projection, these are the generic parameters for the trait and the
/// GAT parameters, if there are any.
///
/// For an inherent projection, they consist of the self type and the GAT parameters,
/// if there are any.
///
/// For RPIT the generic parameters are for the generics of the function,
/// while for TAIT it is used for the generic parameters of the alias.
pub args: I::GenericArgs,

#[type_foldable(identity)]
#[type_visitable(ignore)]
pub kind: AliasTermKind<I>,

/// This field exists to prevent the creation of `AliasTerm` without using [`AliasTerm::new_from_args`].
#[derive_where(skip(Debug))]
_use_alias_term_new_instead: (),
}

impl<I: Interner> Eq for AliasTerm<I> {}

impl<I: Interner> AliasTerm<I> {
pub fn new_from_args(
interner: I,
kind: AliasTermKind<I>,
args: I::GenericArgs,
) -> AliasTerm<I> {
interner.debug_assert_args_compatible(kind.def_id(), args);
AliasTerm { kind, args, _use_alias_term_new_instead: () }
AliasTerm { kind, args, _use_alias_new_instead: () }
}

pub fn new(
Expand Down Expand Up @@ -733,7 +700,7 @@ impl<I: Interner> AliasTerm<I> {
panic!("Cannot turn `{}` into `AliasTy`", kind.descr())
}
};
ty::AliasTy { kind, args: self.args, _use_alias_ty_new_instead: () }
ty::AliasTy { kind, args: self.args, _use_alias_new_instead: () }
}

pub fn expect_ct(self) -> ty::UnevaluatedConst<I> {
Expand Down Expand Up @@ -884,21 +851,13 @@ impl<I: Interner> AliasTerm<I> {

impl<I: Interner> From<ty::AliasTy<I>> for AliasTerm<I> {
fn from(ty: ty::AliasTy<I>) -> Self {
AliasTerm {
args: ty.args,
kind: AliasTermKind::from(ty.kind),
_use_alias_term_new_instead: (),
}
AliasTerm { args: ty.args, kind: AliasTermKind::from(ty.kind), _use_alias_new_instead: () }
}
}

impl<I: Interner> From<ty::UnevaluatedConst<I>> for AliasTerm<I> {
fn from(ty: ty::UnevaluatedConst<I>) -> Self {
AliasTerm {
args: ty.args,
kind: AliasTermKind::from(ty.kind),
_use_alias_term_new_instead: (),
}
AliasTerm { args: ty.args, kind: AliasTermKind::from(ty.kind), _use_alias_new_instead: () }
}
}

Expand Down Expand Up @@ -977,18 +936,36 @@ impl<I: Interner> fmt::Debug for ProjectionPredicate<I> {

/// Used by the new solver to normalize an alias. This always expects the `term` to
/// be an unconstrained inference variable which is used as the output.
#[derive_where(Clone, Copy, Hash, PartialEq; I: Interner)]
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)]
#[derive_where(Clone, Copy, Hash, Eq, PartialEq; I: Interner, K)]
#[derive(GenericTypeVisitable, Lift_Generic)]
#[cfg_attr(
feature = "nightly",
derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext)
)]
pub struct NormalizesTo<I: Interner> {
pub alias: AliasTerm<I>,
pub struct NormalizesTo<I: Interner, K = AliasTermKind<I>> {
pub alias: Alias<I, K>,
pub term: I::Term,
}

impl<I: Interner> Eq for NormalizesTo<I> {}
impl<I: Interner, K: TypeVisitable<I>> TypeVisitable<I> for NormalizesTo<I, K> {
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
try_visit!(self.alias.visit_with(visitor));
self.term.visit_with(visitor)
}
}

impl<I: Interner, K: TypeFoldable<I>> TypeFoldable<I> for NormalizesTo<I, K> {
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
Ok(NormalizesTo {
alias: self.alias.try_fold_with(folder)?,
term: self.term.try_fold_with(folder)?,
})
}

fn fold_with<F: TypeFolder<I>>(self, folder: &mut F) -> Self {
NormalizesTo { alias: self.alias.fold_with(folder), term: self.term.fold_with(folder) }
}
}

@lcnr lcnr Jun 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do the derives not work?

View changes since the review

@Jamesbarford Jamesbarford Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In essence a similar problem that hampered Lift_Generic and the work needs to be done to get it working is similar to what we did for Lift_Generic, I can open another PR for this?


impl<I: Interner> NormalizesTo<I> {
pub fn self_ty(self) -> I::Ty {
Expand All @@ -1008,7 +985,7 @@ impl<I: Interner> NormalizesTo<I> {
}
}

impl<I: Interner> fmt::Debug for NormalizesTo<I> {
impl<I: Interner, K: fmt::Debug> fmt::Debug for NormalizesTo<I, K> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NormalizesTo({:?}, {:?})", self.alias, self.term)
}
Expand Down
66 changes: 66 additions & 0 deletions compiler/rustc_type_ir/src/ty/alias.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use derive_where::derive_where;
#[cfg(feature = "nightly")]
use rustc_macros::{Decodable_NoContext, Encodable_NoContext, StableHash_NoContext};
use rustc_type_ir_macros::{
GenericTypeVisitable, Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic,
};

use crate::Interner;
use crate::predicate::AliasTermKind;
use crate::ty_kind::AliasTyKind;

/// Represents an alias of a type, constant, or other term-like item.
///
/// * For a projection, this would be `<Ty as Trait<...>>::N<...>`.
/// * For an inherent projection, this would be `Ty::N<...>`.
/// * For an opaque type, there is no explicit syntax.
#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner, K)]
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)]
#[cfg_attr(
feature = "nightly",
derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext)
)]
pub struct Alias<I: Interner, K> {
/// The parameters of the associated, opaque, or constant alias.
///
/// For a projection, these are the generic parameters for the trait and the
/// GAT parameters, if there are any.
///
/// For an inherent projection, they consist of the self type and the GAT parameters,
/// if there are any.
///
/// For RPIT the generic parameters are for the generics of the function,
/// while for TAIT it is used for the generic parameters of the alias.
pub args: I::GenericArgs,

pub kind: K,
Comment on lines +24 to +36

@lcnr lcnr Jun 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The parameters of the associated, opaque, or constant alias.
///
/// For a projection, these are the generic parameters for the trait and the
/// GAT parameters, if there are any.
///
/// For an inherent projection, they consist of the self type and the GAT parameters,
/// if there are any.
///
/// For RPIT the generic parameters are for the generics of the function,
/// while for TAIT it is used for the generic parameters of the alias.
pub args: I::GenericArgs,
pub kind: K,
pub kind: K,
/// The parameters of the associated, opaque, or constant alias.
///
/// For a projection, these are the generic parameters for the trait and the
/// GAT parameters, if there are any.
///
/// For an inherent projection, they consist of the self type and the GAT parameters,
/// if there are any.
///
/// For RPIT the generic parameters are for the generics of the function,
/// while for TAIT it is used for the generic parameters of the alias.
pub args: I::GenericArgs,

personal style preference

View changes since the review


/// This field exists to prevent the creation of `Alias` without using the relevant constructor.
#[derive_where(skip(Debug))]
#[type_visitable(ignore)]
#[type_foldable(identity)]
#[lift(identity)]
pub(crate) _use_alias_new_instead: (),
}

impl<I: Interner, K: PartialEq> Eq for Alias<I, K> {}

impl<I: Interner, K: Copy> Alias<I, K> {
pub fn kind(self, _interner: I) -> K {
self.kind
}
}

impl<I: Interner> Alias<I, I::UnevaluatedConstId> {
pub fn new_unevaluated_const_from_args(
interner: I,
kind: I::UnevaluatedConstId,
args: I::GenericArgs,
) -> Self {
interner.debug_assert_args_compatible(kind.into(), args);
Alias { kind, args, _use_alias_new_instead: () }
}
}

@lcnr lcnr Jun 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, could we change this to

trait AliasKind<I: Interner>: Copy {
    fn assert_args_compatible(self, cx: I, args: I::GenericArgs);

    #[inline]
    fn debug_assert_args_compatible(self, cx: I, args: I::GenericArgs) {
        if cfg!(debug_assertions) { self.assert... }
    }
}

impl<I: Interner, K: AliasKind> Alias<I, K> {
    pub fn new(
        cx: I,
        kind: K,
        args: I::GenericArgs,
    ) -> Self {
        kind.debug_assert_args_compatible(interner, args);
        Alias { kind, args, _use_alias_new_instead: () }
    }

    pub fn new_from_iter(
        cx: I,
        kind: K,
        args: impl IntoIterator<...>,
    ) -> Self { ... }
}

View changes since the review


pub type AliasTerm<I> = Alias<I, AliasTermKind<I>>;
pub type AliasTy<I> = Alias<I, AliasTyKind<I>>;
3 changes: 3 additions & 0 deletions compiler/rustc_type_ir/src/ty/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod alias;

pub use alias::{Alias, AliasTerm, AliasTy};
53 changes: 16 additions & 37 deletions compiler/rustc_type_ir/src/ty_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ use rustc_type_ir_macros::{
use self::TyKind::*;
pub use self::closure::*;
use crate::inherent::*;
use crate::ty::AliasTy;
#[cfg(feature = "nightly")]
use crate::visit::TypeVisitable;
use crate::{self as ty, BoundVarIndexKind, FloatTy, IntTy, Interner, UintTy};

mod closure;

#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)]
#[derive(GenericTypeVisitable, Lift_Generic)]
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)]
#[cfg_attr(
feature = "nightly",
derive(Encodable_NoContext, Decodable_NoContext, StableHash_NoContext)
Expand Down Expand Up @@ -431,45 +432,10 @@ impl<I: Interner> fmt::Debug for TyKind<I> {
}
}

/// Represents the projection of an associated, opaque, or lazy-type-alias type.
///
/// * For a projection, this would be `<Ty as Trait<...>>::N<...>`.
/// * For an inherent projection, this would be `Ty::N<...>`.
/// * For an opaque type, there is no explicit syntax.
#[derive_where(Clone, Copy, Hash, PartialEq, Debug; I: Interner)]
#[derive(TypeVisitable_Generic, GenericTypeVisitable, TypeFoldable_Generic, Lift_Generic)]
#[cfg_attr(
feature = "nightly",
derive(Decodable_NoContext, Encodable_NoContext, StableHash_NoContext)
)]
pub struct AliasTy<I: Interner> {
/// The parameters of the associated or opaque type.
///
/// For a projection, these are the generic parameters for the trait and the
/// GAT parameters, if there are any.
///
/// For an inherent projection, they consist of the self type and the GAT parameters,
/// if there are any.
///
/// For RPIT the generic parameters are for the generics of the function,
/// while for TAIT it is used for the generic parameters of the alias.
pub args: I::GenericArgs,

#[type_foldable(identity)]
#[type_visitable(ignore)]
pub kind: AliasTyKind<I>,

/// This field exists to prevent the creation of `AliasTy` without using [`AliasTy::new_from_args`].
#[derive_where(skip(Debug))]
pub(crate) _use_alias_ty_new_instead: (),
}

impl<I: Interner> Eq for AliasTy<I> {}

impl<I: Interner> AliasTy<I> {
pub fn new_from_args(interner: I, kind: AliasTyKind<I>, args: I::GenericArgs) -> AliasTy<I> {
interner.debug_assert_args_compatible(kind.def_id(), args);
AliasTy { kind, args, _use_alias_ty_new_instead: () }
AliasTy { kind, args, _use_alias_new_instead: () }
}

pub fn new(
Expand All @@ -481,6 +447,19 @@ impl<I: Interner> AliasTy<I> {
Self::new_from_args(interner, kind, args)
}

pub fn new_from_def_id(interner: I, def_id: I::DefId, args: I::GenericArgs) -> AliasTy<I> {
let kind = interner.alias_ty_kind_from_def_id(def_id);
Self::new_from_args(interner, kind, args)
}

pub fn def_id(self) -> I::DefId {
self.kind.def_id()
}

pub fn with_args(self, interner: I, args: I::GenericArgs) -> Self {
Self::new_from_args(interner, self.kind, args)
}

/// Whether this alias type is an opaque.
pub fn is_opaque(self) -> bool {
matches!(self.kind, AliasTyKind::Opaque { .. })
Expand Down
Loading
Loading