about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-07-28 22:04:26 +0000
committerbors <bors@rust-lang.org>2023-07-28 22:04:26 +0000
commit04abc370b9f3855b28172b65a7f7d5a433f41412 (patch)
tree97b99183c3b23bc4b43096c77b50afae26a3ce4c
parentb6dd153fbcc06b3bc936aca800af4b8376c229e4 (diff)
parent203d4006688521b0e4e193a18b37d8fae7758d46 (diff)
downloadrust-04abc370b9f3855b28172b65a7f7d5a433f41412.tar.gz
rust-04abc370b9f3855b28172b65a7f7d5a433f41412.zip
Auto merge of #113522 - fmease:generic-consts, r=cjgillot
Implement generic const items

This implements generic parameters and where-clauses on free and associated const items under the experimental feature gate `generic_const_items`. See rust-lang/lang-team#214.

Tracking issue: #113521.
Fixes #104400.

This PR doesn't include rustfmt support as per [nightly style procedure](https://github.com/rust-lang/style-team/blob/master/nightly-style-procedure.md) and it doesn't add rustdoc JSON support (see [related Zulip topic](https://rust-lang.zulipchat.com/#narrow/stream/266220-rustdoc/topic/Rustdoc.20JSON.3A.20Experimental.20rustc.20features)).

CC `@scottmcm` `@compiler-errors` `@WalterSmuts`
r? `@oli-obk`

<details><summary>Resolved Questions</summary>

* Q: Should `const ADD<const N: usize, const M: usize>: usize = N + M; ADD::<0, 1>` trigger the error *generic parameters may not be used in const operations* (which can be unlocked with `#![feature(generic_const_exprs)]`). Currently it doesn't. Or does this fall under [this paragraph](https://github.com/rust-lang/rust/blob/71f71a5397c42fec01f5c1045c638d961fa9f7ca/compiler/rustc_resolve/src/late.rs#L191)?
  * A: No, https://github.com/rust-lang/rust/pull/113522#issuecomment-1628634092
* Q: Should `const UNUSED: () = () where String: Copy;` (with `#![feature(trivial_bounds)]` and with `UNUSED` unused) succeed compilation? Currently it doesn't: *evaluation of constant value failed // entering unreachable code*
  * A: Yes, but postponed until stabilization (latest), https://github.com/rust-lang/rust/pull/113522#issuecomment-1628634092

</details>
-rw-r--r--compiler/rustc_ast/src/ast.rs2
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs3
-rw-r--r--compiler/rustc_ast/src/visit.rs11
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs55
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs1
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/item.rs35
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs1
-rw-r--r--compiler/rustc_expand/src/build.rs11
-rw-r--r--compiler/rustc_feature/src/active.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs23
-rw-r--r--compiler/rustc_hir/src/intravisit.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs110
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs112
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs2
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs3
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs2
-rw-r--r--compiler/rustc_lint/src/builtin.rs3
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs2
-rw-r--r--compiler/rustc_parse/messages.ftl8
-rw-r--r--compiler/rustc_parse/src/errors.rs31
-rw-r--r--compiler/rustc_parse/src/parser/item.rs148
-rw-r--r--compiler/rustc_passes/src/reachable.rs2
-rw-r--r--compiler/rustc_resolve/src/late.rs170
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs8
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs2
-rw-r--r--src/librustdoc/clean/inline.rs5
-rw-r--r--src/librustdoc/clean/mod.rs65
-rw-r--r--src/librustdoc/clean/simplify.rs35
-rw-r--r--src/librustdoc/clean/types.rs16
-rw-r--r--src/librustdoc/html/render/mod.rs48
-rw-r--r--src/librustdoc/html/render/print_item.rs4
-rw-r--r--src/librustdoc/html/render/span_map.rs2
-rw-r--r--src/librustdoc/json/conversions.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/large_const_arrays.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/types/mod.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs2
-rw-r--r--src/tools/tidy/src/ui_tests.rs2
-rw-r--r--tests/rustdoc/generic-const-items.rs38
-rw-r--r--tests/rustdoc/inline_cross/auxiliary/generic-const-items.rs22
-rw-r--r--tests/rustdoc/inline_cross/generic-const-items.rs26
-rw-r--r--tests/ui/generic-const-items/associated-const-equality.rs22
-rw-r--r--tests/ui/generic-const-items/basic.rs61
-rw-r--r--tests/ui/generic-const-items/compare-impl-item.rs30
-rw-r--r--tests/ui/generic-const-items/compare-impl-item.stderr66
-rw-r--r--tests/ui/generic-const-items/const-trait-impl.rs24
-rw-r--r--tests/ui/generic-const-items/duplicate-where-clause.rs27
-rw-r--r--tests/ui/generic-const-items/duplicate-where-clause.stderr27
-rw-r--r--tests/ui/generic-const-items/elided-lifetimes.rs18
-rw-r--r--tests/ui/generic-const-items/elided-lifetimes.stderr35
-rw-r--r--tests/ui/generic-const-items/evaluatable-bounds.rs31
-rw-r--r--tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr10
-rw-r--r--tests/ui/generic-const-items/feature-gate-generic_const_items.rs37
-rw-r--r--tests/ui/generic-const-items/feature-gate-generic_const_items.stderr77
-rw-r--r--tests/ui/generic-const-items/inference-failure.rs15
-rw-r--r--tests/ui/generic-const-items/inference-failure.stderr20
-rw-r--r--tests/ui/generic-const-items/misplaced-where-clause.fixed18
-rw-r--r--tests/ui/generic-const-items/misplaced-where-clause.rs20
-rw-r--r--tests/ui/generic-const-items/misplaced-where-clause.stderr36
-rw-r--r--tests/ui/generic-const-items/parameter-defaults.rs14
-rw-r--r--tests/ui/generic-const-items/parameter-defaults.stderr8
-rw-r--r--tests/ui/generic-const-items/recursive.rs12
-rw-r--r--tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.rs12
-rw-r--r--tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.stderr18
-rw-r--r--tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.rs12
-rw-r--r--tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.stderr11
-rw-r--r--tests/ui/generic-const-items/unsatisfied-bounds.rs34
-rw-r--r--tests/ui/generic-const-items/unsatisfied-bounds.stderr62
-rw-r--r--tests/ui/generic-const-items/unsatisfied-evaluatable-bounds.rs12
-rw-r--r--tests/ui/generic-const-items/unsatisfied-evaluatable-bounds.stderr9
-rw-r--r--tests/ui/generic-const-items/unsatisfied-outlives-bounds.rs17
-rw-r--r--tests/ui/generic-const-items/unsatisfied-outlives-bounds.stderr18
-rw-r--r--tests/ui/object-safety/assoc_const_bounds.rs9
-rw-r--r--tests/ui/object-safety/assoc_const_bounds.stderr15
-rw-r--r--tests/ui/object-safety/assoc_const_bounds_sized.rs9
-rw-r--r--tests/ui/object-safety/assoc_const_bounds_sized.stderr15
-rw-r--r--tests/ui/parser/generic-statics.rs4
-rw-r--r--tests/ui/parser/generic-statics.stderr8
-rw-r--r--tests/ui/suggestions/missing-lifetime-in-assoc-const-type.default.stderr (renamed from tests/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr)8
-rw-r--r--tests/ui/suggestions/missing-lifetime-in-assoc-const-type.generic_const_items.stderr47
-rw-r--r--tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs4
91 files changed, 1645 insertions, 360 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 17b73468a31..dd4321bea1b 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2947,6 +2947,7 @@ pub struct StaticItem {
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct ConstItem {
     pub defaultness: Defaultness,
+    pub generics: Generics,
     pub ty: P<Ty>,
     pub expr: Option<P<Expr>>,
 }
@@ -3058,6 +3059,7 @@ impl ItemKind {
         match self {
             Self::Fn(box Fn { generics, .. })
             | Self::TyAlias(box TyAlias { generics, .. })
+            | Self::Const(box ConstItem { generics, .. })
             | Self::Enum(_, generics)
             | Self::Struct(_, generics)
             | Self::Union(_, generics)
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 53a9c9a046e..84b56efd325 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -1149,10 +1149,11 @@ pub fn noop_flat_map_assoc_item<T: MutVisitor>(
 }
 
 fn visit_const_item<T: MutVisitor>(
-    ConstItem { defaultness, ty, expr }: &mut ConstItem,
+    ConstItem { defaultness, generics, ty, expr }: &mut ConstItem,
     visitor: &mut T,
 ) {
     visit_defaultness(defaultness, visitor);
+    visitor.visit_generics(generics);
     visitor.visit_ty(ty);
     visit_opt(expr, |expr| visitor.visit_expr(expr));
 }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index d9de5b8e197..aed24e11c4e 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -308,8 +308,12 @@ pub fn walk_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a Item) {
     match &item.kind {
         ItemKind::ExternCrate(_) => {}
         ItemKind::Use(use_tree) => visitor.visit_use_tree(use_tree, item.id, false),
-        ItemKind::Static(box StaticItem { ty, mutability: _, expr })
-        | ItemKind::Const(box ConstItem { ty, expr, .. }) => {
+        ItemKind::Static(box StaticItem { ty, mutability: _, expr }) => {
+            visitor.visit_ty(ty);
+            walk_list!(visitor, visit_expr, expr);
+        }
+        ItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => {
+            visitor.visit_generics(generics);
             visitor.visit_ty(ty);
             walk_list!(visitor, visit_expr, expr);
         }
@@ -677,7 +681,8 @@ pub fn walk_assoc_item<'a, V: Visitor<'a>>(visitor: &mut V, item: &'a AssocItem,
     visitor.visit_ident(ident);
     walk_list!(visitor, visit_attribute, attrs);
     match kind {
-        AssocItemKind::Const(box ConstItem { ty, expr, .. }) => {
+        AssocItemKind::Const(box ConstItem { defaultness: _, generics, ty, expr }) => {
+            visitor.visit_generics(generics);
             visitor.visit_ty(ty);
             walk_list!(visitor, visit_expr, expr);
         }
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 2f58f566c81..5a0474dbc01 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -231,9 +231,15 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (ty, body_id) = self.lower_const_item(t, span, e.as_deref());
                 hir::ItemKind::Static(ty, *m, body_id)
             }
-            ItemKind::Const(box ast::ConstItem { ty, expr, .. }) => {
-                let (ty, body_id) = self.lower_const_item(ty, span, expr.as_deref());
-                hir::ItemKind::Const(ty, body_id)
+            ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
+                let (generics, (ty, body_id)) = self.lower_generics(
+                    generics,
+                    Const::No,
+                    id,
+                    &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+                    |this| this.lower_const_item(ty, span, expr.as_deref()),
+                );
+                hir::ItemKind::Const(ty, generics, body_id)
             }
             ItemKind::Fn(box Fn {
                 sig: FnSig { decl, header, span: fn_sig_span },
@@ -715,11 +721,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let trait_item_def_id = hir_id.expect_owner();
 
         let (generics, kind, has_default) = match &i.kind {
-            AssocItemKind::Const(box ConstItem { ty, expr, .. }) => {
-                let ty =
-                    self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
-                let body = expr.as_ref().map(|x| self.lower_const_body(i.span, Some(x)));
-                (hir::Generics::empty(), hir::TraitItemKind::Const(ty, body), body.is_some())
+            AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => {
+                let (generics, kind) = self.lower_generics(
+                    &generics,
+                    Const::No,
+                    i.id,
+                    &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+                    |this| {
+                        let ty = this.lower_ty(
+                            ty,
+                            &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy),
+                        );
+                        let body = expr.as_ref().map(|x| this.lower_const_body(i.span, Some(x)));
+
+                        hir::TraitItemKind::Const(ty, body)
+                    },
+                );
+                (generics, kind, expr.is_some())
             }
             AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
                 let asyncness = sig.header.asyncness;
@@ -817,14 +835,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
         self.lower_attrs(hir_id, &i.attrs);
 
         let (generics, kind) = match &i.kind {
-            AssocItemKind::Const(box ConstItem { ty, expr, .. }) => {
-                let ty =
-                    self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
-                (
-                    hir::Generics::empty(),
-                    hir::ImplItemKind::Const(ty, self.lower_const_body(i.span, expr.as_deref())),
-                )
-            }
+            AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
+                &generics,
+                Const::No,
+                i.id,
+                &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
+                |this| {
+                    let ty = this
+                        .lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
+                    let body = this.lower_const_body(i.span, expr.as_deref());
+
+                    hir::ImplItemKind::Const(ty, body)
+                },
+            ),
             AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
                 self.current_item = Some(i.span);
                 let asyncness = sig.header.asyncness;
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index c0d2e76f310..c4efad7caf2 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -569,6 +569,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
     gate_all!(const_closures, "const closures are experimental");
     gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
     gate_all!(explicit_tail_calls, "`become` expression is experimental");
+    gate_all!(generic_const_items, "generic const items are experimental");
 
     if !visitor.features.negative_bounds {
         for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index 5c01b7ea70a..d27a44f1206 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -30,10 +30,15 @@ impl<'a> State<'a> {
             ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
                 self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
             }
-            ast::ForeignItemKind::Static(ty, mutbl, body) => {
-                let def = ast::Defaultness::Final;
-                self.print_item_const(ident, Some(*mutbl), ty, body.as_deref(), vis, def);
-            }
+            ast::ForeignItemKind::Static(ty, mutbl, body) => self.print_item_const(
+                ident,
+                Some(*mutbl),
+                &ast::Generics::default(),
+                ty,
+                body.as_deref(),
+                vis,
+                ast::Defaultness::Final,
+            ),
             ast::ForeignItemKind::TyAlias(box ast::TyAlias {
                 defaultness,
                 generics,
@@ -67,6 +72,7 @@ impl<'a> State<'a> {
         &mut self,
         ident: Ident,
         mutbl: Option<ast::Mutability>,
+        generics: &ast::Generics,
         ty: &ast::Ty,
         body: Option<&ast::Expr>,
         vis: &ast::Visibility,
@@ -82,6 +88,7 @@ impl<'a> State<'a> {
         };
         self.word_space(leading);
         self.print_ident(ident);
+        self.print_generic_params(&generics.params);
         self.word_space(":");
         self.print_type(ty);
         if body.is_some() {
@@ -92,6 +99,7 @@ impl<'a> State<'a> {
             self.word_space("=");
             self.print_expr(body);
         }
+        self.print_where_clause(&generics.where_clause);
         self.word(";");
         self.end(); // end the outer cbox
     }
@@ -158,20 +166,21 @@ impl<'a> State<'a> {
                 self.word(";");
             }
             ast::ItemKind::Static(box StaticItem { ty, mutability: mutbl, expr: body }) => {
-                let def = ast::Defaultness::Final;
                 self.print_item_const(
                     item.ident,
                     Some(*mutbl),
+                    &ast::Generics::default(),
                     ty,
                     body.as_deref(),
                     &item.vis,
-                    def,
+                    ast::Defaultness::Final,
                 );
             }
-            ast::ItemKind::Const(box ast::ConstItem { defaultness, ty, expr }) => {
+            ast::ItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => {
                 self.print_item_const(
                     item.ident,
                     None,
+                    generics,
                     ty,
                     expr.as_deref(),
                     &item.vis,
@@ -515,8 +524,16 @@ impl<'a> State<'a> {
             ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => {
                 self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs);
             }
-            ast::AssocItemKind::Const(box ast::ConstItem { defaultness, ty, expr }) => {
-                self.print_item_const(ident, None, ty, expr.as_deref(), vis, *defaultness);
+            ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => {
+                self.print_item_const(
+                    ident,
+                    None,
+                    generics,
+                    ty,
+                    expr.as_deref(),
+                    vis,
+                    *defaultness,
+                );
             }
             ast::AssocItemKind::Type(box ast::TyAlias {
                 defaultness,
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index 6bc4f6fc1fc..1580a6f6dd3 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -255,6 +255,7 @@ pub fn expand_test_or_bench(
             ast::ItemKind::Const(
                 ast::ConstItem {
                     defaultness: ast::Defaultness::Final,
+                    generics: ast::Generics::default(),
                     ty: cx.ty(sp, ast::TyKind::Path(None, test_path("TestDescAndFn"))),
                     // test::TestDescAndFn {
                     expr: Some(
diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs
index 264f30fb10a..7de46994434 100644
--- a/compiler/rustc_expand/src/build.rs
+++ b/compiler/rustc_expand/src/build.rs
@@ -643,7 +643,16 @@ impl<'a> ExtCtxt<'a> {
             span,
             name,
             AttrVec::new(),
-            ast::ItemKind::Const(ast::ConstItem { defaultness, ty, expr: Some(expr) }.into()),
+            ast::ItemKind::Const(
+                ast::ConstItem {
+                    defaultness,
+                    // FIXME(generic_const_items): Pass the generics as a parameter.
+                    generics: ast::Generics::default(),
+                    ty,
+                    expr: Some(expr),
+                }
+                .into(),
+            ),
         )
     }
 
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index c03789f500a..22380a52104 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -424,6 +424,8 @@ declare_features! (
     (incomplete, generic_associated_types_extended, "1.61.0", Some(95451), None),
     /// Allows non-trivial generic constants which have to have wfness manually propagated to callers
     (incomplete, generic_const_exprs, "1.56.0", Some(76560), None),
+    /// Allows generic parameters and where-clauses on free & associated const items.
+    (incomplete, generic_const_items, "CURRENT_RUSTC_VERSION", Some(113521), None),
     /// Allows using `..=X` as a patterns in slices.
     (active, half_open_range_patterns_in_slices, "1.66.0", Some(67264), None),
     /// Allows `if let` guard in match arms.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 3663c450ba6..6b76e16825f 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -3130,9 +3130,9 @@ impl<'hir> Item<'hir> {
     }
     /// Expect an [`ItemKind::Const`] or panic.
     #[track_caller]
-    pub fn expect_const(&self) -> (&'hir Ty<'hir>, BodyId) {
-        let ItemKind::Const(ty, body) = self.kind else { self.expect_failed("a constant") };
-        (ty, body)
+    pub fn expect_const(&self) -> (&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId) {
+        let ItemKind::Const(ty, gen, body) = self.kind else { self.expect_failed("a constant") };
+        (ty, gen, body)
     }
     /// Expect an [`ItemKind::Fn`] or panic.
     #[track_caller]
@@ -3319,7 +3319,7 @@ pub enum ItemKind<'hir> {
     /// A `static` item.
     Static(&'hir Ty<'hir>, Mutability, BodyId),
     /// A `const` item.
-    Const(&'hir Ty<'hir>, BodyId),
+    Const(&'hir Ty<'hir>, &'hir Generics<'hir>, BodyId),
     /// A function declaration.
     Fn(FnSig<'hir>, &'hir Generics<'hir>, BodyId),
     /// A MBE macro definition (`macro_rules!` or `macro`).
@@ -3372,6 +3372,7 @@ impl ItemKind<'_> {
         Some(match *self {
             ItemKind::Fn(_, ref generics, _)
             | ItemKind::TyAlias(_, ref generics)
+            | ItemKind::Const(_, ref generics, _)
             | ItemKind::OpaqueTy(OpaqueTy { ref generics, .. })
             | ItemKind::Enum(_, ref generics)
             | ItemKind::Struct(_, ref generics)
@@ -3567,7 +3568,9 @@ impl<'hir> OwnerNode<'hir> {
         match self {
             OwnerNode::Item(Item {
                 kind:
-                    ItemKind::Static(_, _, body) | ItemKind::Const(_, body) | ItemKind::Fn(_, _, body),
+                    ItemKind::Static(_, _, body)
+                    | ItemKind::Const(_, _, body)
+                    | ItemKind::Fn(_, _, body),
                 ..
             })
             | OwnerNode::TraitItem(TraitItem {
@@ -3770,9 +3773,9 @@ impl<'hir> Node<'hir> {
     pub fn ty(self) -> Option<&'hir Ty<'hir>> {
         match self {
             Node::Item(it) => match it.kind {
-                ItemKind::TyAlias(ty, _) | ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => {
-                    Some(ty)
-                }
+                ItemKind::TyAlias(ty, _)
+                | ItemKind::Static(ty, _, _)
+                | ItemKind::Const(ty, _, _) => Some(ty),
                 _ => None,
             },
             Node::TraitItem(it) => match it.kind {
@@ -3800,7 +3803,9 @@ impl<'hir> Node<'hir> {
         match self {
             Node::Item(Item {
                 kind:
-                    ItemKind::Static(_, _, body) | ItemKind::Const(_, body) | ItemKind::Fn(_, _, body),
+                    ItemKind::Static(_, _, body)
+                    | ItemKind::Const(_, _, body)
+                    | ItemKind::Fn(_, _, body),
                 ..
             })
             | Node::TraitItem(TraitItem {
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 347c1f4637f..a8a94e6a476 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -467,11 +467,17 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
         ItemKind::Use(ref path, _) => {
             visitor.visit_use(path, item.hir_id());
         }
-        ItemKind::Static(ref typ, _, body) | ItemKind::Const(ref typ, body) => {
+        ItemKind::Static(ref typ, _, body) => {
             visitor.visit_id(item.hir_id());
             visitor.visit_ty(typ);
             visitor.visit_nested_body(body);
         }
+        ItemKind::Const(ref typ, ref generics, body) => {
+            visitor.visit_id(item.hir_id());
+            visitor.visit_ty(typ);
+            visitor.visit_generics(generics);
+            visitor.visit_nested_body(body);
+        }
         ItemKind::Fn(ref sig, ref generics, body_id) => {
             visitor.visit_id(item.hir_id());
             visitor.visit_fn(
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 919092ecb16..75c45868b90 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -76,7 +76,7 @@ fn check_method_is_structurally_compatible<'tcx>(
     Ok(())
 }
 
-/// This function is best explained by example. Consider a trait with it's implementation:
+/// This function is best explained by example. Consider a trait with its implementation:
 ///
 /// ```rust
 /// trait Trait<'t, T> {
@@ -120,7 +120,7 @@ fn check_method_is_structurally_compatible<'tcx>(
 /// types:
 ///
 /// ```rust,ignore (pseudo-Rust)
-/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
+/// <'b> fn(t: &'i0 U0, m: &'b N0) -> Foo
 /// ```
 ///
 /// We now want to extract and substitute the type of the *trait*
@@ -137,7 +137,7 @@ fn check_method_is_structurally_compatible<'tcx>(
 /// Applying this to the trait method type yields:
 ///
 /// ```rust,ignore (pseudo-Rust)
-/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
+/// <'a> fn(t: &'i0 U0, m: &'a N0) -> Foo
 /// ```
 ///
 /// This type is also the same but the name of the bound region (`'a`
@@ -258,8 +258,6 @@ fn compare_method_predicate_entailment<'tcx>(
     // type.
 
     // Compute placeholder form of impl and trait method tys.
-    let tcx = infcx.tcx;
-
     let mut wf_tys = FxIndexSet::default();
 
     let unnormalized_impl_sig = infcx.instantiate_binder_with_fresh_vars(
@@ -1668,19 +1666,19 @@ fn compare_synthetic_generics<'tcx>(
 /// ```rust,ignore (pseudo-Rust)
 /// trait Foo {
 ///     fn foo<const N: u8>();
-///     type bar<const N: u8>;
+///     type Bar<const N: u8>;
 ///     fn baz<const N: u32>();
-///     type blah<T>;
+///     type Blah<T>;
 /// }
 ///
 /// impl Foo for () {
 ///     fn foo<const N: u64>() {}
 ///     //~^ error
-///     type bar<const N: u64> {}
+///     type Bar<const N: u64> = ();
 ///     //~^ error
 ///     fn baz<T>() {}
 ///     //~^ error
-///     type blah<const N: i64> = u32;
+///     type Blah<const N: i64> = u32;
 ///     //~^ error
 /// }
 /// ```
@@ -1769,36 +1767,82 @@ pub(super) fn compare_impl_const_raw(
     let trait_const_item = tcx.associated_item(trait_const_item_def);
     let impl_trait_ref =
         tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap().instantiate_identity();
-    debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
 
-    let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id());
+    debug!("compare_impl_const(impl_trait_ref={:?})", impl_trait_ref);
 
-    let infcx = tcx.infer_ctxt().build();
-    let param_env = tcx.param_env(impl_const_item_def.to_def_id());
-    let ocx = ObligationCtxt::new(&infcx);
+    compare_number_of_generics(tcx, impl_const_item, trait_const_item, false)?;
+    compare_generic_param_kinds(tcx, impl_const_item, trait_const_item, false)?;
+    compare_const_predicate_entailment(tcx, impl_const_item, trait_const_item, impl_trait_ref)
+}
+
+/// The equivalent of [compare_method_predicate_entailment], but for associated constants
+/// instead of associated functions.
+// FIXME(generic_const_items): If possible extract the common parts of `compare_{type,const}_predicate_entailment`.
+fn compare_const_predicate_entailment<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    impl_ct: ty::AssocItem,
+    trait_ct: ty::AssocItem,
+    impl_trait_ref: ty::TraitRef<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+    let impl_ct_def_id = impl_ct.def_id.expect_local();
+    let impl_ct_span = tcx.def_span(impl_ct_def_id);
 
     // The below is for the most part highly similar to the procedure
     // for methods above. It is simpler in many respects, especially
     // because we shouldn't really have to deal with lifetimes or
     // predicates. In fact some of this should probably be put into
     // shared functions because of DRY violations...
-    let trait_to_impl_args = impl_trait_ref.args;
+    let impl_args = GenericArgs::identity_for_item(tcx, impl_ct.def_id);
+    let trait_to_impl_args =
+        impl_args.rebase_onto(tcx, impl_ct.container_id(tcx), impl_trait_ref.args);
 
     // Create a parameter environment that represents the implementation's
     // method.
     // Compute placeholder form of impl and trait const tys.
-    let impl_ty = tcx.type_of(impl_const_item_def.to_def_id()).instantiate_identity();
-    let trait_ty = tcx.type_of(trait_const_item_def).instantiate(tcx, trait_to_impl_args);
-    let mut cause = ObligationCause::new(
-        impl_c_span,
-        impl_const_item_def,
-        ObligationCauseCode::CompareImplItemObligation {
-            impl_item_def_id: impl_const_item_def,
-            trait_item_def_id: trait_const_item_def,
-            kind: impl_const_item.kind,
-        },
+    let impl_ty = tcx.type_of(impl_ct_def_id).instantiate_identity();
+
+    let trait_ty = tcx.type_of(trait_ct.def_id).instantiate(tcx, trait_to_impl_args);
+    let code = ObligationCauseCode::CompareImplItemObligation {
+        impl_item_def_id: impl_ct_def_id,
+        trait_item_def_id: trait_ct.def_id,
+        kind: impl_ct.kind,
+    };
+    let mut cause = ObligationCause::new(impl_ct_span, impl_ct_def_id, code.clone());
+
+    let impl_ct_predicates = tcx.predicates_of(impl_ct.def_id);
+    let trait_ct_predicates = tcx.predicates_of(trait_ct.def_id);
+
+    check_region_bounds_on_impl_item(tcx, impl_ct, trait_ct, false)?;
+
+    // The predicates declared by the impl definition, the trait and the
+    // associated const in the trait are assumed.
+    let impl_predicates = tcx.predicates_of(impl_ct_predicates.parent.unwrap());
+    let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
+    hybrid_preds.predicates.extend(
+        trait_ct_predicates
+            .instantiate_own(tcx, trait_to_impl_args)
+            .map(|(predicate, _)| predicate),
+    );
+
+    let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
+    let param_env = traits::normalize_param_env_or_error(
+        tcx,
+        param_env,
+        ObligationCause::misc(impl_ct_span, impl_ct_def_id),
     );
 
+    let infcx = tcx.infer_ctxt().build();
+    let ocx = ObligationCtxt::new(&infcx);
+
+    let impl_ct_own_bounds = impl_ct_predicates.instantiate_own(tcx, impl_args);
+    for (predicate, span) in impl_ct_own_bounds {
+        let cause = ObligationCause::misc(span, impl_ct_def_id);
+        let predicate = ocx.normalize(&cause, param_env, predicate);
+
+        let cause = ObligationCause::new(span, impl_ct_def_id, code.clone());
+        ocx.register_obligation(traits::Obligation::new(tcx, cause, param_env, predicate));
+    }
+
     // There is no "body" here, so just pass dummy id.
     let impl_ty = ocx.normalize(&cause, param_env, impl_ty);
 
@@ -1817,7 +1861,7 @@ pub(super) fn compare_impl_const_raw(
         );
 
         // Locate the Span containing just the type of the offending impl
-        let (ty, _) = tcx.hir().expect_impl_item(impl_const_item_def).expect_const();
+        let (ty, _) = tcx.hir().expect_impl_item(impl_ct_def_id).expect_const();
         cause.span = ty.span;
 
         let mut diag = struct_span_err!(
@@ -1825,12 +1869,12 @@ pub(super) fn compare_impl_const_raw(
             cause.span,
             E0326,
             "implemented const `{}` has an incompatible type for trait",
-            trait_const_item.name
+            trait_ct.name
         );
 
-        let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| {
+        let trait_c_span = trait_ct.def_id.as_local().map(|trait_ct_def_id| {
             // Add a label to the Span containing just the type of the const
-            let (ty, _) = tcx.hir().expect_trait_item(trait_c_def_id).expect_const();
+            let (ty, _) = tcx.hir().expect_trait_item(trait_ct_def_id).expect_const();
             ty.span
         });
 
@@ -1857,7 +1901,7 @@ pub(super) fn compare_impl_const_raw(
     }
 
     let outlives_env = OutlivesEnvironment::new(param_env);
-    ocx.resolve_regions_and_report_errors(impl_const_item_def, &outlives_env)
+    ocx.resolve_regions_and_report_errors(impl_ct_def_id, &outlives_env)
 }
 
 pub(super) fn compare_impl_ty<'tcx>(
@@ -1899,7 +1943,7 @@ fn compare_type_predicate_entailment<'tcx>(
         return Ok(());
     }
 
-    // This `HirId` should be used for the `body_id` field on each
+    // This `DefId` should be used for the `body_id` field on each
     // `ObligationCause` (and the `FnCtxt`). This is what
     // `regionck_item` expects.
     let impl_ty_def_id = impl_ty.def_id.expect_local();
@@ -1918,7 +1962,7 @@ fn compare_type_predicate_entailment<'tcx>(
     debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
 
     let impl_ty_span = tcx.def_span(impl_ty_def_id);
-    let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_def_id);
+    let normalize_cause = ObligationCause::misc(impl_ty_span, impl_ty_def_id);
     let param_env = ty::ParamEnv::new(tcx.mk_clauses(&hybrid_preds.predicates), Reveal::UserFacing);
     let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
     let infcx = tcx.infer_ctxt().build();
@@ -1963,7 +2007,7 @@ fn compare_type_predicate_entailment<'tcx>(
 ///
 /// trait X { type Y: Copy } impl X for T { type Y = S; }
 ///
-/// We are able to normalize `<T as X>::U` to `S`, and so when we check the
+/// We are able to normalize `<T as X>::Y` to `S`, and so when we check the
 /// impl is well-formed we have to prove `S: Copy`.
 ///
 /// For default associated types the normalization is not possible (the value
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index edcb9527fe2..6e1762c54f2 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -209,6 +209,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 | ItemKind::Struct(..)
                 | ItemKind::OpaqueTy(..)
                 | ItemKind::Union(..) => (None, Defaults::Allowed),
+                ItemKind::Const(..) => (None, Defaults::Deny),
                 _ => (None, Defaults::FutureCompatDisallowed),
             }
         }
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 979b101e7fe..ab3b2dde078 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -156,6 +156,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             }
             ItemKind::Fn(.., generics, _)
             | ItemKind::TyAlias(_, generics)
+            | ItemKind::Const(_, generics, _)
             | ItemKind::Enum(_, generics)
             | ItemKind::Struct(_, generics)
             | ItemKind::Union(_, generics) => generics,
@@ -762,6 +763,7 @@ pub(super) fn type_param_predicates(
                 ItemKind::Fn(.., generics, _)
                 | ItemKind::Impl(&hir::Impl { generics, .. })
                 | ItemKind::TyAlias(_, generics)
+                | ItemKind::Const(_, generics, _)
                 | ItemKind::OpaqueTy(&OpaqueTy {
                     generics,
                     origin: hir::OpaqueTyOrigin::TyAlias { .. },
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index eb93817e823..3cc6f574aec 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -518,7 +518,6 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
             | hir::ItemKind::Mod(..)
             | hir::ItemKind::ForeignMod { .. }
             | hir::ItemKind::Static(..)
-            | hir::ItemKind::Const(..)
             | hir::ItemKind::GlobalAsm(..) => {
                 // These sorts of items have no lifetime parameters at all.
                 intravisit::walk_item(self, item);
@@ -583,6 +582,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                 })
             }
             hir::ItemKind::TyAlias(_, generics)
+            | hir::ItemKind::Const(_, generics, _)
             | hir::ItemKind::Enum(_, generics)
             | hir::ItemKind::Struct(_, generics)
             | hir::ItemKind::Union(_, generics)
@@ -590,21 +590,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
             | hir::ItemKind::TraitAlias(generics, ..)
             | hir::ItemKind::Impl(&hir::Impl { generics, .. }) => {
                 // These kinds of items have only early-bound lifetime parameters.
-                let bound_vars = generics.params.iter().map(ResolvedArg::early).collect();
-                self.record_late_bound_vars(item.hir_id(), vec![]);
-                let scope = Scope::Binder {
-                    hir_id: item.hir_id(),
-                    bound_vars,
-                    scope_type: BinderScopeType::Normal,
-                    s: self.scope,
-                    where_bound_origin: None,
-                };
-                self.with(scope, |this| {
-                    let scope = Scope::TraitRefBoundary { s: this.scope };
-                    this.with(scope, |this| {
-                        intravisit::walk_item(this, item);
-                    });
-                });
+                self.visit_early(item.hir_id(), generics, |this| intravisit::walk_item(this, item));
             }
         }
     }
@@ -777,39 +763,24 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
         use self::hir::TraitItemKind::*;
         match trait_item.kind {
             Fn(_, _) => {
-                self.visit_early_late(trait_item.hir_id(), &trait_item.generics, |this| {
+                self.visit_early_late(trait_item.hir_id(), trait_item.generics, |this| {
                     intravisit::walk_trait_item(this, trait_item)
                 });
             }
             Type(bounds, ty) => {
-                let generics = &trait_item.generics;
-                let bound_vars = generics.params.iter().map(ResolvedArg::early).collect();
-                self.record_late_bound_vars(trait_item.hir_id(), vec![]);
-                let scope = Scope::Binder {
-                    hir_id: trait_item.hir_id(),
-                    bound_vars,
-                    s: self.scope,
-                    scope_type: BinderScopeType::Normal,
-                    where_bound_origin: None,
-                };
-                self.with(scope, |this| {
-                    let scope = Scope::TraitRefBoundary { s: this.scope };
-                    this.with(scope, |this| {
-                        this.visit_generics(generics);
-                        for bound in bounds {
-                            this.visit_param_bound(bound);
-                        }
-                        if let Some(ty) = ty {
-                            this.visit_ty(ty);
-                        }
-                    })
-                });
-            }
-            Const(_, _) => {
-                // Only methods and types support generics.
-                assert!(trait_item.generics.params.is_empty());
-                intravisit::walk_trait_item(self, trait_item);
+                self.visit_early(trait_item.hir_id(), trait_item.generics, |this| {
+                    this.visit_generics(&trait_item.generics);
+                    for bound in bounds {
+                        this.visit_param_bound(bound);
+                    }
+                    if let Some(ty) = ty {
+                        this.visit_ty(ty);
+                    }
+                })
             }
+            Const(_, _) => self.visit_early(trait_item.hir_id(), trait_item.generics, |this| {
+                intravisit::walk_trait_item(this, trait_item)
+            }),
         }
     }
 
@@ -817,34 +788,16 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
     fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
         use self::hir::ImplItemKind::*;
         match impl_item.kind {
-            Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| {
+            Fn(..) => self.visit_early_late(impl_item.hir_id(), impl_item.generics, |this| {
+                intravisit::walk_impl_item(this, impl_item)
+            }),
+            Type(ty) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| {
+                this.visit_generics(impl_item.generics);
+                this.visit_ty(ty);
+            }),
+            Const(_, _) => self.visit_early(impl_item.hir_id(), impl_item.generics, |this| {
                 intravisit::walk_impl_item(this, impl_item)
             }),
-            Type(ty) => {
-                let generics = &impl_item.generics;
-                let bound_vars: FxIndexMap<LocalDefId, ResolvedArg> =
-                    generics.params.iter().map(ResolvedArg::early).collect();
-                self.record_late_bound_vars(impl_item.hir_id(), vec![]);
-                let scope = Scope::Binder {
-                    hir_id: impl_item.hir_id(),
-                    bound_vars,
-                    s: self.scope,
-                    scope_type: BinderScopeType::Normal,
-                    where_bound_origin: None,
-                };
-                self.with(scope, |this| {
-                    let scope = Scope::TraitRefBoundary { s: this.scope };
-                    this.with(scope, |this| {
-                        this.visit_generics(generics);
-                        this.visit_ty(ty);
-                    })
-                });
-            }
-            Const(_, _) => {
-                // Only methods and types support generics.
-                assert!(impl_item.generics.params.is_empty());
-                intravisit::walk_impl_item(self, impl_item);
-            }
         }
     }
 
@@ -1180,6 +1133,25 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         self.with(scope, walk);
     }
 
+    fn visit_early<F>(&mut self, hir_id: hir::HirId, generics: &'tcx hir::Generics<'tcx>, walk: F)
+    where
+        F: for<'b, 'c> FnOnce(&'b mut BoundVarContext<'c, 'tcx>),
+    {
+        let bound_vars = generics.params.iter().map(ResolvedArg::early).collect();
+        self.record_late_bound_vars(hir_id, vec![]);
+        let scope = Scope::Binder {
+            hir_id,
+            bound_vars,
+            s: self.scope,
+            scope_type: BinderScopeType::Normal,
+            where_bound_origin: None,
+        };
+        self.with(scope, |this| {
+            let scope = Scope::TraitRefBoundary { s: this.scope };
+            this.with(scope, walk)
+        });
+    }
+
     #[instrument(level = "debug", skip(self))]
     fn resolve_lifetime_ref(
         &mut self,
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index a9ef791077e..2bbdbe3a1f6 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -404,7 +404,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
                         icx.to_ty(ty)
                     }
                 }
-                ItemKind::Const(ty, body_id) => {
+                ItemKind::Const(ty, _, body_id) => {
                     if is_suggestable_infer_ty(ty) {
                         infer_placeholder_type(
                             tcx, def_id, body_id, ty.span, item.ident, "constant",
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index f1765174d79..ca7679cfba0 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -130,7 +130,7 @@ fn diagnostic_hir_wf_check<'tcx>(
             hir::Node::Item(item) => match item.kind {
                 hir::ItemKind::TyAlias(ty, _)
                 | hir::ItemKind::Static(ty, _, _)
-                | hir::ItemKind::Const(ty, _) => vec![ty],
+                | hir::ItemKind::Const(ty, _, _) => vec![ty],
                 hir::ItemKind::Impl(impl_) => match &impl_.of_trait {
                     Some(t) => t
                         .path
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index a699cd6c942..2d8b956771b 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -420,12 +420,13 @@ impl<'a> State<'a> {
     fn print_associated_const(
         &mut self,
         ident: Ident,
+        generics: &hir::Generics<'_>,
         ty: &hir::Ty<'_>,
         default: Option<hir::BodyId>,
     ) {
-        self.head("");
         self.word_space("const");
         self.print_ident(ident);
+        self.print_generic_params(generics.params);
         self.word_space(":");
         self.print_type(ty);
         if let Some(expr) = default {
@@ -433,6 +434,7 @@ impl<'a> State<'a> {
             self.word_space("=");
             self.ann.nested(self, Nested::Body(expr));
         }
+        self.print_where_clause(generics);
         self.word(";")
     }
 
@@ -532,9 +534,10 @@ impl<'a> State<'a> {
                 self.word(";");
                 self.end(); // end the outer cbox
             }
-            hir::ItemKind::Const(ty, expr) => {
+            hir::ItemKind::Const(ty, generics, expr) => {
                 self.head("const");
                 self.print_ident(item.ident);
+                self.print_generic_params(generics.params);
                 self.word_space(":");
                 self.print_type(ty);
                 self.space();
@@ -542,6 +545,7 @@ impl<'a> State<'a> {
 
                 self.word_space("=");
                 self.ann.nested(self, Nested::Body(expr));
+                self.print_where_clause(generics);
                 self.word(";");
                 self.end(); // end the outer cbox
             }
@@ -836,7 +840,7 @@ impl<'a> State<'a> {
         self.print_outer_attributes(self.attrs(ti.hir_id()));
         match ti.kind {
             hir::TraitItemKind::Const(ty, default) => {
-                self.print_associated_const(ti.ident, ty, default);
+                self.print_associated_const(ti.ident, ti.generics, ty, default);
             }
             hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(arg_names)) => {
                 self.print_method_sig(ti.ident, sig, ti.generics, arg_names, None);
@@ -865,7 +869,7 @@ impl<'a> State<'a> {
 
         match ii.kind {
             hir::ImplItemKind::Const(ty, expr) => {
-                self.print_associated_const(ii.ident, ty, Some(expr));
+                self.print_associated_const(ii.ident, ii.generics, ty, Some(expr));
             }
             hir::ImplItemKind::Fn(ref sig, body) => {
                 self.head("");
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 3f1f10f2f00..8f5737dd4ad 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -1394,7 +1394,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let Some((
             _,
             hir::Node::Local(hir::Local { ty: Some(ty), .. })
-            | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. }),
+            | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. }),
         )) = parent_node
         else {
             return;
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 864308267bb..c4d3cbc9faa 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -101,7 +101,7 @@ fn primary_body_of(
 ) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> {
     match node {
         Node::Item(item) => match item.kind {
-            hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => {
+            hir::ItemKind::Const(ty, _, body) | hir::ItemKind::Static(ty, _, body) => {
                 Some((body, Some(ty), None))
             }
             hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))),
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 6ffa3b0a0b3..4d236a86dda 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -924,7 +924,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         match opt_def_id {
             Some(def_id) => match self.tcx.hir().get_if_local(def_id) {
                 Some(hir::Node::Item(hir::Item {
-                    kind: hir::ItemKind::Const(_, body_id), ..
+                    kind: hir::ItemKind::Const(_, _, body_id),
+                    ..
                 })) => match self.tcx.hir().get(body_id.hir_id) {
                     hir::Node::Expr(expr) => {
                         if hir::is_range_literal(expr) {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index d3978e242a8..c000f988aa2 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -2068,7 +2068,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 visitor.visit_body(body);
                 visitor.result.map(|r| &r.peel_refs().kind)
             }
-            Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. })) => {
+            Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _, _), .. })) => {
                 Some(&ty.peel_refs().kind)
             }
             _ => None,
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index cf6e7f16f07..ba05622bf37 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -1529,9 +1529,10 @@ declare_lint_pass!(
 impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst {
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
         match it.kind {
-            hir::ItemKind::Const(_, body_id) => {
+            hir::ItemKind::Const(_, _, body_id) => {
                 let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
                 // trigger the query once for all constants since that will already report the errors
+                // FIXME(generic_const_items): Does this work properly with generic const items?
                 cx.tcx.ensure().const_eval_poly(def_id);
             }
             hir::ItemKind::Static(_, _, body_id) => {
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 1fd68dc5cb2..b167364f680 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -24,7 +24,7 @@ pub fn associated_body(node: Node<'_>) -> Option<(LocalDefId, BodyId)> {
     match node {
         Node::Item(Item {
             owner_id,
-            kind: ItemKind::Const(_, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body),
+            kind: ItemKind::Const(_, _, body) | ItemKind::Static(.., body) | ItemKind::Fn(.., body),
             ..
         })
         | Node::TraitItem(TraitItem {
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 1ae8031ef3c..c66eba5520e 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -570,7 +570,7 @@ fn construct_const<'a, 'tcx>(
     // Figure out what primary body this item has.
     let (span, const_ty_span) = match tcx.hir().get(hir_id) {
         Node::Item(hir::Item {
-            kind: hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _),
+            kind: hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _, _),
             span,
             ..
         })
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 9787d98c1a4..4f1d282fe7d 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -690,6 +690,8 @@ parse_single_colon_import_path = expected `::`, found `:`
 parse_single_colon_struct_type = found single colon in a struct field type path
     .suggestion = write a path separator here
 
+parse_static_with_generics = static items may not have generic parameters
+
 parse_struct_literal_body_without_path =
     struct literal body without path
     .suggestion = you might have forgotten to add the struct literal inside the block
@@ -847,6 +849,12 @@ parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by a
     .label = the visibility
     .help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}`
 
+parse_where_clause_before_const_body = where clauses are not allowed before const item bodies
+    .label = unexpected where clause
+    .name_label = while parsing this const item
+    .body_label = the item body
+    .suggestion = move the body before the where clause
+
 parse_where_clause_before_tuple_struct_body = where clauses are not allowed before tuple struct bodies
     .label = unexpected where clause
     .name_label = while parsing this tuple struct
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 96e1c0e3c6d..5456a708898 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2692,3 +2692,34 @@ pub(crate) struct ExpectedBuiltinIdent {
     #[primary_span]
     pub span: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(parse_static_with_generics)]
+pub(crate) struct StaticWithGenerics {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(parse_where_clause_before_const_body)]
+pub(crate) struct WhereClauseBeforeConstBody {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(parse_name_label)]
+    pub name: Span,
+    #[label(parse_body_label)]
+    pub body: Span,
+    #[subdiagnostic]
+    pub sugg: Option<WhereClauseBeforeConstBodySugg>,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
+pub(crate) struct WhereClauseBeforeConstBodySugg {
+    #[suggestion_part(code = "= {snippet} ")]
+    pub left: Span,
+    pub snippet: String,
+    #[suggestion_part(code = "")]
+    pub right: Span,
+}
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 1470180dea7..1301ed3e388 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -226,9 +226,9 @@ impl<'a> Parser<'a> {
         } else if self.is_static_global() {
             // STATIC ITEM
             self.bump(); // `static`
-            let m = self.parse_mutability();
-            let (ident, ty, expr) = self.parse_item_global(Some(m))?;
-            (ident, ItemKind::Static(Box::new(StaticItem { ty, mutability: m, expr })))
+            let mutability = self.parse_mutability();
+            let (ident, item) = self.parse_static_item(mutability)?;
+            (ident, ItemKind::Static(Box::new(item)))
         } else if let Const::Yes(const_span) = self.parse_constness(Case::Sensitive) {
             // CONST ITEM
             if self.token.is_keyword(kw::Impl) {
@@ -236,8 +236,16 @@ impl<'a> Parser<'a> {
                 self.recover_const_impl(const_span, attrs, def_())?
             } else {
                 self.recover_const_mut(const_span);
-                let (ident, ty, expr) = self.parse_item_global(None)?;
-                (ident, ItemKind::Const(Box::new(ConstItem { defaultness: def_(), ty, expr })))
+                let (ident, generics, ty, expr) = self.parse_const_item()?;
+                (
+                    ident,
+                    ItemKind::Const(Box::new(ConstItem {
+                        defaultness: def_(),
+                        generics,
+                        ty,
+                        expr,
+                    })),
+                )
             }
         } else if self.check_keyword(kw::Trait) || self.check_auto_or_unsafe_trait_item() {
             // TRAIT ITEM
@@ -878,6 +886,7 @@ impl<'a> Parser<'a> {
                             self.sess.emit_err(errors::AssociatedStaticItemNotAllowed { span });
                             AssocItemKind::Const(Box::new(ConstItem {
                                 defaultness: Defaultness::Final,
+                                generics: Generics::default(),
                                 ty,
                                 expr,
                             }))
@@ -892,7 +901,7 @@ impl<'a> Parser<'a> {
 
     /// Parses a `type` alias with the following grammar:
     /// ```ebnf
-    /// TypeAlias = "type" Ident Generics {":" GenericBounds}? {"=" Ty}? ";" ;
+    /// TypeAlias = "type" Ident Generics (":" GenericBounds)? WhereClause ("=" Ty)? WhereClause ";" ;
     /// ```
     /// The `"type"` has already been eaten.
     fn parse_type_alias(&mut self, defaultness: Defaultness) -> PResult<'a, ItemInfo> {
@@ -1220,33 +1229,132 @@ impl<'a> Parser<'a> {
         Ok(impl_info)
     }
 
-    /// Parse `["const" | ("static" "mut"?)] $ident ":" $ty (= $expr)?` with
-    /// `["const" | ("static" "mut"?)]` already parsed and stored in `m`.
+    /// Parse a static item with the prefix `"static" "mut"?` already parsed and stored in `mutability`.
     ///
-    /// When `m` is `"const"`, `$ident` may also be `"_"`.
-    fn parse_item_global(
-        &mut self,
-        m: Option<Mutability>,
-    ) -> PResult<'a, (Ident, P<Ty>, Option<P<ast::Expr>>)> {
-        let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?;
+    /// ```ebnf
+    /// Static = "static" "mut"? $ident ":" $ty (= $expr)? ";" ;
+    /// ```
+    fn parse_static_item(&mut self, mutability: Mutability) -> PResult<'a, (Ident, StaticItem)> {
+        let ident = self.parse_ident()?;
+
+        if self.token.kind == TokenKind::Lt && self.may_recover() {
+            let generics = self.parse_generics()?;
+            self.sess.emit_err(errors::StaticWithGenerics { span: generics.span });
+        }
 
-        // Parse the type of a `const` or `static mut?` item.
-        // That is, the `":" $ty` fragment.
+        // Parse the type of a static item. That is, the `":" $ty` fragment.
+        // FIXME: This could maybe benefit from `.may_recover()`?
         let ty = match (self.eat(&token::Colon), self.check(&token::Eq) | self.check(&token::Semi))
         {
-            // If there wasn't a `:` or the colon was followed by a `=` or `;` recover a missing type.
             (true, false) => self.parse_ty()?,
-            (colon, _) => self.recover_missing_const_type(colon, m),
+            // If there wasn't a `:` or the colon was followed by a `=` or `;`, recover a missing type.
+            (colon, _) => self.recover_missing_global_item_type(colon, Some(mutability)),
+        };
+
+        let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
+
+        self.expect_semi()?;
+
+        Ok((ident, StaticItem { ty, mutability, expr }))
+    }
+
+    /// Parse a constant item with the prefix `"const"` already parsed.
+    ///
+    /// ```ebnf
+    /// Const = "const" ($ident | "_") Generics ":" $ty (= $expr)? WhereClause ";" ;
+    /// ```
+    fn parse_const_item(&mut self) -> PResult<'a, (Ident, Generics, P<Ty>, Option<P<ast::Expr>>)> {
+        let ident = self.parse_ident_or_underscore()?;
+
+        let mut generics = self.parse_generics()?;
+
+        // Check the span for emptiness instead of the list of parameters in order to correctly
+        // recognize and subsequently flag empty parameter lists (`<>`) as unstable.
+        if !generics.span.is_empty() {
+            self.sess.gated_spans.gate(sym::generic_const_items, generics.span);
+        }
+
+        // Parse the type of a constant item. That is, the `":" $ty` fragment.
+        // FIXME: This could maybe benefit from `.may_recover()`?
+        let ty = match (
+            self.eat(&token::Colon),
+            self.check(&token::Eq) | self.check(&token::Semi) | self.check_keyword(kw::Where),
+        ) {
+            (true, false) => self.parse_ty()?,
+            // If there wasn't a `:` or the colon was followed by a `=`, `;` or `where`, recover a missing type.
+            (colon, _) => self.recover_missing_global_item_type(colon, None),
         };
 
+        // Proactively parse a where-clause to be able to provide a good error message in case we
+        // encounter the item body following it.
+        let before_where_clause =
+            if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() };
+
         let expr = if self.eat(&token::Eq) { Some(self.parse_expr()?) } else { None };
+
+        let after_where_clause = self.parse_where_clause()?;
+
+        // Provide a nice error message if the user placed a where-clause before the item body.
+        // Users may be tempted to write such code if they are still used to the deprecated
+        // where-clause location on type aliases and associated types. See also #89122.
+        if before_where_clause.has_where_token && let Some(expr) = &expr {
+            self.sess.emit_err(errors::WhereClauseBeforeConstBody {
+                span: before_where_clause.span,
+                name: ident.span,
+                body: expr.span,
+                sugg: if !after_where_clause.has_where_token {
+                    self.sess.source_map().span_to_snippet(expr.span).ok().map(|body| {
+                        errors::WhereClauseBeforeConstBodySugg {
+                            left: before_where_clause.span.shrink_to_lo(),
+                            snippet: body,
+                            right: before_where_clause.span.shrink_to_hi().to(expr.span),
+                        }
+                    })
+                } else {
+                    // FIXME(generic_const_items): Provide a structured suggestion to merge the first
+                    // where-clause into the second one.
+                    None
+                },
+            });
+        }
+
+        // Merge the predicates of both where-clauses since either one can be relevant.
+        // If we didn't parse a body (which is valid for associated consts in traits) and we were
+        // allowed to recover, `before_where_clause` contains the predicates, otherwise they are
+        // in `after_where_clause`. Further, both of them might contain predicates iff two
+        // where-clauses were provided which is syntactically ill-formed but we want to recover from
+        // it and treat them as one large where-clause.
+        let mut predicates = before_where_clause.predicates;
+        predicates.extend(after_where_clause.predicates);
+        let where_clause = WhereClause {
+            has_where_token: before_where_clause.has_where_token
+                || after_where_clause.has_where_token,
+            predicates,
+            span: if after_where_clause.has_where_token {
+                after_where_clause.span
+            } else {
+                before_where_clause.span
+            },
+        };
+
+        if where_clause.has_where_token {
+            self.sess.gated_spans.gate(sym::generic_const_items, where_clause.span);
+        }
+
+        generics.where_clause = where_clause;
+
         self.expect_semi()?;
-        Ok((id, ty, expr))
+
+        Ok((ident, generics, ty, expr))
     }
 
     /// We were supposed to parse `":" $ty` but the `:` or the type was missing.
     /// This means that the type is missing.
-    fn recover_missing_const_type(&mut self, colon_present: bool, m: Option<Mutability>) -> P<Ty> {
+    fn recover_missing_global_item_type(
+        &mut self,
+        colon_present: bool,
+        m: Option<Mutability>,
+    ) -> P<Ty> {
         // Construct the error and stash it away with the hope
         // that typeck will later enrich the error with a type.
         let kind = match m {
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index 160528e4074..7dec5b0acc8 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -236,7 +236,7 @@ impl<'tcx> ReachableContext<'tcx> {
                     // Reachable constants will be inlined into other crates
                     // unconditionally, so we need to make sure that their
                     // contents are also reachable.
-                    hir::ItemKind::Const(_, init) | hir::ItemKind::Static(_, _, init) => {
+                    hir::ItemKind::Const(_, _, init) | hir::ItemKind::Static(_, _, init) => {
                         self.visit_nested_body(init);
                     }
 
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index aab71494fd3..6872b1b24a9 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -337,6 +337,7 @@ enum LifetimeBinderKind {
     PolyTrait,
     WhereBound,
     Item,
+    ConstItem,
     Function,
     Closure,
     ImplBlock,
@@ -349,7 +350,7 @@ impl LifetimeBinderKind {
             BareFnType => "type",
             PolyTrait => "bound",
             WhereBound => "bound",
-            Item => "item",
+            Item | ConstItem => "item",
             ImplBlock => "impl block",
             Function => "function",
             Closure => "closure",
@@ -2404,30 +2405,44 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                 });
             }
 
-            ItemKind::Static(box ast::StaticItem { ref ty, ref expr, .. })
-            | ItemKind::Const(box ast::ConstItem { ref ty, ref expr, .. }) => {
+            ItemKind::Static(box ast::StaticItem { ref ty, ref expr, .. }) => {
                 self.with_static_rib(|this| {
                     this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Static), |this| {
                         this.visit_ty(ty);
                     });
-                    this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
+                    if let Some(expr) = expr {
+                        // We already forbid generic params because of the above item rib,
+                        // so it doesn't matter whether this is a trivial constant.
+                        this.resolve_const_body(expr, Some((item.ident, ConstantItemKind::Static)));
+                    }
+                });
+            }
+
+            ItemKind::Const(box ast::ConstItem { ref generics, ref ty, ref expr, .. }) => {
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::Item(HasGenericParams::Yes(generics.span)),
+                    LifetimeRibKind::Generics {
+                        binder: item.id,
+                        kind: LifetimeBinderKind::ConstItem,
+                        span: generics.span,
+                    },
+                    |this| {
+                        this.visit_generics(generics);
+
+                        this.with_lifetime_rib(
+                            LifetimeRibKind::Elided(LifetimeRes::Static),
+                            |this| this.visit_ty(ty),
+                        );
+
                         if let Some(expr) = expr {
-                            let constant_item_kind = match item.kind {
-                                ItemKind::Const(..) => ConstantItemKind::Const,
-                                ItemKind::Static(..) => ConstantItemKind::Static,
-                                _ => unreachable!(),
-                            };
-                            // We already forbid generic params because of the above item rib,
-                            // so it doesn't matter whether this is a trivial constant.
-                            this.with_constant_rib(
-                                IsRepeatExpr::No,
-                                ConstantHasGenerics::Yes,
-                                Some((item.ident, constant_item_kind)),
-                                |this| this.visit_expr(expr),
+                            this.resolve_const_body(
+                                expr,
+                                Some((item.ident, ConstantItemKind::Const)),
                             );
                         }
-                    });
-                });
+                    },
+                );
             }
 
             ItemKind::Use(ref use_tree) => {
@@ -2700,28 +2715,31 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         for item in trait_items {
             self.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id));
             match &item.kind {
-                AssocItemKind::Const(box ast::ConstItem { ty, expr, .. }) => {
-                    self.visit_ty(ty);
-                    // Only impose the restrictions of `ConstRibKind` for an
-                    // actual constant expression in a provided default.
-                    if let Some(expr) = expr {
-                        // We allow arbitrary const expressions inside of associated consts,
-                        // even if they are potentially not const evaluatable.
-                        //
-                        // Type parameters can already be used and as associated consts are
-                        // not used as part of the type system, this is far less surprising.
-                        self.with_lifetime_rib(
-                            LifetimeRibKind::Elided(LifetimeRes::Infer),
-                            |this| {
-                                this.with_constant_rib(
-                                    IsRepeatExpr::No,
-                                    ConstantHasGenerics::Yes,
-                                    None,
-                                    |this| this.visit_expr(expr),
-                                )
-                            },
-                        );
-                    }
+                AssocItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
+                    self.with_generic_param_rib(
+                        &generics.params,
+                        RibKind::AssocItem,
+                        LifetimeRibKind::Generics {
+                            binder: item.id,
+                            span: generics.span,
+                            kind: LifetimeBinderKind::ConstItem,
+                        },
+                        |this| {
+                            this.visit_generics(generics);
+                            this.visit_ty(ty);
+
+                            // Only impose the restrictions of `ConstRibKind` for an
+                            // actual constant expression in a provided default.
+                            if let Some(expr) = expr {
+                                // We allow arbitrary const expressions inside of associated consts,
+                                // even if they are potentially not const evaluatable.
+                                //
+                                // Type parameters can already be used and as associated consts are
+                                // not used as part of the type system, this is far less surprising.
+                                this.resolve_const_body(expr, None);
+                            }
+                        },
+                    );
                 }
                 AssocItemKind::Fn(box Fn { generics, .. }) => {
                     walk_assoc_item(self, generics, LifetimeBinderKind::Function, item);
@@ -2876,36 +2894,42 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         use crate::ResolutionError::*;
         self.resolve_doc_links(&item.attrs, MaybeExported::ImplItem(trait_id.ok_or(&item.vis)));
         match &item.kind {
-            AssocItemKind::Const(box ast::ConstItem { ty, expr, .. }) => {
+            AssocItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
                 debug!("resolve_implementation AssocItemKind::Const");
-                // If this is a trait impl, ensure the const
-                // exists in trait
-                self.check_trait_item(
-                    item.id,
-                    item.ident,
-                    &item.kind,
-                    ValueNS,
-                    item.span,
-                    seen_trait_items,
-                    |i, s, c| ConstNotMemberOfTrait(i, s, c),
-                );
 
-                self.visit_ty(ty);
-                if let Some(expr) = expr {
-                    // We allow arbitrary const expressions inside of associated consts,
-                    // even if they are potentially not const evaluatable.
-                    //
-                    // Type parameters can already be used and as associated consts are
-                    // not used as part of the type system, this is far less surprising.
-                    self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
-                        this.with_constant_rib(
-                            IsRepeatExpr::No,
-                            ConstantHasGenerics::Yes,
-                            None,
-                            |this| this.visit_expr(expr),
-                        )
-                    });
-                }
+                self.with_generic_param_rib(
+                    &generics.params,
+                    RibKind::AssocItem,
+                    LifetimeRibKind::Generics {
+                        binder: item.id,
+                        span: generics.span,
+                        kind: LifetimeBinderKind::ConstItem,
+                    },
+                    |this| {
+                        // If this is a trait impl, ensure the const
+                        // exists in trait
+                        this.check_trait_item(
+                            item.id,
+                            item.ident,
+                            &item.kind,
+                            ValueNS,
+                            item.span,
+                            seen_trait_items,
+                            |i, s, c| ConstNotMemberOfTrait(i, s, c),
+                        );
+
+                        this.visit_generics(generics);
+                        this.visit_ty(ty);
+                        if let Some(expr) = expr {
+                            // We allow arbitrary const expressions inside of associated consts,
+                            // even if they are potentially not const evaluatable.
+                            //
+                            // Type parameters can already be used and as associated consts are
+                            // not used as part of the type system, this is far less surprising.
+                            this.resolve_const_body(expr, None);
+                        }
+                    },
+                );
             }
             AssocItemKind::Fn(box Fn { generics, .. }) => {
                 debug!("resolve_implementation AssocItemKind::Fn");
@@ -3063,6 +3087,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         );
     }
 
+    fn resolve_const_body(&mut self, expr: &'ast Expr, item: Option<(Ident, ConstantItemKind)>) {
+        self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
+            this.with_constant_rib(IsRepeatExpr::No, ConstantHasGenerics::Yes, item, |this| {
+                this.visit_expr(expr)
+            });
+        })
+    }
+
     fn resolve_params(&mut self, params: &'ast [Param]) {
         let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
         self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
@@ -4448,6 +4480,7 @@ impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> {
     fn visit_item(&mut self, item: &'ast Item) {
         match &item.kind {
             ItemKind::TyAlias(box TyAlias { ref generics, .. })
+            | ItemKind::Const(box ConstItem { ref generics, .. })
             | ItemKind::Fn(box Fn { ref generics, .. })
             | ItemKind::Enum(_, ref generics)
             | ItemKind::Struct(_, ref generics)
@@ -4467,7 +4500,6 @@ impl<'ast> Visitor<'ast> for LifetimeCountVisitor<'_, '_, '_> {
             ItemKind::Mod(..)
             | ItemKind::ForeignMod(..)
             | ItemKind::Static(..)
-            | ItemKind::Const(..)
             | ItemKind::Use(..)
             | ItemKind::ExternCrate(..)
             | ItemKind::MacroDef(..)
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 753a1adc66d..072fa864f4e 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -2348,6 +2348,14 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             let mut should_continue = true;
             match rib.kind {
                 LifetimeRibKind::Generics { binder: _, span, kind } => {
+                    // Avoid suggesting placing lifetime parameters on constant items unless the relevant
+                    // feature is enabled. Suggest the parent item as a possible location if applicable.
+                    if let LifetimeBinderKind::ConstItem = kind
+                        && !self.r.tcx().features().generic_const_items
+                    {
+                        continue;
+                    }
+
                     if !span.can_be_used_for_suggestions() && suggest_note && let Some(name) = name {
                         suggest_note = false; // Avoid displaying the same help multiple times.
                         err.span_label(
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 1b426ef2048..8122fe96a5c 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -783,6 +783,7 @@ symbols! {
         generic_associated_types,
         generic_associated_types_extended,
         generic_const_exprs,
+        generic_const_items,
         generic_param_attrs,
         get_context,
         global_allocator,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index ff7854d51d9..38d6f1cb23d 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -655,6 +655,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         | hir::ItemKind::Impl(hir::Impl { generics, .. })
                         | hir::ItemKind::Fn(_, generics, _)
                         | hir::ItemKind::TyAlias(_, generics)
+                        | hir::ItemKind::Const(_, generics, _)
                         | hir::ItemKind::TraitAlias(generics, _)
                         | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
                     ..
@@ -720,6 +721,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         | hir::ItemKind::Impl(hir::Impl { generics, .. })
                         | hir::ItemKind::Fn(_, generics, _)
                         | hir::ItemKind::TyAlias(_, generics)
+                        | hir::ItemKind::Const(_, generics, _)
                         | hir::ItemKind::TraitAlias(generics, _)
                         | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }),
                     ..
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 1c4149fd545..c31d104f8cb 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -644,6 +644,10 @@ pub(crate) fn print_inlined_const(tcx: TyCtxt<'_>, did: DefId) -> String {
 }
 
 fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant {
+    let mut generics =
+        clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id));
+    clean::simplify::move_bounds_to_generic_parameters(&mut generics);
+
     clean::Constant {
         type_: clean_middle_ty(
             ty::Binder::dummy(cx.tcx.type_of(def_id).instantiate_identity()),
@@ -651,6 +655,7 @@ fn build_const(cx: &mut DocContext<'_>, def_id: DefId) -> clean::Constant {
             Some(def_id),
             None,
         ),
+        generics: Box::new(generics),
         kind: clean::ConstantKind::Extern { def_id },
     }
 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index aeae1dd0570..9839b82d7d7 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -273,6 +273,7 @@ pub(crate) fn clean_const<'tcx>(constant: &hir::ConstArg, cx: &mut DocContext<'t
             Some(def_id),
             None,
         ),
+        generics: Box::new(Generics::default()),
         kind: ConstantKind::Anonymous { body: constant.value.body },
     }
 }
@@ -284,6 +285,7 @@ pub(crate) fn clean_middle_const<'tcx>(
     // FIXME: instead of storing the stringified expression, store `self` directly instead.
     Constant {
         type_: clean_middle_ty(constant.map_bound(|c| c.ty()), cx, None, None),
+        generics: Box::new(Generics::default()),
         kind: ConstantKind::TyConst { expr: constant.skip_binder().to_string().into() },
     }
 }
@@ -1188,11 +1190,18 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
     let local_did = trait_item.owner_id.to_def_id();
     cx.with_param_env(local_did, |cx| {
         let inner = match trait_item.kind {
-            hir::TraitItemKind::Const(ty, Some(default)) => AssocConstItem(
-                clean_ty(ty, cx),
-                ConstantKind::Local { def_id: local_did, body: default },
-            ),
-            hir::TraitItemKind::Const(ty, None) => TyAssocConstItem(clean_ty(ty, cx)),
+            hir::TraitItemKind::Const(ty, Some(default)) => {
+                let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
+                AssocConstItem(
+                    Box::new(generics),
+                    clean_ty(ty, cx),
+                    ConstantKind::Local { def_id: local_did, body: default },
+                )
+            }
+            hir::TraitItemKind::Const(ty, None) => {
+                let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
+                TyAssocConstItem(Box::new(generics), clean_ty(ty, cx))
+            }
             hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
                 let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Body(body));
                 MethodItem(m, None)
@@ -1237,8 +1246,9 @@ pub(crate) fn clean_impl_item<'tcx>(
     cx.with_param_env(local_did, |cx| {
         let inner = match impl_.kind {
             hir::ImplItemKind::Const(ty, expr) => {
+                let generics = clean_generics(impl_.generics, cx);
                 let default = ConstantKind::Local { def_id: local_did, body: expr };
-                AssocConstItem(clean_ty(ty, cx), default)
+                AssocConstItem(Box::new(generics), clean_ty(ty, cx), default)
             }
             hir::ImplItemKind::Fn(ref sig, body) => {
                 let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body));
@@ -1279,14 +1289,21 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                 None,
             );
 
+            let mut generics = Box::new(clean_ty_generics(
+                cx,
+                tcx.generics_of(assoc_item.def_id),
+                tcx.explicit_predicates_of(assoc_item.def_id),
+            ));
+            simplify::move_bounds_to_generic_parameters(&mut generics);
+
             let provided = match assoc_item.container {
                 ty::ImplContainer => true,
                 ty::TraitContainer => tcx.defaultness(assoc_item.def_id).has_value(),
             };
             if provided {
-                AssocConstItem(ty, ConstantKind::Extern { def_id: assoc_item.def_id })
+                AssocConstItem(generics, ty, ConstantKind::Extern { def_id: assoc_item.def_id })
             } else {
-                TyAssocConstItem(ty)
+                TyAssocConstItem(generics, ty)
             }
         }
         ty::AssocKind::Fn => {
@@ -1379,34 +1396,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                 tcx.generics_of(assoc_item.def_id),
                 ty::GenericPredicates { parent: None, predicates },
             );
-            // Move bounds that are (likely) directly attached to the parameters of the
-            // (generic) associated type from the where clause to the respective parameter.
-            // There is no guarantee that this is what the user actually wrote but we have
-            // no way of knowing.
-            let mut where_predicates = ThinVec::new();
-            for mut pred in generics.where_predicates {
-                if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred
-                    && let Some(GenericParamDef {
-                        kind: GenericParamDefKind::Type { bounds: param_bounds, .. },
-                        ..
-                    }) = generics.params.iter_mut().find(|param| &param.name == arg)
-                {
-                    param_bounds.append(bounds);
-                } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
-                    && let Some(GenericParamDef {
-                        kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
-                        ..
-                    }) = generics.params.iter_mut().find(|param| &param.name == arg)
-                {
-                    param_bounds.extend(bounds.drain(..).map(|bound| match bound {
-                        GenericBound::Outlives(lifetime) => lifetime,
-                        _ => unreachable!(),
-                    }));
-                } else {
-                    where_predicates.push(pred);
-                }
-            }
-            generics.where_predicates = where_predicates;
+            simplify::move_bounds_to_generic_parameters(&mut generics);
 
             if let ty::TraitContainer = assoc_item.container {
                 // Move bounds that are (likely) directly attached to the associated type
@@ -2603,8 +2593,9 @@ fn clean_maybe_renamed_item<'tcx>(
             ItemKind::Static(ty, mutability, body_id) => {
                 StaticItem(Static { type_: clean_ty(ty, cx), mutability, expr: Some(body_id) })
             }
-            ItemKind::Const(ty, body_id) => ConstantItem(Constant {
+            ItemKind::Const(ty, generics, body_id) => ConstantItem(Constant {
                 type_: clean_ty(ty, cx),
+                generics: Box::new(clean_generics(generics, cx)),
                 kind: ConstantKind::Local { body: body_id, def_id },
             }),
             ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy {
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index d53b038019b..7b8f20326ed 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -138,3 +138,38 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId)
         })
         .any(|did| trait_is_same_or_supertrait(cx, did, trait_))
 }
+
+/// Move bounds that are (likely) directly attached to generic parameters from the where-clause to
+/// the respective parameter.
+///
+/// There is no guarantee that this is what the user actually wrote but we have no way of knowing.
+// FIXME(fmease): It'd make a lot of sense to just incorporate this logic into `clean_ty_generics`
+// making every of its users benefit from it.
+pub(crate) fn move_bounds_to_generic_parameters(generics: &mut clean::Generics) {
+    use clean::types::*;
+
+    let mut where_predicates = ThinVec::new();
+    for mut pred in generics.where_predicates.drain(..) {
+        if let WherePredicate::BoundPredicate { ty: Generic(arg), bounds, .. } = &mut pred
+            && let Some(GenericParamDef {
+                kind: GenericParamDefKind::Type { bounds: param_bounds, .. },
+                ..
+            }) = generics.params.iter_mut().find(|param| &param.name == arg)
+        {
+            param_bounds.append(bounds);
+        } else if let WherePredicate::RegionPredicate { lifetime: Lifetime(arg), bounds } = &mut pred
+            && let Some(GenericParamDef {
+                kind: GenericParamDefKind::Lifetime { outlives: param_bounds },
+                ..
+            }) = generics.params.iter_mut().find(|param| &param.name == arg)
+        {
+            param_bounds.extend(bounds.drain(..).map(|bound| match bound {
+                GenericBound::Outlives(lifetime) => lifetime,
+                _ => unreachable!(),
+            }));
+        } else {
+            where_predicates.push(pred);
+        }
+    }
+    generics.where_predicates = where_predicates;
+}
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 6eec01771f3..cb089ddd09a 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -824,9 +824,9 @@ pub(crate) enum ItemKind {
     ProcMacroItem(ProcMacro),
     PrimitiveItem(PrimitiveType),
     /// A required associated constant in a trait declaration.
-    TyAssocConstItem(Type),
+    TyAssocConstItem(Box<Generics>, Type),
     /// An associated constant in a trait impl or a provided one in a trait declaration.
-    AssocConstItem(Type, ConstantKind),
+    AssocConstItem(Box<Generics>, Type, ConstantKind),
     /// A required associated type in a trait declaration.
     ///
     /// The bounds may be non-empty if there is a `where` clause.
@@ -871,8 +871,8 @@ impl ItemKind {
             | MacroItem(_)
             | ProcMacroItem(_)
             | PrimitiveItem(_)
-            | TyAssocConstItem(_)
-            | AssocConstItem(_, _)
+            | TyAssocConstItem(..)
+            | AssocConstItem(..)
             | TyAssocTypeItem(..)
             | AssocTypeItem(..)
             | StrippedItem(_)
@@ -1278,7 +1278,7 @@ impl Lifetime {
     }
 }
 
-#[derive(Clone, Debug)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub(crate) enum WherePredicate {
     BoundPredicate { ty: Type, bounds: Vec<GenericBound>, bound_params: Vec<GenericParamDef> },
     RegionPredicate { lifetime: Lifetime, bounds: Vec<GenericBound> },
@@ -1348,7 +1348,7 @@ impl GenericParamDef {
 }
 
 // maybe use a Generic enum and use Vec<Generic>?
-#[derive(Clone, Debug, Default)]
+#[derive(Clone, PartialEq, Eq, Hash, Debug, Default)]
 pub(crate) struct Generics {
     pub(crate) params: ThinVec<GenericParamDef>,
     pub(crate) where_predicates: ThinVec<WherePredicate>,
@@ -2266,6 +2266,7 @@ pub(crate) struct Static {
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
 pub(crate) struct Constant {
     pub(crate) type_: Type,
+    pub(crate) generics: Box<Generics>,
     pub(crate) kind: ConstantKind,
 }
 
@@ -2515,7 +2516,8 @@ mod size_asserts {
     static_assert_size!(GenericParamDef, 56);
     static_assert_size!(Generics, 16);
     static_assert_size!(Item, 56);
-    static_assert_size!(ItemKind, 64);
+    // FIXME(generic_const_items): Further reduce the size.
+    static_assert_size!(ItemKind, 72);
     static_assert_size!(PathSegment, 40);
     static_assert_size!(Type, 32);
     // tidy-alphabetical-end
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 2fcf61d0049..a6200654ffa 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -748,20 +748,22 @@ fn assoc_href_attr(it: &clean::Item, link: AssocItemLink<'_>, cx: &Context<'_>)
 fn assoc_const(
     w: &mut Buffer,
     it: &clean::Item,
+    generics: &clean::Generics,
     ty: &clean::Type,
     default: Option<&clean::ConstantKind>,
     link: AssocItemLink<'_>,
-    extra: &str,
+    indent: usize,
     cx: &Context<'_>,
 ) {
     let tcx = cx.tcx();
     write!(
         w,
-        "{extra}{vis}const <a{href} class=\"constant\">{name}</a>: {ty}",
-        extra = extra,
+        "{indent}{vis}const <a{href} class=\"constant\">{name}</a>{generics}: {ty}",
+        indent = " ".repeat(indent),
         vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
         href = assoc_href_attr(it, link, cx),
         name = it.name.as_ref().unwrap(),
+        generics = generics.print(cx),
         ty = ty.print(cx),
     );
     if let Some(default) = default {
@@ -774,6 +776,7 @@ fn assoc_const(
         //        Find a way to print constants here without all that jazz.
         write!(w, "{}", Escape(&default.value(tcx).unwrap_or_else(|| default.expr(tcx))));
     }
+    write!(w, "{}", print_where_clause(generics, cx, indent, Ending::NoNewline));
 }
 
 fn assoc_type(
@@ -986,19 +989,22 @@ fn render_assoc_item(
         clean::MethodItem(m, _) => {
             assoc_method(w, item, &m.generics, &m.decl, link, parent, cx, render_mode)
         }
-        kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => assoc_const(
-            w,
-            item,
-            ty,
-            match kind {
-                clean::TyAssocConstItem(_) => None,
-                clean::AssocConstItem(_, default) => Some(default),
-                _ => unreachable!(),
-            },
-            link,
-            if parent == ItemType::Trait { "    " } else { "" },
-            cx,
-        ),
+        kind @ (clean::TyAssocConstItem(generics, ty) | clean::AssocConstItem(generics, ty, _)) => {
+            assoc_const(
+                w,
+                item,
+                generics,
+                ty,
+                match kind {
+                    clean::TyAssocConstItem(..) => None,
+                    clean::AssocConstItem(.., default) => Some(default),
+                    _ => unreachable!(),
+                },
+                link,
+                if parent == ItemType::Trait { 4 } else { 0 },
+                cx,
+            )
+        }
         clean::TyAssocTypeItem(ref generics, ref bounds) => assoc_type(
             w,
             item,
@@ -1565,7 +1571,8 @@ fn render_impl(
                     w.write_str("</section>");
                 }
             }
-            kind @ (clean::TyAssocConstItem(ty) | clean::AssocConstItem(ty, _)) => {
+            kind @ (clean::TyAssocConstItem(generics, ty)
+            | clean::AssocConstItem(generics, ty, _)) => {
                 let source_id = format!("{}.{}", item_type, name);
                 let id = cx.derive_id(source_id.clone());
                 write!(w, "<section id=\"{}\" class=\"{}{}\">", id, item_type, in_trait_class);
@@ -1578,14 +1585,15 @@ fn render_impl(
                 assoc_const(
                     w,
                     item,
+                    generics,
                     ty,
                     match kind {
-                        clean::TyAssocConstItem(_) => None,
-                        clean::AssocConstItem(_, default) => Some(default),
+                        clean::TyAssocConstItem(..) => None,
+                        clean::AssocConstItem(.., default) => Some(default),
                         _ => unreachable!(),
                     },
                     link.anchor(if trait_.is_some() { &source_id } else { &id }),
-                    "",
+                    0,
                     cx,
                 );
                 w.write_str("</h4>");
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 383e3c17088..e363e75f565 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1543,10 +1543,12 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle
 
         write!(
             w,
-            "{vis}const {name}: {typ}",
+            "{vis}const {name}{generics}: {typ}{where_clause}",
             vis = visibility_print_with_space(it.visibility(tcx), it.item_id, cx),
             name = it.name.unwrap(),
+            generics = c.generics.print(cx),
             typ = c.type_.print(cx),
+            where_clause = print_where_clause(&c.generics, cx, 0, Ending::NoNewline),
         );
 
         // FIXME: The code below now prints
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index ac587bf6008..5f130f1875a 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -219,7 +219,7 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
     fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
         match item.kind {
             ItemKind::Static(_, _, _)
-            | ItemKind::Const(_, _)
+            | ItemKind::Const(_, _, _)
             | ItemKind::Fn(_, _, _)
             | ItemKind::Macro(_, _)
             | ItemKind::TyAlias(_, _)
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 91cd55b1113..8673138f649 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -171,6 +171,7 @@ impl FromWithTcx<clean::GenericArg> for GenericArg {
 }
 
 impl FromWithTcx<clean::Constant> for Constant {
+    // FIXME(generic_const_items): Add support for generic const items.
     fn from_tcx(constant: clean::Constant, tcx: TyCtxt<'_>) -> Self {
         let expr = constant.expr(tcx);
         let value = constant.value(tcx);
@@ -321,8 +322,12 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
                 impls: Vec::new(), // Added in JsonRenderer::item
             })
         }
-        TyAssocConstItem(ty) => ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None },
-        AssocConstItem(ty, default) => {
+        // FIXME(generic_const_items): Add support for generic associated consts.
+        TyAssocConstItem(_generics, ty) => {
+            ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: None }
+        }
+        // FIXME(generic_const_items): Add support for generic associated consts.
+        AssocConstItem(_generics, ty, default) => {
             ItemEnum::AssocConst { type_: ty.into_tcx(tcx), default: Some(default.expr(tcx)) }
         }
         TyAssocTypeItem(g, b) => ItemEnum::AssocType {
diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
index 4dc750c03b4..9b26c3573e1 100644
--- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
@@ -50,7 +50,11 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if_chain! {
             if !item.span.from_expansion();
-            if let ItemKind::Const(hir_ty, _) = &item.kind;
+            if let ItemKind::Const(hir_ty, generics, _) = &item.kind;
+            // Since static items may not have generics, skip generic const items.
+            // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it
+            // doesn't account for empty where-clauses that only consist of keyword `where` IINM.
+            if generics.params.is_empty() && !generics.has_where_clause_predicates;
             let ty = hir_ty_to_ty(cx.tcx, hir_ty);
             if let ty::Array(element_type, cst) = ty.kind();
             if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind();
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 87699fd0ca6..8bb2fa92585 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -302,7 +302,7 @@ declare_lint_pass!(NonCopyConst => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTER
 
 impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
     fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
-        if let ItemKind::Const(hir_ty, body_id) = it.kind {
+        if let ItemKind::Const(hir_ty, _generics, body_id) = it.kind {
             let ty = hir_ty_to_ty(cx.tcx, hir_ty);
             if !ignored_macro(cx, it) && is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
                 lint(cx, Source::Item { item: it.span });
diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs
index 3c873a5901d..79f9d45d597 100644
--- a/src/tools/clippy/clippy_lints/src/types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/types/mod.rs
@@ -349,7 +349,7 @@ impl<'tcx> LateLintPass<'tcx> for Types {
         let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id);
 
         match item.kind {
-            ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(
+            ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(
                 cx,
                 ty,
                 CheckTyContext {
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 8cc01f1ef97..7e42924603a 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -301,15 +301,17 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
         (
             Const(box ast::ConstItem {
                 defaultness: ld,
+                generics: lg,
                 ty: lt,
                 expr: le,
             }),
             Const(box ast::ConstItem {
                 defaultness: rd,
+                generics: rg,
                 ty: rt,
                 expr: re,
             }),
-        ) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
+        ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le, re),
         (
             Fn(box ast::Fn {
                 defaultness: ld,
@@ -476,15 +478,17 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
         (
             Const(box ast::ConstItem {
                 defaultness: ld,
+                generics: lg,
                 ty: lt,
                 expr: le,
             }),
             Const(box ast::ConstItem {
                 defaultness: rd,
+                generics: rg,
                 ty: rt,
                 expr: re,
             }),
-        ) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
+        ) => eq_defaultness(*ld, *rd) && eq_generics(lg, rg) && eq_ty(lt, rt) && eq_expr_opt(le, re),
         (
             Fn(box ast::Fn {
                 defaultness: ld,
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 061086c4fc2..f19e09a18ec 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -461,7 +461,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                 // Check if this constant is based on `cfg!(..)`,
                 // which is NOT constant for our purposes.
                 if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id)
-                    && let Node::Item(Item { kind: ItemKind::Const(_, body_id), .. }) = node
+                    && let Node::Item(Item { kind: ItemKind::Const(.., body_id), .. }) = node
                     && let Node::Expr(Expr { kind: ExprKind::Lit(_), span, .. }) = self.lcx
                         .tcx
                         .hir()
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 035511e8912..45b99df46b0 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -2380,7 +2380,7 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol
             for id in tcx.hir().module_items(module) {
                 if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
                     && let item = tcx.hir().item(id)
-                    && let ItemKind::Const(ty, _body) = item.kind {
+                    && let ItemKind::Const(ty, _generics, _body) = item.kind {
                     if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
                         // We could also check for the type name `test::TestDescAndFn`
                         if let Res::Def(DefKind::Struct, _) = path.res {
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index ecbd2f79e36..6cc7fbcacaf 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -11,7 +11,7 @@ use std::path::{Path, PathBuf};
 const ENTRY_LIMIT: usize = 900;
 // FIXME: The following limits should be reduced eventually.
 const ISSUES_ENTRY_LIMIT: usize = 1893;
-const ROOT_ENTRY_LIMIT: usize = 871;
+const ROOT_ENTRY_LIMIT: usize = 872;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
     "rs",     // test source files
diff --git a/tests/rustdoc/generic-const-items.rs b/tests/rustdoc/generic-const-items.rs
new file mode 100644
index 00000000000..e2c6a027afa
--- /dev/null
+++ b/tests/rustdoc/generic-const-items.rs
@@ -0,0 +1,38 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+// @has 'generic_const_items/constant.K.html'
+// @has - '//*[@class="rust item-decl"]//code' \
+// "pub const K<'a, T: 'a + Copy, const N: usize>: Option<[T; N]> \
+// where \
+//     String: From<T>;"
+pub const K<'a, T: 'a + Copy, const N: usize>: Option<[T; N]> = None
+where
+    String: From<T>;
+
+// @has generic_const_items/trait.Trait.html
+pub trait Trait<T: ?Sized> {
+    // @has - '//*[@id="associatedconstant.C"]' \
+    // "const C<'a>: &'a T \
+    // where \
+    //     T: 'a + Eq"
+    const C<'a>: &'a T
+    where
+        T: 'a + Eq;
+}
+
+pub struct Implementor;
+
+// @has generic_const_items/struct.Implementor.html
+// @has - '//h3[@class="code-header"]' 'impl Trait<str> for Implementor'
+impl Trait<str> for Implementor {
+    // @has - '//*[@id="associatedconstant.C"]' \
+    // "const C<'a>: &'a str = \"C\" \
+    // where \
+    //     str: 'a"
+    const C<'a>: &'a str = "C"
+    // In real code we could've left off this bound but adding it explicitly allows us to test if
+    // we render where-clauses on associated consts inside impl blocks correctly.
+    where
+        str: 'a;
+}
diff --git a/tests/rustdoc/inline_cross/auxiliary/generic-const-items.rs b/tests/rustdoc/inline_cross/auxiliary/generic-const-items.rs
new file mode 100644
index 00000000000..0fc7a7aaea2
--- /dev/null
+++ b/tests/rustdoc/inline_cross/auxiliary/generic-const-items.rs
@@ -0,0 +1,22 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+pub const K<'a, T: 'a + Copy, const N: usize>: Option<[T; N]> = None
+where
+    String: From<T>;
+
+pub trait Trait<T: ?Sized> {
+    const C<'a>: &'a T
+    where
+        T: 'a + Eq;
+}
+
+pub struct Implementor;
+
+impl Trait<str> for Implementor {
+    const C<'a>: &'a str = "C"
+    // In real code we could've left off this bound but adding it explicitly allows us to test if
+    // we render where-clauses on associated consts inside impl blocks correctly.
+    where
+        str: 'a;
+}
diff --git a/tests/rustdoc/inline_cross/generic-const-items.rs b/tests/rustdoc/inline_cross/generic-const-items.rs
new file mode 100644
index 00000000000..70cf7af888e
--- /dev/null
+++ b/tests/rustdoc/inline_cross/generic-const-items.rs
@@ -0,0 +1,26 @@
+#![crate_name = "user"]
+
+// aux-crate:generic_const_items=generic-const-items.rs
+// edition:2021
+
+// @has 'user/constant.K.html'
+// @has - '//*[@class="rust item-decl"]//code' \
+// "pub const K<'a, T: 'a + Copy, const N: usize>: Option<[T; N]> \
+// where \
+//     String: From<T>;"
+pub use generic_const_items::K;
+
+// @has user/trait.Trait.html
+// @has - '//*[@id="associatedconstant.C"]' \
+// "const C<'a>: &'a T \
+// where \
+//     T: 'a + Eq"
+pub use generic_const_items::Trait;
+
+// @has user/struct.Implementor.html
+// @has - '//h3[@class="code-header"]' 'impl Trait<str> for Implementor'
+// @has - '//*[@id="associatedconstant.C"]' \
+// "const C<'a>: &'a str = \"C\" \
+// where \
+//     str: 'a"
+pub use generic_const_items::Implementor;
diff --git a/tests/ui/generic-const-items/associated-const-equality.rs b/tests/ui/generic-const-items/associated-const-equality.rs
new file mode 100644
index 00000000000..785d3aa5018
--- /dev/null
+++ b/tests/ui/generic-const-items/associated-const-equality.rs
@@ -0,0 +1,22 @@
+// check-pass
+
+#![feature(generic_const_items, associated_const_equality)]
+#![allow(incomplete_features)]
+
+trait Owner {
+    const C<const N: u32>: u32;
+    const K<const N: u32>: u32;
+}
+
+impl Owner for () {
+    const C<const N: u32>: u32 = N;
+    const K<const N: u32>: u32 = N + 1;
+}
+
+fn take0<const N: u32>(_: impl Owner<C<N> = { N }>) {}
+fn take1(_: impl Owner<K<99> = 100>) {}
+
+fn main() {
+    take0::<128>(());
+    take1(());
+}
diff --git a/tests/ui/generic-const-items/basic.rs b/tests/ui/generic-const-items/basic.rs
new file mode 100644
index 00000000000..73bfa803acd
--- /dev/null
+++ b/tests/ui/generic-const-items/basic.rs
@@ -0,0 +1,61 @@
+// check-pass
+
+// Basic usage patterns of free & associated generic const items.
+
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+fn main() {
+    const NULL<T>: Option<T> = None::<T>;
+    const NOTHING<T>: Option<T> = None; // arg inferred
+
+    let _ = NOTHING::<String>;
+    let _: Option<u8> = NULL; // arg inferred
+
+    const IDENTITY<const X: u64>: u64 = X;
+
+    const COUNT: u64 = IDENTITY::<48>;
+    const AMOUNT: u64 = IDENTITY::<COUNT>;
+    const NUMBER: u64 = IDENTITY::<{ AMOUNT * 2 }>;
+    let _ = NUMBER;
+    let _ = IDENTITY::<0>;
+
+    let _ = match 0 {
+        IDENTITY::<1> => 2,
+        IDENTITY::<{ 1 + 1 }> => 4,
+        _ => 0,
+    };
+
+    const CREATE<I: Inhabited>: I = I::PROOF;
+    let _ = CREATE::<u64>;
+    let _: u64 = CREATE; // arg inferred
+
+    let _ = <() as Main<u64>>::MAKE::<u64>;
+    let _: (u64, u64) = <()>::MAKE; // args inferred
+}
+
+pub fn usage<'any>() {
+    const REGION_POLY<'a>: &'a () = &();
+
+    let _: &'any () = REGION_POLY::<'any>;
+    let _: &'any () = REGION_POLY::<'_>;
+    let _: &'static () = REGION_POLY;
+}
+
+trait Main<O> {
+    type Output<I>;
+    const MAKE<I: Inhabited>: Self::Output<I>;
+}
+
+impl<O: Inhabited> Main<O> for () {
+    type Output<I> = (O, I);
+    const MAKE<I: Inhabited>: Self::Output<I> = (O::PROOF, I::PROOF);
+}
+
+trait Inhabited {
+    const PROOF: Self;
+}
+
+impl Inhabited for u64 {
+    const PROOF: Self = 512;
+}
diff --git a/tests/ui/generic-const-items/compare-impl-item.rs b/tests/ui/generic-const-items/compare-impl-item.rs
new file mode 100644
index 00000000000..01e4477c698
--- /dev/null
+++ b/tests/ui/generic-const-items/compare-impl-item.rs
@@ -0,0 +1,30 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+trait Trait<P> {
+    const A: ();
+    const B<const K: u64, const Q: u64>: u64;
+    const C<T>: T;
+    const D<const N: usize>: usize;
+
+    const E: usize;
+    const F<T: PartialEq>: ();
+}
+
+impl<P> Trait<P> for () {
+    const A<T>: () = ();
+    //~^ ERROR const `A` has 1 type parameter but its trait declaration has 0 type parameters
+    const B<const K: u64>: u64 = 0;
+    //~^ ERROR const `B` has 1 const parameter but its trait declaration has 2 const parameters
+    const C<'a>: &'a str = "";
+    //~^ ERROR const `C` has 0 type parameters but its trait declaration has 1 type parameter
+    const D<const N: u16>: u16 = N;
+    //~^ ERROR const `D` has an incompatible generic parameter for trait `Trait`
+
+    const E: usize = 1024
+    where
+        P: Copy; //~ ERROR impl has stricter requirements than trait
+    const F<T: Eq>: () = (); //~ ERROR impl has stricter requirements than trait
+}
+
+fn main() {}
diff --git a/tests/ui/generic-const-items/compare-impl-item.stderr b/tests/ui/generic-const-items/compare-impl-item.stderr
new file mode 100644
index 00000000000..8610d8cba00
--- /dev/null
+++ b/tests/ui/generic-const-items/compare-impl-item.stderr
@@ -0,0 +1,66 @@
+error[E0049]: const `A` has 1 type parameter but its trait declaration has 0 type parameters
+  --> $DIR/compare-impl-item.rs:15:13
+   |
+LL |     const A: ();
+   |            - expected 0 type parameters
+...
+LL |     const A<T>: () = ();
+   |             ^ found 1 type parameter
+
+error[E0049]: const `B` has 1 const parameter but its trait declaration has 2 const parameters
+  --> $DIR/compare-impl-item.rs:17:13
+   |
+LL |     const B<const K: u64, const Q: u64>: u64;
+   |             ------------  ------------
+   |             |
+   |             expected 2 const parameters
+...
+LL |     const B<const K: u64>: u64 = 0;
+   |             ^^^^^^^^^^^^ found 1 const parameter
+
+error[E0049]: const `C` has 0 type parameters but its trait declaration has 1 type parameter
+  --> $DIR/compare-impl-item.rs:19:13
+   |
+LL |     const C<T>: T;
+   |             - expected 1 type parameter
+...
+LL |     const C<'a>: &'a str = "";
+   |             ^^ found 0 type parameters
+
+error[E0053]: const `D` has an incompatible generic parameter for trait `Trait`
+  --> $DIR/compare-impl-item.rs:21:13
+   |
+LL | trait Trait<P> {
+   |       -----
+...
+LL |     const D<const N: usize>: usize;
+   |             -------------- expected const parameter of type `usize`
+...
+LL | impl<P> Trait<P> for () {
+   | -----------------------
+...
+LL |     const D<const N: u16>: u16 = N;
+   |             ^^^^^^^^^^^^ found const parameter of type `u16`
+
+error[E0276]: impl has stricter requirements than trait
+  --> $DIR/compare-impl-item.rs:26:12
+   |
+LL |     const E: usize;
+   |     -------------- definition of `E` from trait
+...
+LL |         P: Copy;
+   |            ^^^^ impl has extra requirement `P: Copy`
+
+error[E0276]: impl has stricter requirements than trait
+  --> $DIR/compare-impl-item.rs:27:16
+   |
+LL |     const F<T: PartialEq>: ();
+   |     ------------------------- definition of `F` from trait
+...
+LL |     const F<T: Eq>: () = ();
+   |                ^^ impl has extra requirement `T: Eq`
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0049, E0053, E0276.
+For more information about an error, try `rustc --explain E0049`.
diff --git a/tests/ui/generic-const-items/const-trait-impl.rs b/tests/ui/generic-const-items/const-trait-impl.rs
new file mode 100644
index 00000000000..8da1448df4f
--- /dev/null
+++ b/tests/ui/generic-const-items/const-trait-impl.rs
@@ -0,0 +1,24 @@
+// check-pass
+
+// Test that we can call methods from const trait impls inside of generic const items.
+
+#![feature(generic_const_items, const_trait_impl)]
+#![allow(incomplete_features)]
+#![crate_type = "lib"]
+
+// FIXME(generic_const_items): Interpret `~const` as always-const.
+const CREATE<T: ~const Create>: T = T::create();
+
+pub const K0: i32 = CREATE::<i32>;
+pub const K1: i32 = CREATE; // arg inferred
+
+#[const_trait]
+trait Create {
+    fn create() -> Self;
+}
+
+impl const Create for i32 {
+    fn create() -> i32 {
+        4096
+    }
+}
diff --git a/tests/ui/generic-const-items/duplicate-where-clause.rs b/tests/ui/generic-const-items/duplicate-where-clause.rs
new file mode 100644
index 00000000000..68da4073fc1
--- /dev/null
+++ b/tests/ui/generic-const-items/duplicate-where-clause.rs
@@ -0,0 +1,27 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+trait Tr<P> {
+    const K: ()
+    where
+        P: Copy
+    where
+        P: Eq;
+    //~^ ERROR cannot define duplicate `where` clauses on an item
+}
+
+// Test that we error on the first where-clause but also that we don't suggest to swap it with the
+// body as it would conflict with the second where-clause.
+// FIXME(generic_const_items): We should provide a structured sugg to merge the 1st into the 2nd WC.
+
+impl<P> Tr<P> for () {
+    const K: ()
+    where
+        P: Eq
+    = ()
+    where
+        P: Copy;
+    //~^^^^^ ERROR where clauses are not allowed before const item bodies
+}
+
+fn main() {}
diff --git a/tests/ui/generic-const-items/duplicate-where-clause.stderr b/tests/ui/generic-const-items/duplicate-where-clause.stderr
new file mode 100644
index 00000000000..5fa61b01ee9
--- /dev/null
+++ b/tests/ui/generic-const-items/duplicate-where-clause.stderr
@@ -0,0 +1,27 @@
+error: cannot define duplicate `where` clauses on an item
+  --> $DIR/duplicate-where-clause.rs:9:9
+   |
+LL |         P: Copy
+   |         - previous `where` clause starts here
+LL |     where
+LL |         P: Eq;
+   |         ^
+   |
+help: consider joining the two `where` clauses into one
+   |
+LL |         P: Copy,
+   |                ~
+
+error: where clauses are not allowed before const item bodies
+  --> $DIR/duplicate-where-clause.rs:19:5
+   |
+LL |       const K: ()
+   |             - while parsing this const item
+LL | /     where
+LL | |         P: Eq
+   | |_____________^ unexpected where clause
+LL |       = ()
+   |         -- the item body
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/generic-const-items/elided-lifetimes.rs b/tests/ui/generic-const-items/elided-lifetimes.rs
new file mode 100644
index 00000000000..cca73e2e81e
--- /dev/null
+++ b/tests/ui/generic-const-items/elided-lifetimes.rs
@@ -0,0 +1,18 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+// Check that we forbid elided lifetimes inside the generics of const items.
+
+const K<T>: () = ()
+where
+    &T: Copy; //~ ERROR `&` without an explicit lifetime name cannot be used here
+
+const I<const S: &str>: &str = "";
+//~^ ERROR `&` without an explicit lifetime name cannot be used here
+//~| ERROR `&str` is forbidden as the type of a const generic parameter
+
+const B<T: Trait<'_>>: () = (); //~ ERROR `'_` cannot be used here
+
+trait Trait<'a> {}
+
+fn main() {}
diff --git a/tests/ui/generic-const-items/elided-lifetimes.stderr b/tests/ui/generic-const-items/elided-lifetimes.stderr
new file mode 100644
index 00000000000..8cd3f9ee7a9
--- /dev/null
+++ b/tests/ui/generic-const-items/elided-lifetimes.stderr
@@ -0,0 +1,35 @@
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/elided-lifetimes.rs:8:5
+   |
+LL |     &T: Copy;
+   |     ^ explicit lifetime name needed here
+   |
+help: consider introducing a higher-ranked lifetime here
+   |
+LL |     for<'a> &'a T: Copy;
+   |     +++++++  ++
+
+error[E0637]: `&` without an explicit lifetime name cannot be used here
+  --> $DIR/elided-lifetimes.rs:10:18
+   |
+LL | const I<const S: &str>: &str = "";
+   |                  ^ explicit lifetime name needed here
+
+error[E0637]: `'_` cannot be used here
+  --> $DIR/elided-lifetimes.rs:14:18
+   |
+LL | const B<T: Trait<'_>>: () = ();
+   |                  ^^ `'_` is a reserved lifetime name
+
+error: `&str` is forbidden as the type of a const generic parameter
+  --> $DIR/elided-lifetimes.rs:10:18
+   |
+LL | const I<const S: &str>: &str = "";
+   |                  ^^^^
+   |
+   = note: the only supported types are integers, `bool` and `char`
+   = help: more complex types are supported with `#![feature(adt_const_params)]`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0637`.
diff --git a/tests/ui/generic-const-items/evaluatable-bounds.rs b/tests/ui/generic-const-items/evaluatable-bounds.rs
new file mode 100644
index 00000000000..cdcfcf9188a
--- /dev/null
+++ b/tests/ui/generic-const-items/evaluatable-bounds.rs
@@ -0,0 +1,31 @@
+// This is a regression test for issue #104400.
+
+// revisions: unconstrained constrained
+//[constrained] check-pass
+
+// Test that we can constrain generic const items that appear inside associated consts by
+// adding a (makeshift) "evaluatable"-bound to the item.
+
+#![feature(generic_const_items, generic_const_exprs)]
+#![allow(incomplete_features)]
+
+trait Trait {
+    const LEN: usize;
+
+    #[cfg(unconstrained)]
+    const ARRAY: [i32; Self::LEN]; //[unconstrained]~ ERROR unconstrained generic constant
+
+    #[cfg(constrained)]
+    const ARRAY: [i32; Self::LEN]
+    where
+        [(); Self::LEN]:;
+}
+
+impl Trait for () {
+    const LEN: usize = 2;
+    const ARRAY: [i32; Self::LEN] = [360, 720];
+}
+
+fn main() {
+    let [_, _] = <() as Trait>::ARRAY;
+}
diff --git a/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr b/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr
new file mode 100644
index 00000000000..930080f7c37
--- /dev/null
+++ b/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr
@@ -0,0 +1,10 @@
+error: unconstrained generic constant
+  --> $DIR/evaluatable-bounds.rs:16:5
+   |
+LL |     const ARRAY: [i32; Self::LEN];
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: try adding a `where` bound using this expression: `where [(); Self::LEN]:`
+
+error: aborting due to previous error
+
diff --git a/tests/ui/generic-const-items/feature-gate-generic_const_items.rs b/tests/ui/generic-const-items/feature-gate-generic_const_items.rs
new file mode 100644
index 00000000000..5c241f256eb
--- /dev/null
+++ b/tests/ui/generic-const-items/feature-gate-generic_const_items.rs
@@ -0,0 +1,37 @@
+pub trait Trait<A> {
+    const ONE<T>: i32;
+    //~^ ERROR generic const items are experimental
+
+    const TWO: ()
+    where
+        A: Copy;
+    //~^^ ERROR generic const items are experimental
+}
+
+const CONST<T>: i32 = 0;
+//~^ ERROR generic const items are experimental
+
+const EMPTY<>: i32 = 0;
+//~^ ERROR generic const items are experimental
+
+const TRUE: () = ()
+where
+    String: Clone;
+//~^^ ERROR generic const items are experimental
+
+// Ensure that we flag generic const items inside macro calls as well:
+
+macro_rules! discard {
+    ($item:item) => {}
+}
+
+discard! { const FREE<T>: () = (); }
+//~^ ERROR generic const items are experimental
+
+discard! { impl () { const ASSOC<const N: ()>: () = (); } }
+//~^ ERROR generic const items are experimental
+
+discard! { impl () { const ASSOC: i32 = 0 where String: Copy; } }
+//~^ ERROR generic const items are experimental
+
+fn main() {}
diff --git a/tests/ui/generic-const-items/feature-gate-generic_const_items.stderr b/tests/ui/generic-const-items/feature-gate-generic_const_items.stderr
new file mode 100644
index 00000000000..a1fdf5f6ef3
--- /dev/null
+++ b/tests/ui/generic-const-items/feature-gate-generic_const_items.stderr
@@ -0,0 +1,77 @@
+error[E0658]: generic const items are experimental
+  --> $DIR/feature-gate-generic_const_items.rs:2:14
+   |
+LL |     const ONE<T>: i32;
+   |              ^^^
+   |
+   = note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
+   = help: add `#![feature(generic_const_items)]` to the crate attributes to enable
+
+error[E0658]: generic const items are experimental
+  --> $DIR/feature-gate-generic_const_items.rs:6:5
+   |
+LL | /     where
+LL | |         A: Copy;
+   | |_______________^
+   |
+   = note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
+   = help: add `#![feature(generic_const_items)]` to the crate attributes to enable
+
+error[E0658]: generic const items are experimental
+  --> $DIR/feature-gate-generic_const_items.rs:11:12
+   |
+LL | const CONST<T>: i32 = 0;
+   |            ^^^
+   |
+   = note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
+   = help: add `#![feature(generic_const_items)]` to the crate attributes to enable
+
+error[E0658]: generic const items are experimental
+  --> $DIR/feature-gate-generic_const_items.rs:14:12
+   |
+LL | const EMPTY<>: i32 = 0;
+   |            ^^
+   |
+   = note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
+   = help: add `#![feature(generic_const_items)]` to the crate attributes to enable
+
+error[E0658]: generic const items are experimental
+  --> $DIR/feature-gate-generic_const_items.rs:18:1
+   |
+LL | / where
+LL | |     String: Clone;
+   | |_________________^
+   |
+   = note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
+   = help: add `#![feature(generic_const_items)]` to the crate attributes to enable
+
+error[E0658]: generic const items are experimental
+  --> $DIR/feature-gate-generic_const_items.rs:28:22
+   |
+LL | discard! { const FREE<T>: () = (); }
+   |                      ^^^
+   |
+   = note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
+   = help: add `#![feature(generic_const_items)]` to the crate attributes to enable
+
+error[E0658]: generic const items are experimental
+  --> $DIR/feature-gate-generic_const_items.rs:31:33
+   |
+LL | discard! { impl () { const ASSOC<const N: ()>: () = (); } }
+   |                                 ^^^^^^^^^^^^^
+   |
+   = note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
+   = help: add `#![feature(generic_const_items)]` to the crate attributes to enable
+
+error[E0658]: generic const items are experimental
+  --> $DIR/feature-gate-generic_const_items.rs:34:43
+   |
+LL | discard! { impl () { const ASSOC: i32 = 0 where String: Copy; } }
+   |                                           ^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
+   = help: add `#![feature(generic_const_items)]` to the crate attributes to enable
+
+error: aborting due to 8 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/generic-const-items/inference-failure.rs b/tests/ui/generic-const-items/inference-failure.rs
new file mode 100644
index 00000000000..fd4f424dd97
--- /dev/null
+++ b/tests/ui/generic-const-items/inference-failure.rs
@@ -0,0 +1,15 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+const NONE<T>: Option<T> = None::<T>;
+const IGNORE<T>: () = ();
+
+fn none() {
+    let _ = NONE; //~ ERROR type annotations needed
+}
+
+fn ignore() {
+    let _ = IGNORE; //~ ERROR type annotations needed
+}
+
+fn main() {}
diff --git a/tests/ui/generic-const-items/inference-failure.stderr b/tests/ui/generic-const-items/inference-failure.stderr
new file mode 100644
index 00000000000..22ff1b9ba7f
--- /dev/null
+++ b/tests/ui/generic-const-items/inference-failure.stderr
@@ -0,0 +1,20 @@
+error[E0282]: type annotations needed for `Option<T>`
+  --> $DIR/inference-failure.rs:8:9
+   |
+LL |     let _ = NONE;
+   |         ^
+   |
+help: consider giving this pattern a type, where the type for type parameter `T` is specified
+   |
+LL |     let _: Option<T> = NONE;
+   |          +++++++++++
+
+error[E0282]: type annotations needed
+  --> $DIR/inference-failure.rs:12:13
+   |
+LL |     let _ = IGNORE;
+   |             ^^^^^^ cannot infer type for type parameter `T` declared on the constant `IGNORE`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/generic-const-items/misplaced-where-clause.fixed b/tests/ui/generic-const-items/misplaced-where-clause.fixed
new file mode 100644
index 00000000000..bff470c2883
--- /dev/null
+++ b/tests/ui/generic-const-items/misplaced-where-clause.fixed
@@ -0,0 +1,18 @@
+// run-rustfix
+
+#![feature(generic_const_items)]
+#![allow(incomplete_features, dead_code)]
+
+const K<T>: u64
+= T::K where
+    T: Tr<()>;
+//~^^^ ERROR where clauses are not allowed before const item bodies
+
+trait Tr<P> {
+    const K: u64
+    = 0 where
+        P: Copy;
+    //~^^^ ERROR where clauses are not allowed before const item bodies
+}
+
+fn main() {}
diff --git a/tests/ui/generic-const-items/misplaced-where-clause.rs b/tests/ui/generic-const-items/misplaced-where-clause.rs
new file mode 100644
index 00000000000..b14c6d594a5
--- /dev/null
+++ b/tests/ui/generic-const-items/misplaced-where-clause.rs
@@ -0,0 +1,20 @@
+// run-rustfix
+
+#![feature(generic_const_items)]
+#![allow(incomplete_features, dead_code)]
+
+const K<T>: u64
+where
+    T: Tr<()>
+= T::K;
+//~^^^ ERROR where clauses are not allowed before const item bodies
+
+trait Tr<P> {
+    const K: u64
+    where
+        P: Copy
+    = 0;
+    //~^^^ ERROR where clauses are not allowed before const item bodies
+}
+
+fn main() {}
diff --git a/tests/ui/generic-const-items/misplaced-where-clause.stderr b/tests/ui/generic-const-items/misplaced-where-clause.stderr
new file mode 100644
index 00000000000..431741d8724
--- /dev/null
+++ b/tests/ui/generic-const-items/misplaced-where-clause.stderr
@@ -0,0 +1,36 @@
+error: where clauses are not allowed before const item bodies
+  --> $DIR/misplaced-where-clause.rs:7:1
+   |
+LL |   const K<T>: u64
+   |         - while parsing this const item
+LL | / where
+LL | |     T: Tr<()>
+   | |_____________^ unexpected where clause
+LL |   = T::K;
+   |     ---- the item body
+   |
+help: move the body before the where clause
+   |
+LL ~ = T::K where
+LL ~     T: Tr<()>;
+   |
+
+error: where clauses are not allowed before const item bodies
+  --> $DIR/misplaced-where-clause.rs:14:5
+   |
+LL |       const K: u64
+   |             - while parsing this const item
+LL | /     where
+LL | |         P: Copy
+   | |_______________^ unexpected where clause
+LL |       = 0;
+   |         - the item body
+   |
+help: move the body before the where clause
+   |
+LL ~     = 0 where
+LL ~         P: Copy;
+   |
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/generic-const-items/parameter-defaults.rs b/tests/ui/generic-const-items/parameter-defaults.rs
new file mode 100644
index 00000000000..a6f82c249fe
--- /dev/null
+++ b/tests/ui/generic-const-items/parameter-defaults.rs
@@ -0,0 +1,14 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+// Check that we emit a *hard* error (not just a lint warning or error for example) for generic
+// parameter defaults on free const items since we are not limited by backward compatibility.
+#![allow(invalid_type_param_default)] // Should have no effect here.
+
+// FIXME(default_type_parameter_fallback): Consider reallowing them once they work properly.
+
+const NONE<T = ()>: Option<T> = None::<T>; //~ ERROR defaults for type parameters are only allowed
+
+fn main() {
+    let _ = NONE;
+}
diff --git a/tests/ui/generic-const-items/parameter-defaults.stderr b/tests/ui/generic-const-items/parameter-defaults.stderr
new file mode 100644
index 00000000000..62da45e55d6
--- /dev/null
+++ b/tests/ui/generic-const-items/parameter-defaults.stderr
@@ -0,0 +1,8 @@
+error: defaults for type parameters are only allowed in `struct`, `enum`, `type`, or `trait` definitions
+  --> $DIR/parameter-defaults.rs:10:12
+   |
+LL | const NONE<T = ()>: Option<T> = None::<T>;
+   |            ^^^^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/generic-const-items/recursive.rs b/tests/ui/generic-const-items/recursive.rs
new file mode 100644
index 00000000000..3266b37d380
--- /dev/null
+++ b/tests/ui/generic-const-items/recursive.rs
@@ -0,0 +1,12 @@
+// FIXME(generic_const_items): This leads to a stack overflow in the compiler!
+// known-bug: unknown
+// ignore-test
+
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+const RECUR<T>: () = RECUR::<(T,)>;
+
+fn main() {
+    let _ = RECUR::<()>;
+}
diff --git a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.rs b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.rs
new file mode 100644
index 00000000000..dd00b327d2d
--- /dev/null
+++ b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.rs
@@ -0,0 +1,12 @@
+#![feature(generic_const_items, trivial_bounds)]
+#![allow(incomplete_features)]
+
+// Ensure that we check if trivial bounds on const items hold or not.
+
+const UNUSABLE: () = ()
+where
+    String: Copy;
+
+fn main() {
+    let _ = UNUSABLE; //~ ERROR the trait bound `String: Copy` is not satisfied
+}
diff --git a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.stderr b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.stderr
new file mode 100644
index 00000000000..c3ef94529a4
--- /dev/null
+++ b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-0.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/trivially-unsatisfied-bounds-0.rs:11:13
+   |
+LL |     let _ = UNUSABLE;
+   |             ^^^^^^^^ the trait `Copy` is not implemented for `String`
+   |
+note: required by a bound in `UNUSABLE`
+  --> $DIR/trivially-unsatisfied-bounds-0.rs:8:13
+   |
+LL | const UNUSABLE: () = ()
+   |       -------- required by a bound in this constant
+LL | where
+LL |     String: Copy;
+   |             ^^^^ required by this bound in `UNUSABLE`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.rs b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.rs
new file mode 100644
index 00000000000..9243deac870
--- /dev/null
+++ b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.rs
@@ -0,0 +1,12 @@
+#![feature(generic_const_items, trivial_bounds)]
+#![allow(incomplete_features, dead_code, trivial_bounds)]
+
+// FIXME(generic_const_items): This looks like a bug to me. I expected that we wouldn't emit any
+// errors. I thought we'd skip the evaluation of consts whose bounds don't hold.
+
+const UNUSED: () = ()
+where
+    String: Copy;
+//~^^^ ERROR evaluation of constant value failed
+
+fn main() {}
diff --git a/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.stderr b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.stderr
new file mode 100644
index 00000000000..a68400798d8
--- /dev/null
+++ b/tests/ui/generic-const-items/trivially-unsatisfied-bounds-1.stderr
@@ -0,0 +1,11 @@
+error[E0080]: evaluation of constant value failed
+  --> $DIR/trivially-unsatisfied-bounds-1.rs:7:1
+   |
+LL | / const UNUSED: () = ()
+LL | | where
+LL | |     String: Copy;
+   | |_________________^ entering unreachable code
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/generic-const-items/unsatisfied-bounds.rs b/tests/ui/generic-const-items/unsatisfied-bounds.rs
new file mode 100644
index 00000000000..05879900172
--- /dev/null
+++ b/tests/ui/generic-const-items/unsatisfied-bounds.rs
@@ -0,0 +1,34 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+// Ensure that we check if bounds on const items hold or not.
+
+use std::convert::Infallible;
+
+const C<T: Copy>: () = ();
+
+const K<T>: () = ()
+where
+    Infallible: From<T>;
+
+trait Trait<P> {
+    const A: u32
+    where
+        P: Copy;
+
+    const B<T>: u32
+    where
+        Infallible: From<T>;
+}
+
+impl<P> Trait<P> for () {
+    const A: u32 = 0;
+    const B<T>: u32 = 1;
+}
+
+fn main() {
+    let () = C::<String>; //~ ERROR the trait bound `String: Copy` is not satisfied
+    let () = K::<()>; //~ ERROR the trait bound `Infallible: From<()>` is not satisfied
+    let _ = <() as Trait<Vec<u8>>>::A; //~ ERROR the trait bound `Vec<u8>: Copy` is not satisfied
+    let _ = <() as Trait<&'static str>>::B::<()>; //~ ERROR the trait bound `Infallible: From<()>` is not satisfied
+}
diff --git a/tests/ui/generic-const-items/unsatisfied-bounds.stderr b/tests/ui/generic-const-items/unsatisfied-bounds.stderr
new file mode 100644
index 00000000000..1fda460372a
--- /dev/null
+++ b/tests/ui/generic-const-items/unsatisfied-bounds.stderr
@@ -0,0 +1,62 @@
+error[E0277]: the trait bound `String: Copy` is not satisfied
+  --> $DIR/unsatisfied-bounds.rs:30:18
+   |
+LL |     let () = C::<String>;
+   |                  ^^^^^^ the trait `Copy` is not implemented for `String`
+   |
+note: required by a bound in `C`
+  --> $DIR/unsatisfied-bounds.rs:8:12
+   |
+LL | const C<T: Copy>: () = ();
+   |            ^^^^ required by this bound in `C`
+
+error[E0277]: the trait bound `Infallible: From<()>` is not satisfied
+  --> $DIR/unsatisfied-bounds.rs:31:18
+   |
+LL |     let () = K::<()>;
+   |                  ^^ the trait `From<()>` is not implemented for `Infallible`
+   |
+   = help: the trait `From<!>` is implemented for `Infallible`
+note: required by a bound in `K`
+  --> $DIR/unsatisfied-bounds.rs:12:17
+   |
+LL | const K<T>: () = ()
+   |       - required by a bound in this constant
+LL | where
+LL |     Infallible: From<T>;
+   |                 ^^^^^^^ required by this bound in `K`
+
+error[E0277]: the trait bound `Vec<u8>: Copy` is not satisfied
+  --> $DIR/unsatisfied-bounds.rs:32:13
+   |
+LL |     let _ = <() as Trait<Vec<u8>>>::A;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Vec<u8>`
+   |
+note: required by a bound in `Trait::A`
+  --> $DIR/unsatisfied-bounds.rs:17:12
+   |
+LL |     const A: u32
+   |           - required by a bound in this associated constant
+LL |     where
+LL |         P: Copy;
+   |            ^^^^ required by this bound in `Trait::A`
+
+error[E0277]: the trait bound `Infallible: From<()>` is not satisfied
+  --> $DIR/unsatisfied-bounds.rs:33:46
+   |
+LL |     let _ = <() as Trait<&'static str>>::B::<()>;
+   |                                              ^^ the trait `From<()>` is not implemented for `Infallible`
+   |
+   = help: the trait `From<!>` is implemented for `Infallible`
+note: required by a bound in `Trait::B`
+  --> $DIR/unsatisfied-bounds.rs:21:21
+   |
+LL |     const B<T>: u32
+   |           - required by a bound in this associated constant
+LL |     where
+LL |         Infallible: From<T>;
+   |                     ^^^^^^^ required by this bound in `Trait::B`
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/generic-const-items/unsatisfied-evaluatable-bounds.rs b/tests/ui/generic-const-items/unsatisfied-evaluatable-bounds.rs
new file mode 100644
index 00000000000..961e5b4aeeb
--- /dev/null
+++ b/tests/ui/generic-const-items/unsatisfied-evaluatable-bounds.rs
@@ -0,0 +1,12 @@
+#![feature(generic_const_items, generic_const_exprs)]
+#![allow(incomplete_features)]
+
+// Ensure that we check if (makeshift) "evaluatable"-bounds on const items hold or not.
+
+const POSITIVE<const N: usize>: usize = N
+where
+    [(); N - 1]:; //~ ERROR evaluation of `POSITIVE::<0>::{constant#0}` failed
+
+fn main() {
+    let _ = POSITIVE::<0>;
+}
diff --git a/tests/ui/generic-const-items/unsatisfied-evaluatable-bounds.stderr b/tests/ui/generic-const-items/unsatisfied-evaluatable-bounds.stderr
new file mode 100644
index 00000000000..bed213b0caa
--- /dev/null
+++ b/tests/ui/generic-const-items/unsatisfied-evaluatable-bounds.stderr
@@ -0,0 +1,9 @@
+error[E0080]: evaluation of `POSITIVE::<0>::{constant#0}` failed
+  --> $DIR/unsatisfied-evaluatable-bounds.rs:8:10
+   |
+LL |     [(); N - 1]:;
+   |          ^^^^^ attempt to compute `0_usize - 1_usize`, which would overflow
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0080`.
diff --git a/tests/ui/generic-const-items/unsatisfied-outlives-bounds.rs b/tests/ui/generic-const-items/unsatisfied-outlives-bounds.rs
new file mode 100644
index 00000000000..204cf9def36
--- /dev/null
+++ b/tests/ui/generic-const-items/unsatisfied-outlives-bounds.rs
@@ -0,0 +1,17 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features)]
+
+// Ensure that we check if outlives-bounds on const items hold or not.
+
+const C<'a, T: 'a>: () = ();
+const K<'a, 'b: 'a>: () = ();
+
+fn parametrized0<'any>() {
+    let () = C::<'static, &'any ()>; //~ ERROR lifetime may not live long enough
+}
+
+fn parametrized1<'any>() {
+    let () = K::<'static, 'any>; //~ ERROR lifetime may not live long enough
+}
+
+fn main() {}
diff --git a/tests/ui/generic-const-items/unsatisfied-outlives-bounds.stderr b/tests/ui/generic-const-items/unsatisfied-outlives-bounds.stderr
new file mode 100644
index 00000000000..72e4265b3d7
--- /dev/null
+++ b/tests/ui/generic-const-items/unsatisfied-outlives-bounds.stderr
@@ -0,0 +1,18 @@
+error: lifetime may not live long enough
+  --> $DIR/unsatisfied-outlives-bounds.rs:10:14
+   |
+LL | fn parametrized0<'any>() {
+   |                  ---- lifetime `'any` defined here
+LL |     let () = C::<'static, &'any ()>;
+   |              ^^^^^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static`
+
+error: lifetime may not live long enough
+  --> $DIR/unsatisfied-outlives-bounds.rs:14:14
+   |
+LL | fn parametrized1<'any>() {
+   |                  ---- lifetime `'any` defined here
+LL |     let () = K::<'static, 'any>;
+   |              ^^^^^^^^^^^^^^^^^^ requires that `'any` must outlive `'static`
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/object-safety/assoc_const_bounds.rs b/tests/ui/object-safety/assoc_const_bounds.rs
index 94b1f63165b..bfa21fd9aea 100644
--- a/tests/ui/object-safety/assoc_const_bounds.rs
+++ b/tests/ui/object-safety/assoc_const_bounds.rs
@@ -1,7 +1,12 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features, dead_code)]
+
+// check-pass
+
 trait Foo<T> {
     const BAR: bool
-        where //~ ERROR: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
-            Self: Sized;
+    where
+        Self: Sized;
 }
 
 trait Cake {}
diff --git a/tests/ui/object-safety/assoc_const_bounds.stderr b/tests/ui/object-safety/assoc_const_bounds.stderr
deleted file mode 100644
index 09bc11e178a..00000000000
--- a/tests/ui/object-safety/assoc_const_bounds.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
-  --> $DIR/assoc_const_bounds.rs:3:9
-   |
-LL | trait Foo<T> {
-   |              - while parsing this item list starting here
-LL |     const BAR: bool
-   |                    - expected one of 7 possible tokens
-LL |         where
-   |         ^^^^^ unexpected token
-LL |             Self: Sized;
-LL | }
-   | - the item list ends here
-
-error: aborting due to previous error
-
diff --git a/tests/ui/object-safety/assoc_const_bounds_sized.rs b/tests/ui/object-safety/assoc_const_bounds_sized.rs
index 2a76e5dce2b..87d1f06f036 100644
--- a/tests/ui/object-safety/assoc_const_bounds_sized.rs
+++ b/tests/ui/object-safety/assoc_const_bounds_sized.rs
@@ -1,7 +1,12 @@
+#![feature(generic_const_items)]
+#![allow(incomplete_features, dead_code)]
+
+// check-pass
+
 trait Foo {
     const BAR: bool
-        where //~ ERROR: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
-            Self: Sized;
+    where
+        Self: Sized;
 }
 
 fn foo(_: &dyn Foo) {}
diff --git a/tests/ui/object-safety/assoc_const_bounds_sized.stderr b/tests/ui/object-safety/assoc_const_bounds_sized.stderr
deleted file mode 100644
index e1f57f67795..00000000000
--- a/tests/ui/object-safety/assoc_const_bounds_sized.stderr
+++ /dev/null
@@ -1,15 +0,0 @@
-error: expected one of `!`, `(`, `+`, `::`, `;`, `<`, or `=`, found keyword `where`
-  --> $DIR/assoc_const_bounds_sized.rs:3:9
-   |
-LL | trait Foo {
-   |           - while parsing this item list starting here
-LL |     const BAR: bool
-   |                    - expected one of 7 possible tokens
-LL |         where
-   |         ^^^^^ unexpected token
-LL |             Self: Sized;
-LL | }
-   | - the item list ends here
-
-error: aborting due to previous error
-
diff --git a/tests/ui/parser/generic-statics.rs b/tests/ui/parser/generic-statics.rs
new file mode 100644
index 00000000000..2fb8781fdff
--- /dev/null
+++ b/tests/ui/parser/generic-statics.rs
@@ -0,0 +1,4 @@
+static S<T>: i32 = 0;
+//~^ ERROR static items may not have generic parameters
+
+fn main() {}
diff --git a/tests/ui/parser/generic-statics.stderr b/tests/ui/parser/generic-statics.stderr
new file mode 100644
index 00000000000..c757232b061
--- /dev/null
+++ b/tests/ui/parser/generic-statics.stderr
@@ -0,0 +1,8 @@
+error: static items may not have generic parameters
+  --> $DIR/generic-statics.rs:1:9
+   |
+LL | static S<T>: i32 = 0;
+   |         ^^^
+
+error: aborting due to previous error
+
diff --git a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.default.stderr
index 233f1bc5a86..24e2e0a0f7a 100644
--- a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.stderr
+++ b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.default.stderr
@@ -1,5 +1,5 @@
 error[E0106]: missing lifetime specifier
-  --> $DIR/missing-lifetime-in-assoc-const-type.rs:2:14
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:6:14
    |
 LL |     const A: &str = "";
    |              ^ expected named lifetime parameter
@@ -11,7 +11,7 @@ LL ~     const A: &'a str = "";
    |
 
 error[E0106]: missing lifetime specifier
-  --> $DIR/missing-lifetime-in-assoc-const-type.rs:3:14
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:7:14
    |
 LL |     const B: S = S { s: &() };
    |              ^ expected named lifetime parameter
@@ -24,7 +24,7 @@ LL ~     const B: S<'a> = S { s: &() };
    |
 
 error[E0106]: missing lifetime specifier
-  --> $DIR/missing-lifetime-in-assoc-const-type.rs:4:15
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:8:15
    |
 LL |     const C: &'_ str = "";
    |               ^^ expected named lifetime parameter
@@ -38,7 +38,7 @@ LL ~     const C: &'a str = "";
    |
 
 error[E0106]: missing lifetime specifiers
-  --> $DIR/missing-lifetime-in-assoc-const-type.rs:5:14
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:9:14
    |
 LL |     const D: T = T { a: &(), b: &() };
    |              ^ expected 2 lifetime parameters
diff --git a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.generic_const_items.stderr b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.generic_const_items.stderr
new file mode 100644
index 00000000000..a97ffe7da79
--- /dev/null
+++ b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.generic_const_items.stderr
@@ -0,0 +1,47 @@
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:6:14
+   |
+LL |     const A: &str = "";
+   |              ^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL |     const A<'a>: &'a str = "";
+   |            ++++   ++
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:7:14
+   |
+LL |     const B: S = S { s: &() };
+   |              ^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL |     const B<'a>: S<'a> = S { s: &() };
+   |            ++++   ++++
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:8:15
+   |
+LL |     const C: &'_ str = "";
+   |               ^^ expected named lifetime parameter
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL |     const C<'a>: &'a str = "";
+   |            ++++   ~~
+
+error[E0106]: missing lifetime specifiers
+  --> $DIR/missing-lifetime-in-assoc-const-type.rs:9:14
+   |
+LL |     const D: T = T { a: &(), b: &() };
+   |              ^ expected 2 lifetime parameters
+   |
+help: consider introducing a named lifetime parameter
+   |
+LL |     const D<'a>: T<'a, 'a> = T { a: &(), b: &() };
+   |            ++++   ++++++++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0106`.
diff --git a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs
index 38332627f4c..2a8b4c3c044 100644
--- a/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs
+++ b/tests/ui/suggestions/missing-lifetime-in-assoc-const-type.rs
@@ -1,3 +1,7 @@
+// revisions: default generic_const_items
+
+#![cfg_attr(generic_const_items, feature(generic_const_items), allow(incomplete_features))]
+
 trait ZstAssert: Sized {
     const A: &str = ""; //~ ERROR missing lifetime specifier
     const B: S = S { s: &() }; //~ ERROR missing lifetime specifier