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 a26eb222c18..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,9 +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) })
             }
-            // FIXME(fmease): rustdoc integration
-            ItemKind::Const(ty, _generics, 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 {