about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/ast.rs48
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs17
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs3
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs28
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs36
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs327
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs12
-rw-r--r--compiler/rustc_middle/src/ty/assoc.rs20
-rw-r--r--compiler/rustc_passes/src/check_attr.rs21
-rw-r--r--compiler/rustc_resolve/src/late.rs18
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs2
18 files changed, 422 insertions, 136 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index a8d30949ea8..fb6b36e1a09 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -124,10 +124,19 @@ impl Path {
         self.segments.first().is_some_and(|segment| segment.ident.name == kw::PathRoot)
     }
 
-    /// If this path is a single identifier with no arguments, does not ensure
-    /// that the path resolves to a const param, the caller should check this.
-    pub fn is_potential_trivial_const_arg(&self) -> bool {
-        matches!(self.segments[..], [PathSegment { args: None, .. }])
+    /// Check if this path is potentially a trivial const arg, i.e., one that can _potentially_
+    /// be represented without an anon const in the HIR.
+    ///
+    /// If `allow_mgca_arg` is true (as should be the case in most situations when
+    /// `#![feature(min_generic_const_args)]` is enabled), then this always returns true
+    /// because all paths are valid.
+    ///
+    /// Otherwise, it returns true iff the path has exactly one segment, and it has no generic args
+    /// (i.e., it is _potentially_ a const parameter).
+    #[tracing::instrument(level = "debug", ret)]
+    pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
+        allow_mgca_arg
+            || self.segments.len() == 1 && self.segments.iter().all(|seg| seg.args.is_none())
     }
 }
 
@@ -1208,22 +1217,31 @@ pub struct Expr {
 }
 
 impl Expr {
-    /// Could this expr be either `N`, or `{ N }`, where `N` is a const parameter.
+    /// Check if this expression is potentially a trivial const arg, i.e., one that can _potentially_
+    /// be represented without an anon const in the HIR.
+    ///
+    /// This will unwrap at most one block level (curly braces). After that, if the expression
+    /// is a path, it mostly dispatches to [`Path::is_potential_trivial_const_arg`].
+    /// See there for more info about `allow_mgca_arg`.
     ///
-    /// If this is not the case, name resolution does not resolve `N` when using
-    /// `min_const_generics` as more complex expressions are not supported.
+    /// The only additional thing to note is that when `allow_mgca_arg` is false, this function
+    /// will only allow paths with no qself, before dispatching to the `Path` function of
+    /// the same name.
     ///
-    /// Does not ensure that the path resolves to a const param, the caller should check this.
+    /// Does not ensure that the path resolves to a const param/item, the caller should check this.
     /// This also does not consider macros, so it's only correct after macro-expansion.
-    pub fn is_potential_trivial_const_arg(&self) -> bool {
+    pub fn is_potential_trivial_const_arg(&self, allow_mgca_arg: bool) -> bool {
         let this = self.maybe_unwrap_block();
-
-        if let ExprKind::Path(None, path) = &this.kind
-            && path.is_potential_trivial_const_arg()
-        {
-            true
+        if allow_mgca_arg {
+            matches!(this.kind, ExprKind::Path(..))
         } else {
-            false
+            if let ExprKind::Path(None, path) = &this.kind
+                && path.is_potential_trivial_const_arg(allow_mgca_arg)
+            {
+                true
+            } else {
+                false
+            }
         }
     }
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 13edcc10c9e..c0188dde565 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1094,7 +1094,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             .and_then(|partial_res| partial_res.full_res())
                         {
                             if !res.matches_ns(Namespace::TypeNS)
-                                && path.is_potential_trivial_const_arg()
+                                && path.is_potential_trivial_const_arg(false)
                             {
                                 debug!(
                                     "lower_generic_arg: Lowering type argument as const argument: {:?}",
@@ -2061,8 +2061,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     ) -> &'hir hir::ConstArg<'hir> {
         let tcx = self.tcx;
 
-        // FIXME(min_generic_const_args): we only allow one-segment const paths for now
-        let ct_kind = if path.is_potential_trivial_const_arg()
+        let ct_kind = if path
+            .is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
             && (tcx.features().min_generic_const_args()
                 || matches!(res, Res::Def(DefKind::ConstParam, _)))
         {
@@ -2072,7 +2072,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 path,
                 ParamMode::Optional,
                 AllowReturnTypeNotation::No,
-                // FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support
+                // FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
                 ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                 None,
             );
@@ -2136,19 +2136,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         };
         let maybe_res =
             self.resolver.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res());
-        // FIXME(min_generic_const_args): we only allow one-segment const paths for now
-        if let ExprKind::Path(None, path) = &expr.kind
-            && path.is_potential_trivial_const_arg()
+        if let ExprKind::Path(qself, path) = &expr.kind
+            && path.is_potential_trivial_const_arg(tcx.features().min_generic_const_args())
             && (tcx.features().min_generic_const_args()
                 || matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))))
         {
             let qpath = self.lower_qpath(
                 expr.id,
-                &None,
+                qself,
                 path,
                 ParamMode::Optional,
                 AllowReturnTypeNotation::No,
-                // FIXME(min_generic_const_args): update for `fn foo() -> Bar<FOO<impl Trait>>` support
+                // FIXME(mgca): update for `fn foo() -> Bar<FOO<impl Trait>>` support
                 ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                 None,
             );
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 90447da6680..6dbd8a7ac01 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -190,7 +190,8 @@ fn make_format_args(
                                 && let [stmt] = block.stmts.as_slice()
                                 && let StmtKind::Expr(expr) = &stmt.kind
                                 && let ExprKind::Path(None, path) = &expr.kind
-                                && path.is_potential_trivial_const_arg()
+                                && path.segments.len() == 1
+                                && path.segments[0].args.is_none()
                             {
                                 err.multipart_suggestion(
                                     "quote your inlined format argument to use as string literal",
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 306535bf764..40857e0066e 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -576,6 +576,13 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         EncodeCrossCrate::Yes, experimental!(patchable_function_entry)
     ),
 
+    // Probably temporary component of min_generic_const_args.
+    // `#[type_const] const ASSOC: usize;`
+    gated!(
+        type_const, Normal, template!(Word), ErrorFollowing,
+        EncodeCrossCrate::Yes, min_generic_const_args, experimental!(type_const),
+    ),
+
     // ==========================================================================
     // Internal attributes: Stability, deprecation, and unsafe:
     // ==========================================================================
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 027aa5554a4..49523912b14 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -45,6 +45,7 @@ use tracing::{debug, instrument};
 
 use crate::check::intrinsic::intrinsic_operation_unsafety;
 use crate::errors;
+use crate::hir_ty_lowering::errors::assoc_kind_str;
 use crate::hir_ty_lowering::{FeedConstTy, HirTyLowerer, RegionInferReason};
 
 pub(crate) mod dump;
@@ -443,13 +444,14 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
         self.tcx.at(span).type_param_predicates((self.item_def_id, def_id, assoc_name))
     }
 
-    fn lower_assoc_ty(
+    fn lower_assoc_shared(
         &self,
         span: Span,
         item_def_id: DefId,
-        item_segment: &hir::PathSegment<'tcx>,
+        item_segment: &rustc_hir::PathSegment<'tcx>,
         poly_trait_ref: ty::PolyTraitRef<'tcx>,
-    ) -> Ty<'tcx> {
+        kind: ty::AssocKind,
+    ) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> {
         if let Some(trait_ref) = poly_trait_ref.no_bound_vars() {
             let item_args = self.lowerer().lower_generic_args_of_assoc_item(
                 span,
@@ -457,7 +459,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
                 item_segment,
                 trait_ref.args,
             );
-            Ty::new_projection_from_args(self.tcx(), item_def_id, item_args)
+            Ok((item_def_id, item_args))
         } else {
             // There are no late-bound regions; we can just ignore the binder.
             let (mut mpart_sugg, mut inferred_sugg) = (None, None);
@@ -518,16 +520,14 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
                 }
                 _ => {}
             }
-            Ty::new_error(
-                self.tcx(),
-                self.tcx().dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams {
-                    span,
-                    inferred_sugg,
-                    bound,
-                    mpart_sugg,
-                    what: "type",
-                }),
-            )
+
+            Err(self.tcx().dcx().emit_err(errors::AssociatedItemTraitUninferredGenericParams {
+                span,
+                inferred_sugg,
+                bound,
+                mpart_sugg,
+                what: assoc_kind_str(kind),
+            }))
         }
     }
 
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index dd346ed1f97..60af36da6e2 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -468,11 +468,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
                 // Good error for `where Trait::method(..): Send`.
                 let Some(self_ty) = opt_self_ty else {
-                    return self.error_missing_qpath_self_ty(
+                    let guar = self.error_missing_qpath_self_ty(
                         trait_def_id,
                         hir_ty.span,
                         item_segment,
+                        ty::AssocKind::Type,
                     );
+                    return Ty::new_error(tcx, guar);
                 };
                 let self_ty = self.lower_ty(self_ty);
 
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 922578792ba..d51fd7f7e78 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -385,14 +385,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         })
     }
 
-    pub(super) fn report_ambiguous_assoc_ty(
+    pub(super) fn report_ambiguous_assoc(
         &self,
         span: Span,
         types: &[String],
         traits: &[String],
         name: Symbol,
+        kind: ty::AssocKind,
     ) -> ErrorGuaranteed {
-        let mut err = struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type");
+        let kind_str = assoc_kind_str(kind);
+        let mut err =
+            struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated {kind_str}");
         if self
             .tcx()
             .resolutions(())
@@ -417,7 +420,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         span,
                         format!(
                             "if there were a type named `Type` that implements a trait named \
-                             `Trait` with associated type `{name}`, you could use the \
+                             `Trait` with associated {kind_str} `{name}`, you could use the \
                              fully-qualified path",
                         ),
                         format!("<Type as Trait>::{name}"),
@@ -440,7 +443,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         span,
                         format!(
                             "if there were a type named `Example` that implemented one of the \
-                             traits with associated type `{name}`, you could use the \
+                             traits with associated {kind_str} `{name}`, you could use the \
                              fully-qualified path",
                         ),
                         traits.iter().map(|trait_str| format!("<Example as {trait_str}>::{name}")),
@@ -451,7 +454,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     err.span_suggestion_verbose(
                         span,
                         format!(
-                            "if there were a trait named `Example` with associated type `{name}` \
+                            "if there were a trait named `Example` with associated {kind_str} `{name}` \
                              implemented for `{type_str}`, you could use the fully-qualified path",
                         ),
                         format!("<{type_str} as Example>::{name}"),
@@ -462,7 +465,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     err.span_suggestions(
                         span,
                         format!(
-                            "if there were a trait named `Example` with associated type `{name}` \
+                            "if there were a trait named `Example` with associated {kind_str} `{name}` \
                              implemented for one of the types, you could use the fully-qualified \
                              path",
                         ),
@@ -491,7 +494,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         err.emit()
     }
 
-    pub(crate) fn complain_about_ambiguous_inherent_assoc_ty(
+    pub(crate) fn complain_about_ambiguous_inherent_assoc(
         &self,
         name: Ident,
         candidates: Vec<DefId>,
@@ -552,13 +555,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     }
 
     // FIXME(inherent_associated_types): Find similarly named associated types and suggest them.
-    pub(crate) fn complain_about_inherent_assoc_ty_not_found(
+    pub(crate) fn complain_about_inherent_assoc_not_found(
         &self,
         name: Ident,
         self_ty: Ty<'tcx>,
         candidates: Vec<(DefId, (DefId, DefId))>,
         fulfillment_errors: Vec<FulfillmentError<'tcx>>,
         span: Span,
+        kind: ty::AssocKind,
     ) -> ErrorGuaranteed {
         // FIXME(fmease): This was copied in parts from an old version of `rustc_hir_typeck::method::suggest`.
         // Either
@@ -568,12 +572,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         let tcx = self.tcx();
 
+        let kind_str = assoc_kind_str(kind);
         let adt_did = self_ty.ty_adt_def().map(|def| def.did());
         let add_def_label = |err: &mut Diag<'_>| {
             if let Some(did) = adt_did {
                 err.span_label(
                     tcx.def_span(did),
-                    format!("associated item `{name}` not found for this {}", tcx.def_descr(did)),
+                    format!(
+                        "associated {kind_str} `{name}` not found for this {}",
+                        tcx.def_descr(did)
+                    ),
                 );
             }
         };
@@ -600,11 +608,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 self.dcx(),
                 name.span,
                 E0220,
-                "associated type `{name}` not found for `{self_ty}` in the current scope"
+                "associated {kind_str} `{name}` not found for `{self_ty}` in the current scope"
             );
             err.span_label(name.span, format!("associated item not found in `{self_ty}`"));
             err.note(format!(
-                "the associated type was found for\n{type_candidates}{additional_types}",
+                "the associated {kind_str} was found for\n{type_candidates}{additional_types}",
             ));
             add_def_label(&mut err);
             return err.emit();
@@ -685,7 +693,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         let mut err = self.dcx().struct_span_err(
             name.span,
-            format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied")
+            format!("the associated {kind_str} `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied")
         );
         if !bounds.is_empty() {
             err.note(format!(
@@ -695,7 +703,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
         err.span_label(
             name.span,
-            format!("associated type cannot be referenced on `{self_ty}` due to unsatisfied trait bounds")
+            format!("associated {kind_str} cannot be referenced on `{self_ty}` due to unsatisfied trait bounds")
         );
 
         for (span, mut bounds) in bound_spans {
@@ -1614,7 +1622,7 @@ fn generics_args_err_extend<'a>(
     }
 }
 
-pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
+pub(crate) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str {
     match kind {
         ty::AssocKind::Fn => "function",
         ty::AssocKind::Const => "constant",
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index fa58f12c553..60a60f6415a 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -115,7 +115,7 @@ fn generic_arg_mismatch_err(
                     body.value.kind
                 && let Res::Def(DefKind::Fn { .. }, id) = path.res
             {
-                // FIXME(min_generic_const_args): this branch is dead once new const path lowering
+                // FIXME(mgca): this branch is dead once new const path lowering
                 // (for single-segment paths) is no longer gated
                 err.help(format!("`{}` is a function item, not a type", tcx.item_name(id)));
                 err.help("function item types cannot be named directly");
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index a0f848f07c4..d044688246f 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -44,14 +44,16 @@ use rustc_middle::ty::{
 };
 use rustc_middle::{bug, span_bug};
 use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
+use rustc_session::parse::feature_err;
 use rustc_span::edit_distance::find_best_match_for_name;
-use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw};
+use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::wf::object_region_bounds;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
 use rustc_type_ir::Upcast;
 use tracing::{debug, instrument};
 
+use self::errors::assoc_kind_str;
 use crate::check::check_abi_fn_ptr;
 use crate::errors::{
     AmbiguousLifetimeBound, BadReturnTypeNotation, InvalidBaseType, NoVariantNamed,
@@ -152,7 +154,7 @@ pub trait HirTyLowerer<'tcx> {
         assoc_name: Ident,
     ) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]>;
 
-    /// Lower an associated type to a projection.
+    /// Lower an associated type/const (from a trait) to a projection.
     ///
     /// This method has to be defined by the concrete lowering context because
     /// dealing with higher-ranked trait references depends on its capabilities:
@@ -164,13 +166,14 @@ pub trait HirTyLowerer<'tcx> {
     ///
     /// The canonical example of this is associated type `T::P` where `T` is a type
     /// param constrained by `T: for<'a> Trait<'a>` and where `Trait` defines `P`.
-    fn lower_assoc_ty(
+    fn lower_assoc_shared(
         &self,
         span: Span,
         item_def_id: DefId,
         item_segment: &hir::PathSegment<'tcx>,
         poly_trait_ref: ty::PolyTraitRef<'tcx>,
-    ) -> Ty<'tcx>;
+        kind: ty::AssocKind,
+    ) -> Result<(DefId, GenericArgsRef<'tcx>), ErrorGuaranteed>;
 
     fn lower_fn_sig(
         &self,
@@ -245,6 +248,42 @@ pub enum FeedConstTy<'a, 'tcx> {
     No,
 }
 
+#[derive(Debug, Clone, Copy)]
+enum LowerAssocMode {
+    Type { permit_variants: bool },
+    Const,
+}
+
+impl LowerAssocMode {
+    fn kind(self) -> ty::AssocKind {
+        match self {
+            LowerAssocMode::Type { .. } => ty::AssocKind::Type,
+            LowerAssocMode::Const => ty::AssocKind::Const,
+        }
+    }
+
+    fn def_kind(self) -> DefKind {
+        match self {
+            LowerAssocMode::Type { .. } => DefKind::AssocTy,
+            LowerAssocMode::Const => DefKind::AssocConst,
+        }
+    }
+
+    fn permit_variants(self) -> bool {
+        match self {
+            LowerAssocMode::Type { permit_variants } => permit_variants,
+            // FIXME(mgca): Support paths like `Option::<T>::None` or `Option::<T>::Some` which resolve to const ctors/fn items respectively
+            LowerAssocMode::Const => false,
+        }
+    }
+}
+
+#[derive(Debug, Clone, Copy)]
+enum LoweredAssoc<'tcx> {
+    Term(DefId, GenericArgsRef<'tcx>),
+    Variant { adt: Ty<'tcx>, variant_did: DefId },
+}
+
 /// New-typed boolean indicating whether explicit late-bound lifetimes
 /// are present in a set of generic arguments.
 ///
@@ -630,7 +669,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         (args, arg_count)
     }
 
-    #[instrument(level = "debug", skip_all)]
+    #[instrument(level = "debug", skip(self))]
     pub fn lower_generic_args_of_assoc_item(
         &self,
         span: Span,
@@ -638,7 +677,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         item_segment: &hir::PathSegment<'tcx>,
         parent_args: GenericArgsRef<'tcx>,
     ) -> GenericArgsRef<'tcx> {
-        debug!(?span, ?item_def_id, ?item_segment);
         let (args, _) =
             self.lower_generic_args_of_path(span, item_def_id, parent_args, item_segment, None);
         if let Some(c) = item_segment.args().constraints.first() {
@@ -1118,7 +1156,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     // NOTE: When this function starts resolving `Trait::AssocTy` successfully
     // it should also start reporting the `BARE_TRAIT_OBJECTS` lint.
     #[instrument(level = "debug", skip_all, ret)]
-    pub fn lower_assoc_path(
+    pub fn lower_assoc_path_ty(
         &self,
         hir_ref_id: HirId,
         span: Span,
@@ -1127,6 +1165,72 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         assoc_segment: &'tcx hir::PathSegment<'tcx>,
         permit_variants: bool,
     ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
+        let tcx = self.tcx();
+        match self.lower_assoc_path_shared(
+            hir_ref_id,
+            span,
+            qself_ty,
+            qself,
+            assoc_segment,
+            LowerAssocMode::Type { permit_variants },
+        )? {
+            LoweredAssoc::Term(def_id, args) => {
+                let alias_ty = ty::AliasTy::new_from_args(tcx, def_id, args);
+                let ty = Ty::new_alias(tcx, alias_ty.kind(tcx), alias_ty);
+                Ok((ty, tcx.def_kind(def_id), def_id))
+            }
+            LoweredAssoc::Variant { adt, variant_did } => Ok((adt, DefKind::Variant, variant_did)),
+        }
+    }
+
+    #[instrument(level = "debug", skip_all, ret)]
+    fn lower_assoc_path_const(
+        &self,
+        hir_ref_id: HirId,
+        span: Span,
+        qself_ty: Ty<'tcx>,
+        qself: &'tcx hir::Ty<'tcx>,
+        assoc_segment: &'tcx hir::PathSegment<'tcx>,
+    ) -> Result<Const<'tcx>, ErrorGuaranteed> {
+        let tcx = self.tcx();
+        let (def_id, args) = match self.lower_assoc_path_shared(
+            hir_ref_id,
+            span,
+            qself_ty,
+            qself,
+            assoc_segment,
+            LowerAssocMode::Const,
+        )? {
+            LoweredAssoc::Term(def_id, args) => {
+                if !tcx.associated_item(def_id).is_type_const_capable(tcx) {
+                    let mut err = tcx.dcx().struct_span_err(
+                        span,
+                        "use of trait associated const without `#[type_const]`",
+                    );
+                    err.note("the declaration in the trait must be marked with `#[type_const]`");
+                    return Err(err.emit());
+                }
+                (def_id, args)
+            }
+            // FIXME(mgca): implement support for this once ready to support all adt ctor expressions,
+            // not just const ctors
+            LoweredAssoc::Variant { .. } => {
+                span_bug!(span, "unexpected variant res for type associated const path")
+            }
+        };
+        Ok(Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(def_id, args)))
+    }
+
+    #[instrument(level = "debug", skip_all, ret)]
+    fn lower_assoc_path_shared(
+        &self,
+        hir_ref_id: HirId,
+        span: Span,
+        qself_ty: Ty<'tcx>,
+        qself: &'tcx hir::Ty<'tcx>,
+        assoc_segment: &'tcx hir::PathSegment<'tcx>,
+        mode: LowerAssocMode,
+    ) -> Result<LoweredAssoc<'tcx>, ErrorGuaranteed> {
         debug!(%qself_ty, ?assoc_segment.ident);
         let tcx = self.tcx();
 
@@ -1141,13 +1245,16 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     .iter()
                     .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did()));
                 if let Some(variant_def) = variant_def {
-                    if permit_variants {
+                    if mode.permit_variants() {
                         tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None);
                         let _ = self.prohibit_generic_args(
                             slice::from_ref(assoc_segment).iter(),
                             GenericsArgsErrExtend::EnumVariant { qself, assoc_segment, adt_def },
                         );
-                        return Ok((qself_ty, DefKind::Variant, variant_def.def_id));
+                        return Ok(LoweredAssoc::Variant {
+                            adt: qself_ty,
+                            variant_did: variant_def.def_id,
+                        });
                     } else {
                         variant_resolution = Some(variant_def.def_id);
                     }
@@ -1155,15 +1262,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
 
             // FIXME(inherent_associated_types, #106719): Support self types other than ADTs.
-            if let Some((ty, did)) = self.probe_inherent_assoc_ty(
-                assoc_ident,
+            if let Some((did, args)) = self.probe_inherent_assoc_shared(
                 assoc_segment,
                 adt_def.did(),
                 qself_ty,
                 hir_ref_id,
                 span,
+                mode.kind(),
             )? {
-                return Ok((ty, DefKind::AssocTy, did));
+                return Ok(LoweredAssoc::Term(did, args));
             }
         }
 
@@ -1192,7 +1299,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         )
                     },
                     AssocItemQSelf::SelfTyAlias,
-                    ty::AssocKind::Type,
+                    mode.kind(),
                     assoc_ident,
                     span,
                     None,
@@ -1204,14 +1311,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             ) => self.probe_single_ty_param_bound_for_assoc_item(
                 param_did.expect_local(),
                 qself.span,
-                ty::AssocKind::Type,
+                mode.kind(),
                 assoc_ident,
                 span,
             )?,
             _ => {
+                let kind_str = assoc_kind_str(mode.kind());
                 let reported = if variant_resolution.is_some() {
                     // Variant in type position
-                    let msg = format!("expected type, found variant `{assoc_ident}`");
+                    let msg = format!("expected {kind_str}, found variant `{assoc_ident}`");
                     self.dcx().span_err(span, msg)
                 } else if qself_ty.is_enum() {
                     let mut err = self.dcx().create_err(NoVariantNamed {
@@ -1310,11 +1418,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         self.probe_traits_that_match_assoc_ty(qself_ty, assoc_ident);
 
                     // Don't print `ty::Error` to the user.
-                    self.report_ambiguous_assoc_ty(
+                    self.report_ambiguous_assoc(
                         span,
                         &[qself_ty.to_string()],
                         &traits,
                         assoc_ident.name,
+                        mode.kind(),
                     )
                 };
                 return Err(reported);
@@ -1322,10 +1431,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         };
 
         let trait_did = bound.def_id();
-        let assoc_ty = self
-            .probe_assoc_item(assoc_ident, ty::AssocKind::Type, hir_ref_id, span, trait_did)
-            .expect("failed to find associated type");
-        let ty = self.lower_assoc_ty(span, assoc_ty.def_id, assoc_segment, bound);
+        let assoc_item = self
+            .probe_assoc_item(assoc_ident, mode.kind(), hir_ref_id, span, trait_did)
+            .expect("failed to find associated item");
+        let (def_id, args) =
+            self.lower_assoc_shared(span, assoc_item.def_id, assoc_segment, bound, mode.kind())?;
+        let result = LoweredAssoc::Term(def_id, args);
 
         if let Some(variant_def_id) = variant_resolution {
             tcx.node_span_lint(AMBIGUOUS_ASSOCIATED_ITEMS, hir_ref_id, span, |lint| {
@@ -1341,7 +1452,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 };
 
                 could_refer_to(DefKind::Variant, variant_def_id, "");
-                could_refer_to(DefKind::AssocTy, assoc_ty.def_id, " also");
+                could_refer_to(mode.def_kind(), assoc_item.def_id, " also");
 
                 lint.span_suggestion(
                     span,
@@ -1351,36 +1462,51 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 );
             });
         }
-        Ok((ty, DefKind::AssocTy, assoc_ty.def_id))
+        Ok(result)
     }
 
-    fn probe_inherent_assoc_ty(
+    fn probe_inherent_assoc_shared(
         &self,
-        name: Ident,
         segment: &hir::PathSegment<'tcx>,
         adt_did: DefId,
         self_ty: Ty<'tcx>,
         block: HirId,
         span: Span,
-    ) -> Result<Option<(Ty<'tcx>, DefId)>, ErrorGuaranteed> {
+        kind: ty::AssocKind,
+    ) -> Result<Option<(DefId, GenericArgsRef<'tcx>)>, ErrorGuaranteed> {
         let tcx = self.tcx();
 
-        // Don't attempt to look up inherent associated types when the feature is not enabled.
-        // Theoretically it'd be fine to do so since we feature-gate their definition site.
-        // However, due to current limitations of the implementation (caused by us performing
-        // selection during HIR ty lowering instead of in the trait solver), IATs can lead to cycle
-        // errors (#108491) which mask the feature-gate error, needlessly confusing users
-        // who use IATs by accident (#113265).
         if !tcx.features().inherent_associated_types() {
-            return Ok(None);
+            match kind {
+                // Don't attempt to look up inherent associated types when the feature is not enabled.
+                // Theoretically it'd be fine to do so since we feature-gate their definition site.
+                // However, due to current limitations of the implementation (caused by us performing
+                // selection during HIR ty lowering instead of in the trait solver), IATs can lead to cycle
+                // errors (#108491) which mask the feature-gate error, needlessly confusing users
+                // who use IATs by accident (#113265).
+                ty::AssocKind::Type => return Ok(None),
+                ty::AssocKind::Const => {
+                    // We also gate the mgca codepath for type-level uses of inherent consts
+                    // with the inherent_associated_types feature gate since it relies on the
+                    // same machinery and has similar rough edges.
+                    return Err(feature_err(
+                        &tcx.sess,
+                        sym::inherent_associated_types,
+                        span,
+                        "inherent associated types are unstable",
+                    )
+                    .emit());
+                }
+                ty::AssocKind::Fn => unreachable!(),
+            }
         }
 
+        let name = segment.ident;
         let candidates: Vec<_> = tcx
             .inherent_impls(adt_did)
             .iter()
             .filter_map(|&impl_| {
-                let (item, scope) =
-                    self.probe_assoc_item_unchecked(name, ty::AssocKind::Type, block, impl_)?;
+                let (item, scope) = self.probe_assoc_item_unchecked(name, kind, block, impl_)?;
                 Some((impl_, (item.def_id, scope)))
             })
             .collect();
@@ -1395,13 +1521,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         // In contexts that have no inference context, just make a new one.
         // We do need a local variable to store it, though.
-        let infcx_;
         let infcx = match self.infcx() {
             Some(infcx) => infcx,
             None => {
                 assert!(!self_ty.has_infer());
-                infcx_ = tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis());
-                &infcx_
+                &tcx.infer_ctxt().ignoring_regions().build(TypingMode::non_body_analysis())
             }
         };
 
@@ -1420,8 +1544,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             &mut universes,
             self_ty,
             |self_ty| {
-                self.select_inherent_assoc_type_candidates(
-                    infcx, name, span, self_ty, param_env, candidates,
+                self.select_inherent_assoc_candidates(
+                    infcx, name, span, self_ty, param_env, candidates, kind,
                 )
             },
         )?;
@@ -1438,13 +1562,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 .chain(args.into_iter().skip(parent_args.len())),
         );
 
-        let ty =
-            Ty::new_alias(tcx, ty::Inherent, ty::AliasTy::new_from_args(tcx, assoc_item, args));
-
-        Ok(Some((ty, assoc_item)))
+        Ok(Some((assoc_item, args)))
     }
 
-    fn select_inherent_assoc_type_candidates(
+    fn select_inherent_assoc_candidates(
         &self,
         infcx: &InferCtxt<'tcx>,
         name: Ident,
@@ -1452,6 +1573,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         self_ty: Ty<'tcx>,
         param_env: ParamEnv<'tcx>,
         candidates: Vec<(DefId, (DefId, DefId))>,
+        kind: ty::AssocKind,
     ) -> Result<(DefId, (DefId, DefId)), ErrorGuaranteed> {
         let tcx = self.tcx();
         let mut fulfillment_errors = Vec::new();
@@ -1496,17 +1618,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             .collect();
 
         match &applicable_candidates[..] {
-            &[] => Err(self.complain_about_inherent_assoc_ty_not_found(
+            &[] => Err(self.complain_about_inherent_assoc_not_found(
                 name,
                 self_ty,
                 candidates,
                 fulfillment_errors,
                 span,
+                kind,
             )),
 
             &[applicable_candidate] => Ok(applicable_candidate),
 
-            &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc_ty(
+            &[_, ..] => Err(self.complain_about_ambiguous_inherent_assoc(
                 name,
                 applicable_candidates.into_iter().map(|(_, (candidate, _))| candidate).collect(),
                 span,
@@ -1638,7 +1761,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
     /// Lower a qualified path to a type.
     #[instrument(level = "debug", skip_all)]
-    fn lower_qpath(
+    fn lower_qpath_ty(
         &self,
         span: Span,
         opt_self_ty: Option<Ty<'tcx>>,
@@ -1646,13 +1769,64 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         trait_segment: &hir::PathSegment<'tcx>,
         item_segment: &hir::PathSegment<'tcx>,
     ) -> Ty<'tcx> {
+        match self.lower_qpath_shared(
+            span,
+            opt_self_ty,
+            item_def_id,
+            trait_segment,
+            item_segment,
+            ty::AssocKind::Type,
+        ) {
+            Ok((item_def_id, item_args)) => {
+                Ty::new_projection_from_args(self.tcx(), item_def_id, item_args)
+            }
+            Err(guar) => Ty::new_error(self.tcx(), guar),
+        }
+    }
+
+    /// Lower a qualified path to a const.
+    #[instrument(level = "debug", skip_all)]
+    fn lower_qpath_const(
+        &self,
+        span: Span,
+        opt_self_ty: Option<Ty<'tcx>>,
+        item_def_id: DefId,
+        trait_segment: &hir::PathSegment<'tcx>,
+        item_segment: &hir::PathSegment<'tcx>,
+    ) -> Const<'tcx> {
+        match self.lower_qpath_shared(
+            span,
+            opt_self_ty,
+            item_def_id,
+            trait_segment,
+            item_segment,
+            ty::AssocKind::Const,
+        ) {
+            Ok((item_def_id, item_args)) => {
+                let uv = ty::UnevaluatedConst::new(item_def_id, item_args);
+                Const::new_unevaluated(self.tcx(), uv)
+            }
+            Err(guar) => Const::new_error(self.tcx(), guar),
+        }
+    }
+
+    #[instrument(level = "debug", skip_all)]
+    fn lower_qpath_shared(
+        &self,
+        span: Span,
+        opt_self_ty: Option<Ty<'tcx>>,
+        item_def_id: DefId,
+        trait_segment: &hir::PathSegment<'tcx>,
+        item_segment: &hir::PathSegment<'tcx>,
+        kind: ty::AssocKind,
+    ) -> Result<(DefId, GenericArgsRef<'tcx>), ErrorGuaranteed> {
         let tcx = self.tcx();
 
         let trait_def_id = tcx.parent(item_def_id);
         debug!(?trait_def_id);
 
         let Some(self_ty) = opt_self_ty else {
-            return self.error_missing_qpath_self_ty(trait_def_id, span, item_segment);
+            return Err(self.error_missing_qpath_self_ty(trait_def_id, span, item_segment, kind));
         };
         debug!(?self_ty);
 
@@ -1663,7 +1837,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let item_args =
             self.lower_generic_args_of_assoc_item(span, item_def_id, item_segment, trait_ref.args);
 
-        Ty::new_projection_from_args(tcx, item_def_id, item_args)
+        Ok((item_def_id, item_args))
     }
 
     fn error_missing_qpath_self_ty(
@@ -1671,7 +1845,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         trait_def_id: DefId,
         span: Span,
         item_segment: &hir::PathSegment<'tcx>,
-    ) -> Ty<'tcx> {
+        kind: ty::AssocKind,
+    ) -> ErrorGuaranteed {
         let tcx = self.tcx();
         let path_str = tcx.def_path_str(trait_def_id);
 
@@ -1707,9 +1882,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         // FIXME: also look at `tcx.generics_of(self.item_def_id()).params` any that
         // references the trait. Relevant for the first case in
         // `src/test/ui/associated-types/associated-types-in-ambiguous-context.rs`
-        let reported =
-            self.report_ambiguous_assoc_ty(span, &type_names, &[path_str], item_segment.ident.name);
-        Ty::new_error(tcx, reported)
+        self.report_ambiguous_assoc(span, &type_names, &[path_str], item_segment.ident.name, kind)
     }
 
     pub fn prohibit_generic_args<'a>(
@@ -2013,7 +2186,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     path.segments[..path.segments.len() - 2].iter(),
                     GenericsArgsErrExtend::None,
                 );
-                self.lower_qpath(
+                self.lower_qpath_ty(
                     span,
                     opt_self_ty,
                     def_id,
@@ -2167,11 +2340,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let opt_self_ty = maybe_qself.as_ref().map(|qself| self.lower_ty(qself));
                 self.lower_const_path_resolved(opt_self_ty, path, hir_id)
             }
-            hir::ConstArgKind::Path(qpath) => ty::Const::new_error_with_message(
-                tcx,
-                qpath.span(),
-                format!("Const::lower_const_arg: invalid qpath {qpath:?}"),
-            ),
+            hir::ConstArgKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
+                debug!(?qself, ?segment);
+                let ty = self.lower_ty(qself);
+                self.lower_assoc_path_const(hir_id, const_arg.span(), ty, qself, segment)
+                    .unwrap_or_else(|guar| Const::new_error(tcx, guar))
+            }
+            hir::ConstArgKind::Path(qpath @ hir::QPath::LangItem(..)) => {
+                ty::Const::new_error_with_message(
+                    tcx,
+                    qpath.span(),
+                    format!("Const::lower_const_arg: invalid qpath {qpath:?}"),
+                )
+            }
             hir::ConstArgKind::Anon(anon) => self.lower_anon_const(anon),
             hir::ConstArgKind::Infer(span, ()) => self.ct_infer(None, span),
         }
@@ -2207,6 +2388,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 );
                 ty::Const::new_unevaluated(tcx, ty::UnevaluatedConst::new(did, args))
             }
+            Res::Def(DefKind::AssocConst, did) => {
+                debug_assert!(path.segments.len() >= 2);
+                let _ = self.prohibit_generic_args(
+                    path.segments[..path.segments.len() - 2].iter(),
+                    GenericsArgsErrExtend::None,
+                );
+                self.lower_qpath_const(
+                    span,
+                    opt_self_ty,
+                    did,
+                    &path.segments[path.segments.len() - 2],
+                    path.segments.last().unwrap(),
+                )
+            }
             Res::Def(DefKind::Static { .. }, _) => {
                 span_bug!(span, "use of bare `static` ConstArgKind::Path's not yet supported")
             }
@@ -2223,7 +2418,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
             // Exhaustive match to be clear about what exactly we're considering to be
             // an invalid Res for a const path.
-            Res::Def(
+            res @ (Res::Def(
                 DefKind::Mod
                 | DefKind::Enum
                 | DefKind::Variant
@@ -2237,7 +2432,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 | DefKind::Union
                 | DefKind::Trait
                 | DefKind::ForeignTy
-                | DefKind::AssocConst
                 | DefKind::TyParam
                 | DefKind::Macro(_)
                 | DefKind::LifetimeParam
@@ -2260,12 +2454,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             | Res::Local(_)
             | Res::ToolMod
             | Res::NonMacroAttr(_)
-            | Res::Err => Const::new_error_with_message(tcx, span, "invalid Res for const path"),
+            | Res::Err) => Const::new_error_with_message(
+                tcx,
+                span,
+                format!("invalid Res {res:?} for const path"),
+            ),
         }
     }
 
-    /// Literals and const generic parameters are eagerly converted to a constant, everything else
-    /// becomes `Unevaluated`.
+    /// Literals are eagerly converted to a constant, everything else becomes `Unevaluated`.
     #[instrument(skip(self), level = "debug")]
     fn lower_anon_const(&self, anon: &AnonConst) -> Const<'tcx> {
         let tcx = self.tcx();
@@ -2464,7 +2661,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             hir::TyKind::Path(hir::QPath::TypeRelative(qself, segment)) => {
                 debug!(?qself, ?segment);
                 let ty = self.lower_ty(qself);
-                self.lower_assoc_path(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false)
+                self.lower_assoc_path_ty(hir_ty.hir_id, hir_ty.span, ty, qself, segment, false)
                     .map(|(ty, _, _)| ty)
                     .unwrap_or_else(|guar| Ty::new_error(tcx, guar))
             }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index e60a4bb47b5..82e99ca7afa 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -2174,7 +2174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 let result = self
                     .lowerer()
-                    .lower_assoc_path(hir_id, path_span, ty.raw, qself, segment, true);
+                    .lower_assoc_path_ty(hir_id, path_span, ty.raw, qself, segment, true);
                 let ty = result
                     .map(|(ty, _, _)| ty)
                     .unwrap_or_else(|guar| Ty::new_error(self.tcx(), guar));
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 42236ac6d80..e14f1528d2c 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -17,7 +17,7 @@ use rustc_infer::infer;
 use rustc_infer::traits::Obligation;
 use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeVisitableExt};
 use rustc_session::Session;
-use rustc_span::{self, DUMMY_SP, Ident, Span, sym};
+use rustc_span::{self, DUMMY_SP, ErrorGuaranteed, Ident, Span, sym};
 use rustc_trait_selection::error_reporting::TypeErrCtxt;
 use rustc_trait_selection::error_reporting::infer::sub_relations::SubRelations;
 use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode, ObligationCtxt};
@@ -308,15 +308,17 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
         ))
     }
 
-    fn lower_assoc_ty(
+    fn lower_assoc_shared(
         &self,
         span: Span,
         item_def_id: DefId,
-        item_segment: &hir::PathSegment<'tcx>,
+        item_segment: &rustc_hir::PathSegment<'tcx>,
         poly_trait_ref: ty::PolyTraitRef<'tcx>,
-    ) -> Ty<'tcx> {
+        _kind: ty::AssocKind,
+    ) -> Result<(DefId, ty::GenericArgsRef<'tcx>), ErrorGuaranteed> {
         let trait_ref = self.instantiate_binder_with_fresh_vars(
             span,
+            // FIXME(mgca): this should be assoc const if that is the `kind`
             infer::BoundRegionConversionTime::AssocTypeProjection(item_def_id),
             poly_trait_ref,
         );
@@ -328,7 +330,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
             trait_ref.args,
         );
 
-        Ty::new_projection_from_args(self.tcx(), item_def_id, item_args)
+        Ok((item_def_id, item_args))
     }
 
     fn probe_adt(&self, span: Span, ty: Ty<'tcx>) -> Option<ty::AdtDef<'tcx>> {
diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs
index 6309dd2e490..ce4c08aa485 100644
--- a/compiler/rustc_middle/src/ty/assoc.rs
+++ b/compiler/rustc_middle/src/ty/assoc.rs
@@ -3,7 +3,7 @@ use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Namespace};
 use rustc_hir::def_id::DefId;
 use rustc_macros::{Decodable, Encodable, HashStable};
-use rustc_span::{Ident, Symbol};
+use rustc_span::{Ident, Symbol, sym};
 
 use super::{TyCtxt, Visibility};
 use crate::ty;
@@ -108,6 +108,24 @@ impl AssocItem {
     pub fn is_impl_trait_in_trait(&self) -> bool {
         self.opt_rpitit_info.is_some()
     }
+
+    /// Returns true if:
+    /// - This trait associated item has the `#[type_const]` attribute,
+    /// - If it is in a trait impl, the item from the original trait has this attribute, or
+    /// - It is an inherent assoc const.
+    pub fn is_type_const_capable(&self, tcx: TyCtxt<'_>) -> bool {
+        if self.kind != ty::AssocKind::Const {
+            return false;
+        }
+
+        let def_id = match (self.container, self.trait_item_def_id) {
+            (AssocItemContainer::Trait, _) => self.def_id,
+            (AssocItemContainer::Impl, Some(trait_item_did)) => trait_item_did,
+            // Inherent impl but this attr is only applied to trait assoc items.
+            (AssocItemContainer::Impl, None) => return true,
+        };
+        tcx.has_attr(def_id, sym::type_const)
+    }
 }
 
 #[derive(Copy, Clone, PartialEq, Debug, HashStable, Eq, Hash, Encodable, Decodable)]
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index d6c0edf5ae7..91916b8f5fc 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -14,6 +14,7 @@ use rustc_attr_parsing::{AttributeKind, ReprAttr, find_attr};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Applicability, DiagCtxtHandle, IntoDiagArg, MultiSpan, StashKey};
 use rustc_feature::{AttributeDuplicates, AttributeType, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute};
+use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalModDefId;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{
@@ -257,6 +258,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         [sym::coroutine, ..] => {
                             self.check_coroutine(attr, target);
                         }
+                        [sym::type_const, ..] => {
+                            self.check_type_const(hir_id,attr, target);
+                        }
                         [sym::linkage, ..] => self.check_linkage(attr, span, target),
                         [sym::rustc_pub_transparent, ..] => self.check_rustc_pub_transparent(attr.span(), span, attrs),
                         [
@@ -2519,6 +2523,23 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
+    fn check_type_const(&self, hir_id: HirId, attr: &Attribute, target: Target) {
+        let tcx = self.tcx;
+        if target == Target::AssocConst
+            && let parent = tcx.parent(hir_id.expect_owner().to_def_id())
+            && self.tcx.def_kind(parent) == DefKind::Trait
+        {
+            return;
+        } else {
+            self.dcx()
+                .struct_span_err(
+                    attr.span(),
+                    "`#[type_const]` must only be applied to trait associated constants",
+                )
+                .emit();
+        }
+    }
+
     fn check_linkage(&self, attr: &Attribute, span: Span, target: Target) {
         match target {
             Target::Fn
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index f119ed55e7d..1e86b9721d4 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1202,7 +1202,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
                 if let TyKind::Path(None, ref path) = ty.kind
                     // We cannot disambiguate multi-segment paths right now as that requires type
                     // checking.
-                    && path.is_potential_trivial_const_arg()
+                    && path.is_potential_trivial_const_arg(false)
                 {
                     let mut check_ns = |ns| {
                         self.maybe_resolve_ident_in_lexical_scope(path.segments[0].ident, ns)
@@ -4630,11 +4630,12 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
             constant, anon_const_kind
         );
 
-        self.resolve_anon_const_manual(
-            constant.value.is_potential_trivial_const_arg(),
-            anon_const_kind,
-            |this| this.resolve_expr(&constant.value, None),
-        )
+        let is_trivial_const_arg = constant
+            .value
+            .is_potential_trivial_const_arg(self.r.tcx.features().min_generic_const_args());
+        self.resolve_anon_const_manual(is_trivial_const_arg, anon_const_kind, |this| {
+            this.resolve_expr(&constant.value, None)
+        })
     }
 
     /// There are a few places that we need to resolve an anon const but we did not parse an
@@ -4794,8 +4795,11 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
                     // Constant arguments need to be treated as AnonConst since
                     // that is how they will be later lowered to HIR.
                     if const_args.contains(&idx) {
+                        let is_trivial_const_arg = argument.is_potential_trivial_const_arg(
+                            self.r.tcx.features().min_generic_const_args(),
+                        );
                         self.resolve_anon_const_manual(
-                            argument.is_potential_trivial_const_arg(),
+                            is_trivial_const_arg,
                             AnonConstKind::ConstArg(IsRepeatExpr::No),
                             |this| this.resolve_expr(argument, None),
                         );
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 1dc84150cbe..522bccb1acb 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -2084,6 +2084,7 @@ symbols! {
         type_ascribe,
         type_ascription,
         type_changing_struct_update,
+        type_const,
         type_id,
         type_ir_inherent,
         type_length_limit,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index d270b4cf96b..8c1df9b5113 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -3250,7 +3250,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         span: Span,
     ) -> Result<Diag<'a>, ErrorGuaranteed> {
-        if !self.tcx.features().generic_const_exprs() {
+        if !self.tcx.features().generic_const_exprs()
+            && !self.tcx.features().min_generic_const_args()
+        {
             let guar = self
                 .dcx()
                 .struct_span_err(span, "constant expression depends on a generic parameter")
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index bdee6ca2afe..75f53b063d1 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -85,6 +85,12 @@ pub fn is_const_evaluatable<'tcx>(
             }
             _ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
         }
+    } else if tcx.features().min_generic_const_args() {
+        // This is a sanity check to make sure that non-generics consts are checked to
+        // be evaluatable in case they aren't cchecked elsewhere. This will NOT error
+        // if the const uses generics, as desired.
+        crate::traits::evaluate_const(infcx, unexpanded_ct, param_env);
+        Ok(())
     } else {
         let uv = match unexpanded_ct.kind() {
             ty::ConstKind::Unevaluated(uv) => uv,
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index a58317dd56d..97db1ddf4ff 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -410,7 +410,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         let predicate = obligation.predicate.skip_binder();
 
         let mut assume = predicate.trait_ref.args.const_at(2);
-        // FIXME(min_generic_const_exprs): We should shallowly normalize this.
+        // 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)
         }