about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBoxy <rust@boxyuwu.dev>2025-05-01 13:26:23 +0100
committerBoxy <rust@boxyuwu.dev>2025-05-01 20:05:02 +0100
commit238d113b0726b9734ec42737cbc461d08520bf35 (patch)
tree9de7a1b49d6267428d9e4b83af526ff8f933d567
parent53e3907bcb5a4519a6558051476c3198aba2a12c (diff)
downloadrust-238d113b0726b9734ec42737cbc461d08520bf35.tar.gz
rust-238d113b0726b9734ec42737cbc461d08520bf35.zip
Set groundwork for proper const normalization
-rw-r--r--compiler/rustc_infer/src/infer/projection.rs25
-rw-r--r--compiler/rustc_middle/src/ty/context.rs12
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs5
-rw-r--r--compiler/rustc_middle/src/ty/util.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs16
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs13
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/normalize.rs196
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs170
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs20
-rw-r--r--compiler/rustc_traits/src/normalize_projection_ty.rs14
-rw-r--r--compiler/rustc_type_ir/src/predicate.rs70
-rw-r--r--compiler/rustc_type_ir/src/relate.rs2
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs22
-rw-r--r--tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr15
18 files changed, 369 insertions, 228 deletions
diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs
index 1bee9632110..2a4f9db8963 100644
--- a/compiler/rustc_infer/src/infer/projection.rs
+++ b/compiler/rustc_infer/src/infer/projection.rs
@@ -1,7 +1,8 @@
 use rustc_middle::traits::ObligationCause;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty;
 
 use super::InferCtxt;
+use crate::infer::Term;
 use crate::traits::{Obligation, PredicateObligations};
 
 impl<'tcx> InferCtxt<'tcx> {
@@ -11,24 +12,32 @@ impl<'tcx> InferCtxt<'tcx> {
     /// of the given projection. This allows us to proceed with projections
     /// while they cannot be resolved yet due to missing information or
     /// simply due to the lack of access to the trait resolution machinery.
-    pub fn projection_ty_to_infer(
+    pub fn projection_term_to_infer(
         &self,
         param_env: ty::ParamEnv<'tcx>,
-        projection_ty: ty::AliasTy<'tcx>,
+        alias_term: ty::AliasTerm<'tcx>,
         cause: ObligationCause<'tcx>,
         recursion_depth: usize,
         obligations: &mut PredicateObligations<'tcx>,
-    ) -> Ty<'tcx> {
+    ) -> Term<'tcx> {
         debug_assert!(!self.next_trait_solver());
-        let ty_var = self.next_ty_var(self.tcx.def_span(projection_ty.def_id));
+
+        let span = self.tcx.def_span(alias_term.def_id);
+        let infer_var = if alias_term.kind(self.tcx).is_type() {
+            self.next_ty_var(span).into()
+        } else {
+            self.next_const_var(span).into()
+        };
+
         let projection =
             ty::PredicateKind::Clause(ty::ClauseKind::Projection(ty::ProjectionPredicate {
-                projection_term: projection_ty.into(),
-                term: ty_var.into(),
+                projection_term: alias_term,
+                term: infer_var,
             }));
         let obligation =
             Obligation::with_depth(self.tcx, cause, recursion_depth, param_env, projection);
         obligations.push(obligation);
-        ty_var
+
+        infer_var
     }
 }
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 3ea285d3d8e..0f7f8527088 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -243,10 +243,18 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
                     ty::AliasTermKind::ProjectionTy
                 }
             }
+            DefKind::AssocConst => {
+                if let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(alias.def_id))
+                {
+                    ty::AliasTermKind::InherentConst
+                } else {
+                    ty::AliasTermKind::ProjectionConst
+                }
+            }
             DefKind::OpaqueTy => ty::AliasTermKind::OpaqueTy,
             DefKind::TyAlias => ty::AliasTermKind::FreeTy,
-            DefKind::AssocConst => ty::AliasTermKind::ProjectionConst,
-            DefKind::AnonConst | DefKind::Const | DefKind::Ctor(_, CtorKind::Const) => {
+            DefKind::Const => ty::AliasTermKind::FreeConst,
+            DefKind::AnonConst | DefKind::Ctor(_, CtorKind::Const) => {
                 ty::AliasTermKind::UnevaluatedConst
             }
             kind => bug!("unexpected DefKind in AliasTy: {kind:?}"),
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index ad8677f7c7d..af90c2fb95d 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -3195,7 +3195,7 @@ define_print! {
 
     ty::AliasTerm<'tcx> {
         match self.kind(cx.tcx()) {
-            ty::AliasTermKind::InherentTy => p!(pretty_print_inherent_projection(*self)),
+            ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => p!(pretty_print_inherent_projection(*self)),
             ty::AliasTermKind::ProjectionTy => {
                 if !(cx.should_print_verbose() || with_reduced_queries())
                     && cx.tcx().is_impl_trait_in_trait(self.def_id)
@@ -3205,7 +3205,8 @@ define_print! {
                     p!(print_def_path(self.def_id, self.args));
                 }
             }
-            | ty::AliasTermKind::FreeTy
+            ty::AliasTermKind::FreeTy
+            | ty::AliasTermKind::FreeConst
             | ty::AliasTermKind::OpaqueTy
             | ty::AliasTermKind::UnevaluatedConst
             | ty::AliasTermKind::ProjectionConst => {
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index f5158edffcf..6fe5927c29f 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -966,7 +966,9 @@ impl<'tcx> TyCtxt<'tcx> {
             }
             ty::AliasTermKind::OpaqueTy => Some(self.variances_of(def_id)),
             ty::AliasTermKind::InherentTy
+            | ty::AliasTermKind::InherentConst
             | ty::AliasTermKind::FreeTy
+            | ty::AliasTermKind::FreeConst
             | ty::AliasTermKind::UnevaluatedConst
             | ty::AliasTermKind::ProjectionConst => None,
         }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 8e69ff568b9..4a7e98f24ae 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -568,6 +568,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
 
         // Lower the named constant to a THIR pattern.
         let args = self.typeck_results.node_args(id);
+        // FIXME(mgca): we will need to special case IACs here to have type system compatible
+        // generic args, instead of how we represent them in body expressions.
         let c = ty::Const::new_unevaluated(self.tcx, ty::UnevaluatedConst { def: def_id, args });
         let mut pattern = self.const_to_pat(c, ty, id, span);
 
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs
index d077f8a9be8..8aa6e4a3d71 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs
@@ -19,19 +19,25 @@ where
         goal: Goal<I, ty::NormalizesTo<I>>,
     ) -> QueryResult<I> {
         let cx = self.cx();
-        let free_ty = goal.predicate.alias;
+        let free_alias = goal.predicate.alias;
 
         // Check where clauses
         self.add_goals(
             GoalSource::Misc,
-            cx.predicates_of(free_ty.def_id)
-                .iter_instantiated(cx, free_ty.args)
+            cx.predicates_of(free_alias.def_id)
+                .iter_instantiated(cx, free_alias.args)
                 .map(|pred| goal.with(cx, pred)),
         );
 
-        let actual = cx.type_of(free_ty.def_id).instantiate(cx, free_ty.args);
-        self.instantiate_normalizes_to_term(goal, actual.into());
+        let actual = if free_alias.kind(cx).is_type() {
+            cx.type_of(free_alias.def_id).instantiate(cx, free_alias.args)
+        } else {
+            // FIXME(mgca): once const items are actual aliases defined as equal to type system consts
+            // this should instead return that.
+            panic!("normalizing free const aliases in the type system is unsupported");
+        };
 
+        self.instantiate_normalizes_to_term(goal, actual.into());
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
 }
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs
index 1d1ff09ee41..2640238f5a9 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs
@@ -15,12 +15,12 @@ where
     D: SolverDelegate<Interner = I>,
     I: Interner,
 {
-    pub(super) fn normalize_inherent_associated_type(
+    pub(super) fn normalize_inherent_associated_term(
         &mut self,
         goal: Goal<I, ty::NormalizesTo<I>>,
     ) -> QueryResult<I> {
         let cx = self.cx();
-        let inherent = goal.predicate.alias.expect_ty(cx);
+        let inherent = goal.predicate.alias;
 
         let impl_def_id = cx.parent(inherent.def_id);
         let impl_args = self.fresh_args_for_item(impl_def_id);
@@ -48,8 +48,13 @@ where
                 .map(|pred| goal.with(cx, pred)),
         );
 
-        let normalized = cx.type_of(inherent.def_id).instantiate(cx, inherent_args);
-        self.instantiate_normalizes_to_term(goal, normalized.into());
+        let normalized = if inherent.kind(cx).is_type() {
+            cx.type_of(inherent.def_id).instantiate(cx, inherent_args).into()
+        } else {
+            // FIXME(mgca): Properly handle IACs in the type system
+            panic!("normalizing inherent associated consts in the type system is unsupported");
+        };
+        self.instantiate_normalizes_to_term(goal, normalized);
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
 }
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index b030af48381..400b4ce1200 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -48,9 +48,13 @@ where
                     })
                 })
             }
-            ty::AliasTermKind::InherentTy => self.normalize_inherent_associated_type(goal),
+            ty::AliasTermKind::InherentTy | ty::AliasTermKind::InherentConst => {
+                self.normalize_inherent_associated_term(goal)
+            }
             ty::AliasTermKind::OpaqueTy => self.normalize_opaque_type(goal),
-            ty::AliasTermKind::FreeTy => self.normalize_free_alias(goal),
+            ty::AliasTermKind::FreeTy | ty::AliasTermKind::FreeConst => {
+                self.normalize_free_alias(goal)
+            }
             ty::AliasTermKind::UnevaluatedConst => self.normalize_anon_const(goal),
         }
     }
@@ -333,6 +337,8 @@ where
                     cx.type_of(target_item_def_id).map_bound(|ty| ty.into())
                 }
                 ty::AliasTermKind::ProjectionConst => {
+                    // FIXME(mgca): once const items are actual aliases defined as equal to type system consts
+                    // this should instead return that.
                     if cx.features().associated_const_equality() {
                         panic!("associated const projection is not supported yet")
                     } else {
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 0987c5b42d8..5b938456e03 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -51,7 +51,7 @@ pub use self::dyn_compatibility::{
 pub use self::engine::{ObligationCtxt, TraitEngineExt};
 pub use self::fulfill::{FulfillmentContext, OldSolverError, PendingPredicateObligation};
 pub use self::normalize::NormalizeExt;
-pub use self::project::{normalize_inherent_projection, normalize_projection_ty};
+pub use self::project::{normalize_inherent_projection, normalize_projection_term};
 pub use self::select::{
     EvaluationCache, EvaluationResult, IntercrateAmbiguityCause, OverflowError, SelectionCache,
     SelectionContext,
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
index e08428f925c..88a0c402702 100644
--- a/compiler/rustc_trait_selection/src/traits/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -1,6 +1,7 @@
 //! Deeply normalize types using the old trait solver.
 
 use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_hir::def::DefKind;
 use rustc_infer::infer::at::At;
 use rustc_infer::infer::{InferCtxt, InferOk};
 use rustc_infer::traits::{
@@ -10,15 +11,12 @@ use rustc_macros::extension;
 use rustc_middle::span_bug;
 use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
 use rustc_middle::ty::{
-    self, AliasTy, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
+    self, AliasTerm, Term, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
     TypeVisitableExt, TypingMode,
 };
 use tracing::{debug, instrument};
 
-use super::{
-    BoundVarReplacer, PlaceholderReplacer, SelectionContext, project,
-    with_replaced_escaping_bound_vars,
-};
+use super::{BoundVarReplacer, PlaceholderReplacer, SelectionContext, project};
 use crate::error_reporting::InferCtxtErrorExt;
 use crate::error_reporting::traits::OverflowCause;
 use crate::solve::NextSolverError;
@@ -179,8 +177,10 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
         if !needs_normalization(self.selcx.infcx, &value) { value } else { value.fold_with(self) }
     }
 
-    fn normalize_trait_projection(&mut self, data: AliasTy<'tcx>) -> Ty<'tcx> {
-        if !data.has_escaping_bound_vars() {
+    // FIXME(mgca): While this supports constants, it is only used for types by default right now
+    #[instrument(level = "debug", skip(self), ret)]
+    fn normalize_trait_projection(&mut self, proj: AliasTerm<'tcx>) -> Term<'tcx> {
+        if !proj.has_escaping_bound_vars() {
             // When we don't have escaping bound vars we can normalize ambig aliases
             // to inference variables (done in `normalize_projection_ty`). This would
             // be wrong if there were escaping bound vars as even if we instantiated
@@ -189,23 +189,15 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
             //
             // Also, as an optimization: when we don't have escaping bound vars, we don't
             // need to replace them with placeholders (see branch below).
-            let data = data.fold_with(self);
-            let normalized_ty = project::normalize_projection_ty(
+            let proj = proj.fold_with(self);
+            project::normalize_projection_term(
                 self.selcx,
                 self.param_env,
-                data,
+                proj,
                 self.cause.clone(),
                 self.depth,
                 self.obligations,
-            );
-            debug!(
-                ?self.depth,
-                ?ty,
-                ?normalized_ty,
-                obligations.len = ?self.obligations.len(),
-                "AssocTypeNormalizer: normalized type"
-            );
-            normalized_ty.expect_type()
+            )
         } else {
             // If there are escaping bound vars, we temporarily replace the
             // bound vars with placeholders. Note though, that in the case
@@ -218,72 +210,64 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
             // Note: this isn't necessarily the final approach here; we may
             // want to figure out how to register obligations with escaping vars
             // or handle this some other way.
-
             let infcx = self.selcx.infcx;
-            let (data, mapped_regions, mapped_types, mapped_consts) =
-                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
-            let data = data.fold_with(self);
-            let normalized_ty = project::opt_normalize_projection_term(
+            let (proj, mapped_regions, mapped_types, mapped_consts) =
+                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, proj);
+            let proj = proj.fold_with(self);
+            let normalized_term = project::opt_normalize_projection_term(
                 self.selcx,
                 self.param_env,
-                data.into(),
+                proj,
                 self.cause.clone(),
                 self.depth,
                 self.obligations,
             )
             .ok()
             .flatten()
-            .map(|term| term.expect_type())
-            .map(|normalized_ty| {
-                PlaceholderReplacer::replace_placeholders(
-                    infcx,
-                    mapped_regions,
-                    mapped_types,
-                    mapped_consts,
-                    &self.universes,
-                    normalized_ty,
-                )
-            })
-            .unwrap_or_else(|| ty.super_fold_with(self));
-
-            debug!(
-                ?self.depth,
-                ?ty,
-                ?normalized_ty,
-                obligations.len = ?self.obligations.len(),
-                "AssocTypeNormalizer: normalized type"
-            );
-            normalized_ty
+            .unwrap_or(proj.to_term(infcx.tcx));
+
+            PlaceholderReplacer::replace_placeholders(
+                infcx,
+                mapped_regions,
+                mapped_types,
+                mapped_consts,
+                &self.universes,
+                normalized_term,
+            )
         }
     }
 
-    fn normalize_inherent_projection(&mut self, data: AliasTy<'tcx>) -> Ty<'tcx> {
-        if !data.has_escaping_bound_vars() {
-            // This branch is *mostly* just an optimization: when we don't
-            // have escaping bound vars, we don't need to replace them with
-            // placeholders (see branch below). *Also*, we know that we can
-            // register an obligation to *later* project, since we know
-            // there won't be bound vars there.
-
-            let data = data.fold_with(self);
+    // FIXME(mgca): While this supports constants, it is only used for types by default right now
+    #[instrument(level = "debug", skip(self), ret)]
+    fn normalize_inherent_projection(&mut self, inherent: AliasTerm<'tcx>) -> Term<'tcx> {
+        if !inherent.has_escaping_bound_vars() {
+            // When we don't have escaping bound vars we can normalize ambig aliases
+            // to inference variables (done in `normalize_projection_ty`). This would
+            // be wrong if there were escaping bound vars as even if we instantiated
+            // the bound vars with placeholders, we wouldn't be able to map them back
+            // after normalization succeeded.
+            //
+            // Also, as an optimization: when we don't have escaping bound vars, we don't
+            // need to replace them with placeholders (see branch below).
 
+            let inherent = inherent.fold_with(self);
             project::normalize_inherent_projection(
                 self.selcx,
                 self.param_env,
-                data,
+                inherent,
                 self.cause.clone(),
                 self.depth,
                 self.obligations,
             )
         } else {
             let infcx = self.selcx.infcx;
-            let (data, mapped_regions, mapped_types, mapped_consts) =
-                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
-            let data = data.fold_with(self);
-            let ty = project::normalize_inherent_projection(
+            let (inherent, mapped_regions, mapped_types, mapped_consts) =
+                BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, inherent);
+            let inherent = inherent.fold_with(self);
+            let inherent = project::normalize_inherent_projection(
                 self.selcx,
                 self.param_env,
-                data,
+                inherent,
                 self.cause.clone(),
                 self.depth,
                 self.obligations,
@@ -295,16 +279,18 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
                 mapped_types,
                 mapped_consts,
                 &self.universes,
-                ty,
+                inherent,
             )
         }
     }
 
-    fn normalize_free_alias(&mut self, data: AliasTy<'tcx>) -> Ty<'tcx> {
+    // FIXME(mgca): While this supports constants, it is only used for types by default right now
+    #[instrument(level = "debug", skip(self), ret)]
+    fn normalize_free_alias(&mut self, free: AliasTerm<'tcx>) -> Term<'tcx> {
         let recursion_limit = self.cx().recursion_limit();
         if !recursion_limit.value_within_limit(self.depth) {
             self.selcx.infcx.err_ctxt().report_overflow_error(
-                OverflowCause::DeeplyNormalize(data.into()),
+                OverflowCause::DeeplyNormalize(free.into()),
                 self.cause.span,
                 false,
                 |diag| {
@@ -315,9 +301,13 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
 
         let infcx = self.selcx.infcx;
         self.obligations.extend(
-            infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map(
+            // FIXME(BoxyUwU):
+            // FIXME(lazy_type_alias):
+            // It seems suspicious to instantiate the predicates with arguments that might be bound vars,
+            // we might wind up instantiating one of these bound vars underneath a hrtb.
+            infcx.tcx.predicates_of(free.def_id).instantiate_own(infcx.tcx, free.args).map(
                 |(mut predicate, span)| {
-                    if data.has_escaping_bound_vars() {
+                    if free.has_escaping_bound_vars() {
                         (predicate, ..) = BoundVarReplacer::replace_bound_vars(
                             infcx,
                             &mut self.universes,
@@ -325,13 +315,21 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
                         );
                     }
                     let mut cause = self.cause.clone();
-                    cause.map_code(|code| ObligationCauseCode::TypeAlias(code, span, data.def_id));
+                    cause.map_code(|code| ObligationCauseCode::TypeAlias(code, span, free.def_id));
                     Obligation::new(infcx.tcx, cause, self.param_env, predicate)
                 },
             ),
         );
         self.depth += 1;
-        let res = infcx.tcx.type_of(data.def_id).instantiate(infcx.tcx, data.args).fold_with(self);
+        let res = if free.kind(infcx.tcx).is_type() {
+            infcx.tcx.type_of(free.def_id).instantiate(infcx.tcx, free.args).fold_with(self).into()
+        } else {
+            // FIXME(mgca): once const items are actual aliases defined as equal to type system consts
+            // this should instead use that rather than evaluating.
+            super::evaluate_const(infcx, free.to_term(infcx.tcx).expect_const(), self.param_env)
+                .super_fold_with(self)
+                .into()
+        };
         self.depth -= 1;
         res
     }
@@ -415,28 +413,64 @@ impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx
                     }
                 }
             }
-            ty::Projection => self.normalize_trait_projection(data),
-            ty::Free => self.normalize_free_alias(data),
-            ty::Inherent => self.normalize_inherent_projection(data),
+
+            ty::Projection => self.normalize_trait_projection(data.into()).expect_type(),
+            ty::Inherent => self.normalize_inherent_projection(data.into()).expect_type(),
+            ty::Free => self.normalize_free_alias(data.into()).expect_type(),
         }
     }
 
     #[instrument(skip(self), level = "debug")]
-    fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
         let tcx = self.selcx.tcx();
-        if tcx.features().generic_const_exprs() || !needs_normalization(self.selcx.infcx, &constant)
-        {
-            constant
+        if tcx.features().generic_const_exprs() || !needs_normalization(self.selcx.infcx, &ct) {
+            return ct;
+        }
+
+        // Doing "proper" normalization of const aliases is inherently cyclic until const items
+        // are real aliases instead of having bodies. We gate proper const alias handling behind
+        // mgca to avoid breaking stable code, though this should become the "main" codepath long
+        // before mgca is stabilized.
+        //
+        // FIXME(BoxyUwU): Enabling this by default is blocked on a refactoring to how const items
+        // are represented.
+        if tcx.features().min_generic_const_args() {
+            let uv = match ct.kind() {
+                ty::ConstKind::Unevaluated(uv) => uv,
+                _ => return ct.super_fold_with(self),
+            };
+
+            let ct = match tcx.def_kind(uv.def) {
+                DefKind::AssocConst => match tcx.def_kind(tcx.parent(uv.def)) {
+                    DefKind::Trait => self.normalize_trait_projection(uv.into()),
+                    DefKind::Impl { of_trait: false } => {
+                        self.normalize_inherent_projection(uv.into())
+                    }
+                    kind => unreachable!(
+                        "unexpected `DefKind` for const alias' resolution's parent def: {:?}",
+                        kind
+                    ),
+                },
+                DefKind::Const | DefKind::AnonConst => self.normalize_free_alias(uv.into()),
+                kind => {
+                    unreachable!("unexpected `DefKind` for const alias to resolve to: {:?}", kind)
+                }
+            };
+
+            // We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be
+            // unnormalized after const evaluation returns.
+            ct.expect_const().super_fold_with(self)
         } else {
-            let constant = constant.super_fold_with(self);
-            debug!(?constant, ?self.param_env);
-            with_replaced_escaping_bound_vars(
+            let ct = ct.super_fold_with(self);
+            return super::with_replaced_escaping_bound_vars(
                 self.selcx.infcx,
                 &mut self.universes,
-                constant,
-                |constant| super::evaluate_const(self.selcx.infcx, constant, self.param_env),
+                ct,
+                |ct| super::evaluate_const(self.selcx.infcx, ct, self.param_env),
             )
-            .super_fold_with(self)
+            .super_fold_with(self);
+            // We re-fold the normalized const as the `ty` field on `ConstKind::Value` may be
+            // unnormalized after const evaluation returns.
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index dd868c9d40e..ca58da5ca6d 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -5,7 +5,6 @@ use std::ops::ControlFlow;
 use rustc_data_structures::sso::SsoHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::ErrorGuaranteed;
-use rustc_hir::def::DefKind;
 use rustc_hir::lang_items::LangItem;
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
@@ -172,6 +171,7 @@ pub(super) enum ProjectAndUnifyResult<'tcx> {
 /// ```
 /// If successful, this may result in additional obligations. Also returns
 /// the projection cache key used to track these additional obligations.
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
 #[instrument(level = "debug", skip(selcx))]
 pub(super) fn poly_project_and_unify_term<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
@@ -201,6 +201,7 @@ pub(super) fn poly_project_and_unify_term<'cx, 'tcx>(
 /// If successful, this may result in additional obligations.
 ///
 /// See [poly_project_and_unify_term] for an explanation of the return value.
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
 #[instrument(level = "debug", skip(selcx))]
 fn project_and_unify_term<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
@@ -258,34 +259,28 @@ fn project_and_unify_term<'cx, 'tcx>(
 /// there are unresolved type variables in the projection, we will
 /// instantiate it with a fresh type variable `$X` and generate a new
 /// obligation `<T as Trait>::Item == $X` for later.
-pub fn normalize_projection_ty<'a, 'b, 'tcx>(
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
+pub fn normalize_projection_term<'a, 'b, 'tcx>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
-    projection_ty: ty::AliasTy<'tcx>,
+    alias_term: ty::AliasTerm<'tcx>,
     cause: ObligationCause<'tcx>,
     depth: usize,
     obligations: &mut PredicateObligations<'tcx>,
 ) -> Term<'tcx> {
-    opt_normalize_projection_term(
-        selcx,
-        param_env,
-        projection_ty.into(),
-        cause.clone(),
-        depth,
-        obligations,
-    )
-    .ok()
-    .flatten()
-    .unwrap_or_else(move || {
-        // if we bottom out in ambiguity, create a type variable
-        // and a deferred predicate to resolve this when more type
-        // information is available.
-
-        selcx
-            .infcx
-            .projection_ty_to_infer(param_env, projection_ty, cause, depth + 1, obligations)
-            .into()
-    })
+    opt_normalize_projection_term(selcx, param_env, alias_term, cause.clone(), depth, obligations)
+        .ok()
+        .flatten()
+        .unwrap_or_else(move || {
+            // if we bottom out in ambiguity, create a type variable
+            // and a deferred predicate to resolve this when more type
+            // information is available.
+
+            selcx
+                .infcx
+                .projection_term_to_infer(param_env, alias_term, cause, depth + 1, obligations)
+                .into()
+        })
 }
 
 /// The guts of `normalize`: normalize a specific projection like `<T
@@ -298,6 +293,7 @@ pub fn normalize_projection_ty<'a, 'b, 'tcx>(
 /// often immediately appended to another obligations vector. So now this
 /// function takes an obligations vector and appends to it directly, which is
 /// slightly uglier but avoids the need for an extra short-lived allocation.
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
 #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
 pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
@@ -456,6 +452,7 @@ pub(super) fn opt_normalize_projection_term<'a, 'b, 'tcx>(
 /// an error for this obligation, but we legitimately should not,
 /// because it contains `[type error]`. Yuck! (See issue #29857 for
 /// one case where this arose.)
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
 fn normalize_to_error<'a, 'tcx>(
     selcx: &SelectionContext<'a, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -469,9 +466,10 @@ fn normalize_to_error<'a, 'tcx>(
         | ty::AliasTermKind::InherentTy
         | ty::AliasTermKind::OpaqueTy
         | ty::AliasTermKind::FreeTy => selcx.infcx.next_ty_var(cause.span).into(),
-        ty::AliasTermKind::UnevaluatedConst | ty::AliasTermKind::ProjectionConst => {
-            selcx.infcx.next_const_var(cause.span).into()
-        }
+        ty::AliasTermKind::FreeConst
+        | ty::AliasTermKind::InherentConst
+        | ty::AliasTermKind::UnevaluatedConst
+        | ty::AliasTermKind::ProjectionConst => selcx.infcx.next_const_var(cause.span).into(),
     };
     let mut obligations = PredicateObligations::new();
     obligations.push(Obligation {
@@ -484,36 +482,37 @@ fn normalize_to_error<'a, 'tcx>(
 }
 
 /// Confirm and normalize the given inherent projection.
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
 #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
 pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
-    alias_ty: ty::AliasTy<'tcx>,
+    alias_term: ty::AliasTerm<'tcx>,
     cause: ObligationCause<'tcx>,
     depth: usize,
     obligations: &mut PredicateObligations<'tcx>,
-) -> Ty<'tcx> {
+) -> ty::Term<'tcx> {
     let tcx = selcx.tcx();
 
     if !tcx.recursion_limit().value_within_limit(depth) {
         // Halt compilation because it is important that overflows never be masked.
         tcx.dcx().emit_fatal(InherentProjectionNormalizationOverflow {
             span: cause.span,
-            ty: alias_ty.to_string(),
+            ty: alias_term.to_string(),
         });
     }
 
-    let args = compute_inherent_assoc_ty_args(
+    let args = compute_inherent_assoc_term_args(
         selcx,
         param_env,
-        alias_ty,
+        alias_term,
         cause.clone(),
         depth,
         obligations,
     );
 
     // Register the obligations arising from the impl and from the associated type itself.
-    let predicates = tcx.predicates_of(alias_ty.def_id).instantiate(tcx, args);
+    let predicates = tcx.predicates_of(alias_term.def_id).instantiate(tcx, args);
     for (predicate, span) in predicates {
         let predicate = normalize_with_depth_to(
             selcx,
@@ -531,7 +530,7 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
             // cause code, inherent projections will be printed with identity instantiation in
             // diagnostics which is not ideal.
             // Consider creating separate cause codes for this specific situation.
-            ObligationCauseCode::WhereClause(alias_ty.def_id, span),
+            ObligationCauseCode::WhereClause(alias_term.def_id, span),
         );
 
         obligations.push(Obligation::with_depth(
@@ -543,27 +542,33 @@ pub fn normalize_inherent_projection<'a, 'b, 'tcx>(
         ));
     }
 
-    let ty = tcx.type_of(alias_ty.def_id).instantiate(tcx, args);
+    let term: Term<'tcx> = if alias_term.kind(tcx).is_type() {
+        tcx.type_of(alias_term.def_id).instantiate(tcx, args).into()
+    } else {
+        get_associated_const_value(selcx, alias_term.to_term(tcx).expect_const(), param_env).into()
+    };
 
-    let mut ty = selcx.infcx.resolve_vars_if_possible(ty);
-    if ty.has_aliases() {
-        ty = normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, ty, obligations);
+    let mut term = selcx.infcx.resolve_vars_if_possible(term);
+    if term.has_aliases() {
+        term =
+            normalize_with_depth_to(selcx, param_env, cause.clone(), depth + 1, term, obligations);
     }
 
-    ty
+    term
 }
 
-pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
+pub fn compute_inherent_assoc_term_args<'a, 'b, 'tcx>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
-    alias_ty: ty::AliasTy<'tcx>,
+    alias_term: ty::AliasTerm<'tcx>,
     cause: ObligationCause<'tcx>,
     depth: usize,
     obligations: &mut PredicateObligations<'tcx>,
 ) -> ty::GenericArgsRef<'tcx> {
     let tcx = selcx.tcx();
 
-    let impl_def_id = tcx.parent(alias_ty.def_id);
+    let impl_def_id = tcx.parent(alias_term.def_id);
     let impl_args = selcx.infcx.fresh_args_for_item(cause.span, impl_def_id);
 
     let mut impl_ty = tcx.type_of(impl_def_id).instantiate(tcx, impl_args);
@@ -580,7 +585,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
 
     // Infer the generic parameters of the impl by unifying the
     // impl type with the self type of the projection.
-    let mut self_ty = alias_ty.self_ty();
+    let mut self_ty = alias_term.self_ty();
     if !selcx.infcx.next_trait_solver() {
         self_ty = normalize_with_depth_to(
             selcx,
@@ -602,7 +607,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
         }
     }
 
-    alias_ty.rebase_inherent_args_onto_impl(impl_args, tcx)
+    alias_term.rebase_inherent_args_onto_impl(impl_args, tcx)
 }
 
 enum Projected<'tcx> {
@@ -630,6 +635,7 @@ impl<'tcx> Progress<'tcx> {
 ///
 /// IMPORTANT:
 /// - `obligation` must be fully normalized
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
 #[instrument(level = "info", skip(selcx))]
 fn project<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
@@ -896,7 +902,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
             ImplSource::UserDefined(impl_data) => {
                 // We have to be careful when projecting out of an
                 // impl because of specialization. If we are not in
-                // codegen (i.e., projection mode is not "any"), and the
+                // codegen (i.e., `TypingMode` is not `PostAnalysis`), and the
                 // impl's type is declared as default, then we disable
                 // projection (even if the trait ref is fully
                 // monomorphic). In the case where trait ref is not
@@ -1189,6 +1195,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
     });
 }
 
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
 fn confirm_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
@@ -1222,6 +1229,7 @@ fn confirm_candidate<'cx, 'tcx>(
     result
 }
 
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
 fn confirm_select_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
@@ -1873,6 +1881,7 @@ fn confirm_async_fn_kind_helper_candidate<'cx, 'tcx>(
         .with_addl_obligations(nested)
 }
 
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
 fn confirm_param_env_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
@@ -1926,9 +1935,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
     ) {
         Ok(InferOk { value: _, obligations }) => {
             nested_obligations.extend(obligations);
-            assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations);
-            // FIXME(associated_const_equality): Handle consts here as well? Maybe this progress type should just take
-            // a term instead.
+            assoc_term_own_obligations(selcx, obligation, &mut nested_obligations);
             Progress { term: cache_entry.term, obligations: nested_obligations }
         }
         Err(e) => {
@@ -1942,6 +1949,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>(
     }
 }
 
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
 fn confirm_impl_candidate<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
@@ -1955,8 +1963,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     let trait_def_id = tcx.trait_id_of_impl(impl_def_id).unwrap();
 
     let param_env = obligation.param_env;
-    let assoc_ty = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
-        Ok(assoc_ty) => assoc_ty,
+    let assoc_term = match specialization_graph::assoc_def(tcx, impl_def_id, assoc_item_id) {
+        Ok(assoc_term) => assoc_term,
         Err(guar) => return Ok(Projected::Progress(Progress::error(tcx, guar))),
     };
 
@@ -1965,10 +1973,10 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     // has impossible-to-satisfy predicates (since those were
     // allowed in <https://github.com/rust-lang/rust/pull/135480>),
     // or because the impl is literally missing the definition.
-    if !assoc_ty.item.defaultness(tcx).has_value() {
+    if !assoc_term.item.defaultness(tcx).has_value() {
         debug!(
             "confirm_impl_candidate: no associated type {:?} for {:?}",
-            assoc_ty.item.name(),
+            assoc_term.item.name(),
             obligation.predicate
         );
         if tcx.impl_self_is_guaranteed_unsized(impl_def_id) {
@@ -1979,7 +1987,11 @@ fn confirm_impl_candidate<'cx, 'tcx>(
             return Ok(Projected::NoProgress(obligation.predicate.to_term(tcx)));
         } else {
             return Ok(Projected::Progress(Progress {
-                term: Ty::new_misc_error(tcx).into(),
+                term: if obligation.predicate.kind(tcx).is_type() {
+                    Ty::new_misc_error(tcx).into()
+                } else {
+                    ty::Const::new_misc_error(tcx).into()
+                },
                 obligations: nested,
             }));
         }
@@ -1992,27 +2004,32 @@ fn confirm_impl_candidate<'cx, 'tcx>(
     // * `args` is `[u32]`
     // * `args` ends up as `[u32, S]`
     let args = obligation.predicate.args.rebase_onto(tcx, trait_def_id, args);
-    let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_ty.defining_node);
-    let is_const = matches!(tcx.def_kind(assoc_ty.item.def_id), DefKind::AssocConst);
-
-    let term: ty::EarlyBinder<'tcx, ty::Term<'tcx>> = if is_const {
-        let did = assoc_ty.item.def_id;
-        let identity_args = crate::traits::GenericArgs::identity_for_item(tcx, did);
-        let uv = ty::UnevaluatedConst::new(did, identity_args);
-        ty::EarlyBinder::bind(ty::Const::new_unevaluated(tcx, uv).into())
+    let args = translate_args(selcx.infcx, param_env, impl_def_id, args, assoc_term.defining_node);
+
+    let term = if obligation.predicate.kind(tcx).is_type() {
+        tcx.type_of(assoc_term.item.def_id).map_bound(|ty| ty.into())
     } else {
-        tcx.type_of(assoc_ty.item.def_id).map_bound(|ty| ty.into())
+        ty::EarlyBinder::bind(
+            get_associated_const_value(
+                selcx,
+                obligation.predicate.to_term(tcx).expect_const(),
+                param_env,
+            )
+            .into(),
+        )
     };
 
-    let progress = if !tcx.check_args_compatible(assoc_ty.item.def_id, args) {
-        let err = Ty::new_error_with_message(
-            tcx,
-            obligation.cause.span,
-            "impl item and trait item have different parameters",
-        );
-        Progress { term: err.into(), obligations: nested }
+    let progress = if !tcx.check_args_compatible(assoc_term.item.def_id, args) {
+        let msg = "impl item and trait item have different parameters";
+        let span = obligation.cause.span;
+        let err = if obligation.predicate.kind(tcx).is_type() {
+            Ty::new_error_with_message(tcx, span, msg).into()
+        } else {
+            ty::Const::new_error_with_message(tcx, span, msg).into()
+        };
+        Progress { term: err, obligations: nested }
     } else {
-        assoc_ty_own_obligations(selcx, obligation, &mut nested);
+        assoc_term_own_obligations(selcx, obligation, &mut nested);
         Progress { term: term.instantiate(tcx, args), obligations: nested }
     };
     Ok(Projected::Progress(progress))
@@ -2020,7 +2037,8 @@ fn confirm_impl_candidate<'cx, 'tcx>(
 
 // Get obligations corresponding to the predicates from the where-clause of the
 // associated type itself.
-fn assoc_ty_own_obligations<'cx, 'tcx>(
+// FIXME(mgca): While this supports constants, it is only used for types by default right now
+fn assoc_term_own_obligations<'cx, 'tcx>(
     selcx: &mut SelectionContext<'cx, 'tcx>,
     obligation: &ProjectionTermObligation<'tcx>,
     nested: &mut PredicateObligations<'tcx>,
@@ -2090,3 +2108,15 @@ impl<'cx, 'tcx> ProjectionCacheKeyExt<'cx, 'tcx> for ProjectionCacheKey<'tcx> {
         })
     }
 }
+
+fn get_associated_const_value<'tcx>(
+    selcx: &mut SelectionContext<'_, 'tcx>,
+    alias_ct: ty::Const<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+) -> ty::Const<'tcx> {
+    // FIXME(mgca): We shouldn't be invoking ctfe here, instead const items should be aliases to type
+    // system consts that we can retrieve with some `query const_arg_of_alias` query. Evaluating the
+    // constant is "close enough" to getting the actual rhs of the const item for now even if it might
+    // lead to some cycles
+    super::evaluate_const(selcx.infcx, alias_ct, param_env)
+}
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 8008c7e4d34..94190cd3ae3 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -406,7 +406,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let predicate = self.infcx.enter_forall_and_leak_universe(obligation.predicate);
 
         let mut assume = predicate.trait_ref.args.const_at(2);
-        // FIXME(mgca): We should shallowly normalize this.
         if self.tcx().features().generic_const_exprs() {
             assume = crate::traits::evaluate_const(self.infcx, assume, obligation.param_env)
         }
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 2e32cda7602..08d3b92e9b5 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -6,6 +6,7 @@
 use std::iter;
 
 use rustc_hir as hir;
+use rustc_hir::def::DefKind;
 use rustc_hir::lang_items::LangItem;
 use rustc_infer::traits::{ObligationCauseCode, PredicateObligations};
 use rustc_middle::bug;
@@ -486,7 +487,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
     /// Pushes the obligations required for an inherent alias to be WF
     /// into `self.out`.
     // FIXME(inherent_associated_types): Merge this function with `fn compute_alias`.
-    fn add_wf_preds_for_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) {
+    fn add_wf_preds_for_inherent_projection(&mut self, data: ty::AliasTerm<'tcx>) {
         // An inherent projection is well-formed if
         //
         // (a) its predicates hold (*)
@@ -498,7 +499,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
         if !data.self_ty().has_escaping_bound_vars() {
             // FIXME(inherent_associated_types): Should this happen inside of a snapshot?
             // FIXME(inherent_associated_types): This is incompatible with the new solver and lazy norm!
-            let args = traits::project::compute_inherent_assoc_ty_args(
+            let args = traits::project::compute_inherent_assoc_term_args(
                 &mut traits::SelectionContext::new(self.infcx),
                 self.param_env,
                 data,
@@ -776,7 +777,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                 self.out.extend(obligations);
             }
             ty::Alias(ty::Inherent, data) => {
-                self.add_wf_preds_for_inherent_projection(data);
+                self.add_wf_preds_for_inherent_projection(data.into());
                 return; // Subtree handled by compute_inherent_projection.
             }
 
@@ -961,9 +962,6 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
         match c.kind() {
             ty::ConstKind::Unevaluated(uv) => {
                 if !c.has_escaping_bound_vars() {
-                    let obligations = self.nominal_obligations(uv.def, uv.args);
-                    self.out.extend(obligations);
-
                     let predicate = ty::Binder::dummy(ty::PredicateKind::Clause(
                         ty::ClauseKind::ConstEvaluatable(c),
                     ));
@@ -975,6 +973,16 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                         self.param_env,
                         predicate,
                     ));
+
+                    if tcx.def_kind(uv.def) == DefKind::AssocConst
+                        && tcx.def_kind(tcx.parent(uv.def)) == (DefKind::Impl { of_trait: false })
+                    {
+                        self.add_wf_preds_for_inherent_projection(uv.into());
+                        return; // Subtree is handled by above function
+                    } else {
+                        let obligations = self.nominal_obligations(uv.def, uv.args);
+                        self.out.extend(obligations);
+                    }
                 }
             }
             ty::ConstKind::Infer(_) => {
diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs
index 14a92ebb9f9..e52898cc6e2 100644
--- a/compiler/rustc_traits/src/normalize_projection_ty.rs
+++ b/compiler/rustc_traits/src/normalize_projection_ty.rs
@@ -32,8 +32,14 @@ fn normalize_canonicalized_projection_ty<'tcx>(
             let selcx = &mut SelectionContext::new(ocx.infcx);
             let cause = ObligationCause::dummy();
             let mut obligations = PredicateObligations::new();
-            let answer =
-                traits::normalize_projection_ty(selcx, param_env, goal, cause, 0, &mut obligations);
+            let answer = traits::normalize_projection_term(
+                selcx,
+                param_env,
+                goal.into(),
+                cause,
+                0,
+                &mut obligations,
+            );
             ocx.register_obligations(obligations);
             // #112047: With projections and opaques, we are able to create opaques that
             // are recursive (given some generic parameters of the opaque's type variables).
@@ -104,14 +110,14 @@ fn normalize_canonicalized_inherent_projection_ty<'tcx>(
             let answer = traits::normalize_inherent_projection(
                 selcx,
                 param_env,
-                goal,
+                goal.into(),
                 cause,
                 0,
                 &mut obligations,
             );
             ocx.register_obligations(obligations);
 
-            Ok(NormalizationResult { normalized_ty: answer })
+            Ok(NormalizationResult { normalized_ty: answer.expect_type() })
         },
     )
 }
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index 0411c5c2325..b59495b93c8 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -474,10 +474,15 @@ pub enum AliasTermKind {
     /// Currently only used if the type alias references opaque types.
     /// Can always be normalized away.
     FreeTy,
-    /// An unevaluated const coming from a generic const expression.
+
+    /// An unevaluated anonymous constants.
     UnevaluatedConst,
     /// An unevaluated const coming from an associated const.
     ProjectionConst,
+    /// A top level const item not part of a trait or impl.
+    FreeConst,
+    /// An associated const in an inherent `impl`
+    InherentConst,
 }
 
 impl AliasTermKind {
@@ -486,11 +491,27 @@ impl AliasTermKind {
             AliasTermKind::ProjectionTy => "associated type",
             AliasTermKind::ProjectionConst => "associated const",
             AliasTermKind::InherentTy => "inherent associated type",
+            AliasTermKind::InherentConst => "inherent associated const",
             AliasTermKind::OpaqueTy => "opaque type",
             AliasTermKind::FreeTy => "type alias",
+            AliasTermKind::FreeConst => "unevaluated constant",
             AliasTermKind::UnevaluatedConst => "unevaluated constant",
         }
     }
+
+    pub fn is_type(self) -> bool {
+        match self {
+            AliasTermKind::ProjectionTy
+            | AliasTermKind::InherentTy
+            | AliasTermKind::OpaqueTy
+            | AliasTermKind::FreeTy => true,
+
+            AliasTermKind::UnevaluatedConst
+            | AliasTermKind::ProjectionConst
+            | AliasTermKind::InherentConst
+            | AliasTermKind::FreeConst => false,
+        }
+    }
 }
 
 impl From<ty::AliasTyKind> for AliasTermKind {
@@ -566,7 +587,10 @@ impl<I: Interner> AliasTerm<I> {
             | AliasTermKind::InherentTy
             | AliasTermKind::OpaqueTy
             | AliasTermKind::FreeTy => {}
-            AliasTermKind::UnevaluatedConst | AliasTermKind::ProjectionConst => {
+            AliasTermKind::InherentConst
+            | AliasTermKind::FreeConst
+            | AliasTermKind::UnevaluatedConst
+            | AliasTermKind::ProjectionConst => {
                 panic!("Cannot turn `UnevaluatedConst` into `AliasTy`")
             }
         }
@@ -603,18 +627,19 @@ impl<I: Interner> AliasTerm<I> {
                 ty::AliasTy { def_id: self.def_id, args: self.args, _use_alias_ty_new_instead: () },
             )
             .into(),
-            AliasTermKind::UnevaluatedConst | AliasTermKind::ProjectionConst => {
-                I::Const::new_unevaluated(
-                    interner,
-                    ty::UnevaluatedConst::new(self.def_id, self.args),
-                )
-                .into()
-            }
+            AliasTermKind::FreeConst
+            | AliasTermKind::InherentConst
+            | AliasTermKind::UnevaluatedConst
+            | AliasTermKind::ProjectionConst => I::Const::new_unevaluated(
+                interner,
+                ty::UnevaluatedConst::new(self.def_id, self.args),
+            )
+            .into(),
         }
     }
 }
 
-/// The following methods work only with (trait) associated type projections.
+/// The following methods work only with (trait) associated term projections.
 impl<I: Interner> AliasTerm<I> {
     pub fn self_ty(self) -> I::Ty {
         self.args.type_at(0)
@@ -659,6 +684,31 @@ impl<I: Interner> AliasTerm<I> {
     }
 }
 
+/// The following methods work only with inherent associated term projections.
+impl<I: Interner> AliasTerm<I> {
+    /// Transform the generic parameters to have the given `impl` args as the base and the GAT args on top of that.
+    ///
+    /// Does the following transformation:
+    ///
+    /// ```text
+    /// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m]
+    ///
+    ///     I_i impl args
+    ///     P_j GAT args
+    /// ```
+    pub fn rebase_inherent_args_onto_impl(
+        self,
+        impl_args: I::GenericArgs,
+        interner: I,
+    ) -> I::GenericArgs {
+        debug_assert!(matches!(
+            self.kind(interner),
+            AliasTermKind::InherentTy | AliasTermKind::InherentConst
+        ));
+        interner.mk_args_from_iter(impl_args.iter().chain(self.args.iter().skip(1)))
+    }
+}
+
 impl<I: Interner> From<ty::AliasTy<I>> for AliasTerm<I> {
     fn from(ty: ty::AliasTy<I>) -> Self {
         AliasTerm { args: ty.args, def_id: ty.def_id, _use_alias_term_new_instead: () }
diff --git a/compiler/rustc_type_ir/src/relate.rs b/compiler/rustc_type_ir/src/relate.rs
index c80a567117c..e3c4a793b37 100644
--- a/compiler/rustc_type_ir/src/relate.rs
+++ b/compiler/rustc_type_ir/src/relate.rs
@@ -273,8 +273,10 @@ impl<I: Interner> Relate<I> for ty::AliasTerm<I> {
                     false, // do not fetch `type_of(a_def_id)`, as it will cause a cycle
                 )?,
                 ty::AliasTermKind::ProjectionTy
+                | ty::AliasTermKind::FreeConst
                 | ty::AliasTermKind::FreeTy
                 | ty::AliasTermKind::InherentTy
+                | ty::AliasTermKind::InherentConst
                 | ty::AliasTermKind::UnevaluatedConst
                 | ty::AliasTermKind::ProjectionConst => {
                     relate_args_invariantly(relation, a.args, b.args)?
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index 21adbffc027..cf2e4284d10 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -514,28 +514,6 @@ impl<I: Interner> AliasTy<I> {
     }
 }
 
-/// The following methods work only with inherent associated type projections.
-impl<I: Interner> AliasTy<I> {
-    /// Transform the generic parameters to have the given `impl` args as the base and the GAT args on top of that.
-    ///
-    /// Does the following transformation:
-    ///
-    /// ```text
-    /// [Self, P_0...P_m] -> [I_0...I_n, P_0...P_m]
-    ///
-    ///     I_i impl args
-    ///     P_j GAT args
-    /// ```
-    pub fn rebase_inherent_args_onto_impl(
-        self,
-        impl_args: I::GenericArgs,
-        interner: I,
-    ) -> I::GenericArgs {
-        debug_assert_eq!(self.kind(interner), AliasTyKind::Inherent);
-        interner.mk_args_from_iter(impl_args.iter().chain(self.args.iter().skip(1)))
-    }
-}
-
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 #[cfg_attr(
     feature = "nightly",
diff --git a/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr b/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr
index a05aaf2af64..1aac357d60e 100644
--- a/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr
+++ b/tests/ui/const-generics/generic_const_exprs/cross_crate_predicate.stderr
@@ -39,14 +39,6 @@ error: unconstrained generic constant
 LL |     let _ = const_evaluatable_lib::test1::<T>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: required by a bound in `test1`
-  --> $DIR/auxiliary/const_evaluatable_lib.rs:5:10
-   |
-LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
-   |        ----- required by a bound in this function
-LL | where
-LL |     [u8; std::mem::size_of::<T>() - 1]: Sized,
-   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1`
 help: try adding a `where` bound
    |
 LL | fn user<T>() where [(); std::mem::size_of::<T>() - 1]: {
@@ -59,10 +51,13 @@ LL |     let _ = const_evaluatable_lib::test1::<T>();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: required by a bound in `test1`
-  --> $DIR/auxiliary/const_evaluatable_lib.rs:3:27
+  --> $DIR/auxiliary/const_evaluatable_lib.rs:5:10
    |
 LL | pub fn test1<T>() -> [u8; std::mem::size_of::<T>() - 1]
-   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1`
+   |        ----- required by a bound in this function
+LL | where
+LL |     [u8; std::mem::size_of::<T>() - 1]: Sized,
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `test1`
 help: try adding a `where` bound
    |
 LL | fn user<T>() where [(); std::mem::size_of::<T>() - 1]: {