From b02a9688e8d5f7effcf3c1deb624e034fa106353 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Wed, 28 Feb 2024 08:13:29 +0100
Subject: [PATCH 1/3] rustdoc: pass owner item of generics to cleaning
 functions

---
 src/librustdoc/clean/auto_trait.rs   |   2 +
 src/librustdoc/clean/blanket_impl.rs |   1 +
 src/librustdoc/clean/inline.rs       |  30 ++++---
 src/librustdoc/clean/mod.rs          | 121 +++++++++++++++------------
 4 files changed, 91 insertions(+), 63 deletions(-)

diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index fbc2c3c5af459..ae756f2b1fc74 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -106,6 +106,7 @@ where
                     self.cx,
                     tcx.generics_of(item_def_id),
                     ty::GenericPredicates::default(),
+                    item_def_id,
                 );
                 let params = raw_generics.params;
 
@@ -456,6 +457,7 @@ where
             self.cx,
             tcx.generics_of(item_def_id),
             tcx.explicit_predicates_of(item_def_id),
+            item_def_id,
         );
         let mut generic_params = raw_generics.params;
 
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 47cfe651e319d..983d3be2d474c 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -101,6 +101,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                             cx,
                             cx.tcx.generics_of(impl_def_id),
                             cx.tcx.explicit_predicates_of(impl_def_id),
+                            impl_def_id,
                         ),
                         // FIXME(eddyb) compute both `trait_` and `for_` from
                         // the post-inference `trait_ref`, as it's more accurate.
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 03f62f41a26f4..3486a986c88eb 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -270,7 +270,7 @@ pub(crate) fn build_external_trait(cx: &mut DocContext<'_>, did: DefId) -> clean
         .collect();
 
     let predicates = cx.tcx.predicates_of(did);
-    let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
+    let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates, did);
     let generics = filter_non_trait_generics(did, generics);
     let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
     clean::Trait { def_id: did, generics, items: trait_items, bounds: supertrait_bounds }
@@ -282,8 +282,12 @@ pub(crate) fn build_function<'tcx>(
 ) -> Box<clean::Function> {
     let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
     // The generics need to be cleaned before the signature.
-    let mut generics =
-        clean_ty_generics(cx, cx.tcx.generics_of(def_id), cx.tcx.explicit_predicates_of(def_id));
+    let mut generics = clean_ty_generics(
+        cx,
+        cx.tcx.generics_of(def_id),
+        cx.tcx.explicit_predicates_of(def_id),
+        def_id,
+    );
     let bound_vars = clean_bound_vars(sig.bound_vars());
 
     // At the time of writing early & late-bound params are stored separately in rustc,
@@ -315,7 +319,7 @@ fn build_enum(cx: &mut DocContext<'_>, did: DefId) -> clean::Enum {
     let predicates = cx.tcx.explicit_predicates_of(did);
 
     clean::Enum {
-        generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
+        generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates, did),
         variants: cx.tcx.adt_def(did).variants().iter().map(|v| clean_variant_def(v, cx)).collect(),
     }
 }
@@ -326,7 +330,7 @@ fn build_struct(cx: &mut DocContext<'_>, did: DefId) -> clean::Struct {
 
     clean::Struct {
         ctor_kind: variant.ctor_kind(),
-        generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
+        generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates, did),
         fields: variant.fields.iter().map(|x| clean_middle_field(x, cx)).collect(),
     }
 }
@@ -335,7 +339,7 @@ fn build_union(cx: &mut DocContext<'_>, did: DefId) -> clean::Union {
     let predicates = cx.tcx.explicit_predicates_of(did);
     let variant = cx.tcx.adt_def(did).non_enum_variant();
 
-    let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates);
+    let generics = clean_ty_generics(cx, cx.tcx.generics_of(did), predicates, did);
     let fields = variant.fields.iter().map(|x| clean_middle_field(x, cx)).collect();
     clean::Union { generics, fields }
 }
@@ -352,7 +356,7 @@ fn build_type_alias(
 
     Box::new(clean::TypeAlias {
         type_,
-        generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates),
+        generics: clean_ty_generics(cx, cx.tcx.generics_of(did), predicates, did),
         inner_type,
         item_type: None,
     })
@@ -530,7 +534,7 @@ pub(crate) fn build_impl(
                 })
                 .map(|item| clean_impl_item(item, cx))
                 .collect::<Vec<_>>(),
-            clean_generics(impl_.generics, cx),
+            clean_generics(impl_.generics, cx, did),
         ),
         None => (
             tcx.associated_items(did)
@@ -558,7 +562,7 @@ pub(crate) fn build_impl(
                 .map(|item| clean_middle_assoc_item(item, cx))
                 .collect::<Vec<_>>(),
             clean::enter_impl_trait(cx, |cx| {
-                clean_ty_generics(cx, tcx.generics_of(did), predicates)
+                clean_ty_generics(cx, tcx.generics_of(did), predicates, did)
             }),
         ),
     };
@@ -716,8 +720,12 @@ 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));
+    let mut generics = clean_ty_generics(
+        cx,
+        cx.tcx.generics_of(def_id),
+        cx.tcx.explicit_predicates_of(def_id),
+        def_id,
+    );
     clean::simplify::move_bounds_to_generic_parameters(&mut generics);
 
     clean::Constant {
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index b32d3ad562d03..f4110428e5c27 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -631,22 +631,18 @@ fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool {
 }
 
 pub(crate) fn clean_generics<'tcx>(
-    gens: &hir::Generics<'tcx>,
+    generics: &hir::Generics<'tcx>,
     cx: &mut DocContext<'tcx>,
+    _item_def_id: DefId,
 ) -> Generics {
-    let impl_trait_params = gens
+    let impl_trait_params = generics
         .params
         .iter()
         .filter(|param| is_impl_trait(param))
         .map(|param| {
-            let param = clean_generic_param(cx, Some(gens), param);
-            match param.kind {
-                GenericParamDefKind::Lifetime { .. } => unreachable!(),
-                GenericParamDefKind::Type { ref bounds, .. } => {
-                    cx.impl_trait_bounds.insert(param.def_id.into(), bounds.to_vec());
-                }
-                GenericParamDefKind::Const { .. } => unreachable!(),
-            }
+            let param = clean_generic_param(cx, Some(generics), param);
+            let GenericParamDefKind::Type { bounds, .. } = &param.kind else { unreachable!() };
+            cx.impl_trait_bounds.insert(param.def_id.into(), bounds.to_vec());
             param
         })
         .collect::<Vec<_>>();
@@ -654,7 +650,7 @@ pub(crate) fn clean_generics<'tcx>(
     let mut bound_predicates = FxIndexMap::default();
     let mut region_predicates = FxIndexMap::default();
     let mut eq_predicates = ThinVec::default();
-    for pred in gens.predicates.iter().filter_map(|x| clean_where_predicate(x, cx)) {
+    for pred in generics.predicates.iter().filter_map(|x| clean_where_predicate(x, cx)) {
         match pred {
             WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
                 match bound_predicates.entry(ty) {
@@ -697,15 +693,15 @@ pub(crate) fn clean_generics<'tcx>(
         }
     }
 
-    let mut params = ThinVec::with_capacity(gens.params.len());
+    let mut params = ThinVec::with_capacity(generics.params.len());
     // In this loop, we gather the generic parameters (`<'a, B: 'a>`) and check if they have
     // bounds in the where predicates. If so, we move their bounds into the where predicates
     // while also preventing duplicates.
-    for p in gens.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
-        let mut p = clean_generic_param(cx, Some(gens), p);
-        match &mut p.kind {
-            GenericParamDefKind::Lifetime { ref mut outlives } => {
-                if let Some(region_pred) = region_predicates.get_mut(&Lifetime(p.name)) {
+    for param in generics.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
+        let mut param = clean_generic_param(cx, Some(generics), param);
+        match &mut param.kind {
+            GenericParamDefKind::Lifetime { outlives } => {
+                if let Some(region_pred) = region_predicates.get_mut(&Lifetime(param.name)) {
                     // We merge bounds in the `where` clause.
                     for outlive in outlives.drain(..) {
                         let outlive = GenericBound::Outlives(outlive);
@@ -716,7 +712,7 @@ pub(crate) fn clean_generics<'tcx>(
                 }
             }
             GenericParamDefKind::Type { bounds, synthetic: false, .. } => {
-                if let Some(bound_pred) = bound_predicates.get_mut(&Type::Generic(p.name)) {
+                if let Some(bound_pred) = bound_predicates.get_mut(&Type::Generic(param.name)) {
                     // We merge bounds in the `where` clause.
                     for bound in bounds.drain(..) {
                         if !bound_pred.0.contains(&bound) {
@@ -729,7 +725,7 @@ pub(crate) fn clean_generics<'tcx>(
                 // nothing to do here.
             }
         }
-        params.push(p);
+        params.push(param);
     }
     params.extend(impl_trait_params);
 
@@ -754,8 +750,9 @@ pub(crate) fn clean_generics<'tcx>(
 
 fn clean_ty_generics<'tcx>(
     cx: &mut DocContext<'tcx>,
-    gens: &ty::Generics,
-    preds: ty::GenericPredicates<'tcx>,
+    generics: &ty::Generics,
+    predicates: ty::GenericPredicates<'tcx>,
+    _item_def_id: DefId,
 ) -> Generics {
     // Don't populate `cx.impl_trait_bounds` before `clean`ning `where` clauses,
     // since `Clean for ty::Predicate` would consume them.
@@ -764,7 +761,7 @@ fn clean_ty_generics<'tcx>(
     // Bounds in the type_params and lifetimes fields are repeated in the
     // predicates field (see rustc_hir_analysis::collect::ty_generics), so remove
     // them.
-    let stripped_params = gens
+    let stripped_params = generics
         .params
         .iter()
         .filter_map(|param| match param.kind {
@@ -790,7 +787,7 @@ fn clean_ty_generics<'tcx>(
     let mut impl_trait_proj =
         FxHashMap::<u32, Vec<(DefId, PathSegment, ty::Binder<'_, ty::Term<'_>>)>>::default();
 
-    let where_predicates = preds
+    let where_predicates = predicates
         .predicates
         .iter()
         .flat_map(|(pred, _)| {
@@ -1040,7 +1037,13 @@ fn clean_fn_or_proc_macro<'tcx>(
     match macro_kind {
         Some(kind) => clean_proc_macro(item, name, kind, cx),
         None => {
-            let mut func = clean_function(cx, sig, generics, FunctionArgs::Body(body_id));
+            let mut func = clean_function(
+                cx,
+                item.owner_id.to_def_id(),
+                sig,
+                generics,
+                FunctionArgs::Body(body_id),
+            );
             clean_fn_decl_legacy_const_generics(&mut func, attrs);
             FunctionItem(func)
         }
@@ -1087,13 +1090,14 @@ enum FunctionArgs<'tcx> {
 
 fn clean_function<'tcx>(
     cx: &mut DocContext<'tcx>,
+    did: DefId,
     sig: &hir::FnSig<'tcx>,
     generics: &hir::Generics<'tcx>,
     args: FunctionArgs<'tcx>,
 ) -> Box<Function> {
     let (generics, decl) = enter_impl_trait(cx, |cx| {
         // NOTE: generics must be cleaned before args
-        let generics = clean_generics(generics, cx);
+        let generics = clean_generics(generics, cx, did);
         let args = match args {
             FunctionArgs::Body(body_id) => {
                 clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id)
@@ -1232,31 +1236,41 @@ fn clean_poly_trait_ref<'tcx>(
 }
 
 fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
-    let local_did = trait_item.owner_id.to_def_id();
-    cx.with_param_env(local_did, |cx| {
+    let def_id = trait_item.owner_id.to_def_id();
+    cx.with_param_env(def_id, |cx| {
         let inner = match trait_item.kind {
             hir::TraitItemKind::Const(ty, Some(default)) => {
-                let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
+                let generics =
+                    enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx, def_id));
                 AssocConstItem(
                     generics,
                     Box::new(clean_ty(ty, cx)),
-                    ConstantKind::Local { def_id: local_did, body: default },
+                    ConstantKind::Local { def_id, body: default },
                 )
             }
             hir::TraitItemKind::Const(ty, None) => {
-                let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
+                let generics =
+                    enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx, def_id));
                 TyAssocConstItem(generics, Box::new(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));
+                let m =
+                    clean_function(cx, def_id, sig, trait_item.generics, FunctionArgs::Body(body));
                 MethodItem(m, None)
             }
             hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(names)) => {
-                let m = clean_function(cx, sig, trait_item.generics, FunctionArgs::Names(names));
+                let m = clean_function(
+                    cx,
+                    def_id,
+                    sig,
+                    trait_item.generics,
+                    FunctionArgs::Names(names),
+                );
                 TyMethodItem(m)
             }
             hir::TraitItemKind::Type(bounds, Some(default)) => {
-                let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
+                let generics =
+                    enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx, def_id));
                 let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect();
                 let item_type = clean_middle_ty(
                     ty::Binder::dummy(hir_ty_to_ty(cx.tcx, default)),
@@ -1275,12 +1289,13 @@ fn clean_trait_item<'tcx>(trait_item: &hir::TraitItem<'tcx>, cx: &mut DocContext
                 )
             }
             hir::TraitItemKind::Type(bounds, None) => {
-                let generics = enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx));
+                let generics =
+                    enter_impl_trait(cx, |cx| clean_generics(trait_item.generics, cx, def_id));
                 let bounds = bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect();
                 TyAssocTypeItem(generics, bounds)
             }
         };
-        Item::from_def_id_and_parts(local_did, Some(trait_item.ident.name), inner, cx)
+        Item::from_def_id_and_parts(def_id, Some(trait_item.ident.name), inner, cx)
     })
 }
 
@@ -1288,22 +1303,22 @@ pub(crate) fn clean_impl_item<'tcx>(
     impl_: &hir::ImplItem<'tcx>,
     cx: &mut DocContext<'tcx>,
 ) -> Item {
-    let local_did = impl_.owner_id.to_def_id();
-    cx.with_param_env(local_did, |cx| {
+    let def_id = impl_.owner_id.to_def_id();
+    cx.with_param_env(def_id, |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 };
+                let generics = clean_generics(impl_.generics, cx, def_id);
+                let default = ConstantKind::Local { def_id, body: expr };
                 AssocConstItem(generics, Box::new(clean_ty(ty, cx)), default)
             }
             hir::ImplItemKind::Fn(ref sig, body) => {
-                let m = clean_function(cx, sig, impl_.generics, FunctionArgs::Body(body));
+                let m = clean_function(cx, def_id, sig, impl_.generics, FunctionArgs::Body(body));
                 let defaultness = cx.tcx.defaultness(impl_.owner_id);
                 MethodItem(m, Some(defaultness))
             }
             hir::ImplItemKind::Type(hir_ty) => {
                 let type_ = clean_ty(hir_ty, cx);
-                let generics = clean_generics(impl_.generics, cx);
+                let generics = clean_generics(impl_.generics, cx, def_id);
                 let item_type = clean_middle_ty(
                     ty::Binder::dummy(hir_ty_to_ty(cx.tcx, hir_ty)),
                     cx,
@@ -1322,7 +1337,7 @@ pub(crate) fn clean_impl_item<'tcx>(
             }
         };
 
-        Item::from_def_id_and_parts(local_did, Some(impl_.ident.name), inner, cx)
+        Item::from_def_id_and_parts(def_id, Some(impl_.ident.name), inner, cx)
     })
 }
 
@@ -1344,6 +1359,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                 cx,
                 tcx.generics_of(assoc_item.def_id),
                 tcx.explicit_predicates_of(assoc_item.def_id),
+                assoc_item.def_id,
             );
             simplify::move_bounds_to_generic_parameters(&mut generics);
 
@@ -1428,6 +1444,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
                 cx,
                 tcx.generics_of(assoc_item.def_id),
                 ty::GenericPredicates { parent: None, predicates },
+                assoc_item.def_id,
             );
             simplify::move_bounds_to_generic_parameters(&mut generics);
 
@@ -2731,12 +2748,12 @@ fn clean_maybe_renamed_item<'tcx>(
             }
             ItemKind::Const(ty, generics, body_id) => ConstantItem(Constant {
                 type_: Box::new(clean_ty(ty, cx)),
-                generics: clean_generics(generics, cx),
+                generics: clean_generics(generics, cx, def_id),
                 kind: ConstantKind::Local { body: body_id, def_id },
             }),
             ItemKind::OpaqueTy(ref ty) => OpaqueTyItem(OpaqueTy {
                 bounds: ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
-                generics: clean_generics(ty.generics, cx),
+                generics: clean_generics(ty.generics, cx, def_id),
             }),
             ItemKind::TyAlias(hir_ty, generics) => {
                 *cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
@@ -2747,7 +2764,7 @@ fn clean_maybe_renamed_item<'tcx>(
                     None,
                     None,
                 );
-                let generics = clean_generics(generics, cx);
+                let generics = clean_generics(generics, cx, def_id);
                 if let Some(count) = cx.current_type_aliases.get_mut(&def_id) {
                     *count -= 1;
                     if *count == 0 {
@@ -2777,19 +2794,19 @@ fn clean_maybe_renamed_item<'tcx>(
             }
             ItemKind::Enum(ref def, generics) => EnumItem(Enum {
                 variants: def.variants.iter().map(|v| clean_variant(v, cx)).collect(),
-                generics: clean_generics(generics, cx),
+                generics: clean_generics(generics, cx, def_id),
             }),
             ItemKind::TraitAlias(generics, bounds) => TraitAliasItem(TraitAlias {
-                generics: clean_generics(generics, cx),
+                generics: clean_generics(generics, cx, def_id),
                 bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
             }),
             ItemKind::Union(ref variant_data, generics) => UnionItem(Union {
-                generics: clean_generics(generics, cx),
+                generics: clean_generics(generics, cx, def_id),
                 fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
             }),
             ItemKind::Struct(ref variant_data, generics) => StructItem(Struct {
                 ctor_kind: variant_data.ctor_kind(),
-                generics: clean_generics(generics, cx),
+                generics: clean_generics(generics, cx, def_id),
                 fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(),
             }),
             ItemKind::Impl(impl_) => return clean_impl(impl_, item.owner_id.def_id, cx),
@@ -2813,7 +2830,7 @@ fn clean_maybe_renamed_item<'tcx>(
                 TraitItem(Box::new(Trait {
                     def_id,
                     items,
-                    generics: clean_generics(generics, cx),
+                    generics: clean_generics(generics, cx, def_id),
                     bounds: bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect(),
                 }))
             }
@@ -2876,7 +2893,7 @@ fn clean_impl<'tcx>(
     let mut make_item = |trait_: Option<Path>, for_: Type, items: Vec<Item>| {
         let kind = ImplItem(Box::new(Impl {
             unsafety: impl_.unsafety,
-            generics: clean_generics(impl_.generics, cx),
+            generics: clean_generics(impl_.generics, cx, def_id.to_def_id()),
             trait_,
             for_,
             items,
@@ -3086,7 +3103,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
             hir::ForeignItemKind::Fn(decl, names, generics) => {
                 let (generics, decl) = enter_impl_trait(cx, |cx| {
                     // NOTE: generics must be cleaned before args
-                    let generics = clean_generics(generics, cx);
+                    let generics = clean_generics(generics, cx, def_id);
                     let args = clean_args_from_types_and_names(cx, decl.inputs, names);
                     let decl = clean_fn_decl_with_args(cx, decl, None, args);
                     (generics, decl)

From 81217c53d564ca677d6087446c961e36986f616e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Tue, 13 Feb 2024 06:09:13 +0100
Subject: [PATCH 2/3] rustdoc: change memory repr of generic params

In anticipation of adding variance to them.
---
 src/librustdoc/clean/auto_trait.rs | 12 ++--
 src/librustdoc/clean/mod.rs        | 91 +++++++++++++-----------------
 src/librustdoc/clean/simplify.rs   | 16 ++----
 src/librustdoc/clean/types.rs      | 60 ++++++++++++++++----
 src/librustdoc/html/format.rs      | 20 +++----
 src/librustdoc/json/conversions.rs | 52 ++++-------------
 6 files changed, 120 insertions(+), 131 deletions(-)

diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index ae756f2b1fc74..e7c964769a2ed 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -632,19 +632,19 @@ where
         existing_predicates.extend(final_bounds);
 
         for param in generic_params.iter_mut() {
-            match param.kind {
-                GenericParamDefKind::Type { ref mut default, ref mut bounds, .. } => {
+            match &mut param.kind {
+                GenericParamDefKind::Type(ty_param) => {
                     // We never want something like `impl<T=Foo>`.
-                    default.take();
+                    ty_param.default.take();
                     let generic_ty = Type::Generic(param.name);
                     if !has_sized.contains(&generic_ty) {
-                        bounds.insert(0, GenericBound::maybe_sized(self.cx));
+                        ty_param.bounds.insert(0, GenericBound::maybe_sized(self.cx));
                     }
                 }
                 GenericParamDefKind::Lifetime { .. } => {}
-                GenericParamDefKind::Const { ref mut default, .. } => {
+                GenericParamDefKind::Const(ct_param) => {
                     // We never want something like `impl<const N: usize = 10>`
-                    default.take();
+                    ct_param.default.take();
                 }
             }
         }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index f4110428e5c27..75d1e93d925b2 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -507,32 +507,31 @@ fn clean_generic_param_def<'tcx>(
 ) -> GenericParamDef {
     let (name, kind) = match def.kind {
         ty::GenericParamDefKind::Lifetime => {
-            (def.name, GenericParamDefKind::Lifetime { outlives: ThinVec::new() })
+            (def.name, LifetimeParam { outlives: ThinVec::new() }.into())
         }
         ty::GenericParamDefKind::Type { has_default, synthetic, .. } => {
-            let default = if has_default {
-                Some(clean_middle_ty(
+            let default = has_default.then(|| {
+                clean_middle_ty(
                     ty::Binder::dummy(cx.tcx.type_of(def.def_id).instantiate_identity()),
                     cx,
                     Some(def.def_id),
                     None,
-                ))
-            } else {
-                None
-            };
+                )
+            });
             (
                 def.name,
-                GenericParamDefKind::Type {
-                    bounds: ThinVec::new(), // These are filled in from the where-clauses.
-                    default: default.map(Box::new),
+                TypeParam {
+                    bounds: ThinVec::new(), // These are filled in from the where-clause.
+                    default,
                     synthetic,
-                },
+                }
+                .into(),
             )
         }
         ty::GenericParamDefKind::Const { has_default, is_host_effect } => (
             def.name,
-            GenericParamDefKind::Const {
-                ty: Box::new(clean_middle_ty(
+            ConstParam {
+                ty: clean_middle_ty(
                     ty::Binder::dummy(
                         cx.tcx
                             .type_of(def.def_id)
@@ -542,15 +541,13 @@ fn clean_generic_param_def<'tcx>(
                     cx,
                     Some(def.def_id),
                     None,
-                )),
-                default: match has_default {
-                    true => Some(Box::new(
-                        cx.tcx.const_param_default(def.def_id).instantiate_identity().to_string(),
-                    )),
-                    false => None,
-                },
+                ),
+                default: has_default.then(|| {
+                    cx.tcx.const_param_default(def.def_id).instantiate_identity().to_string()
+                }),
                 is_host_effect,
-            },
+            }
+            .into(),
         ),
     };
 
@@ -577,7 +574,7 @@ fn clean_generic_param<'tcx>(
             } else {
                 ThinVec::new()
             };
-            (param.name.ident().name, GenericParamDefKind::Lifetime { outlives })
+            (param.name.ident().name, LifetimeParam { outlives }.into())
         }
         hir::GenericParamKind::Type { ref default, synthetic } => {
             let bounds = if let Some(generics) = generics {
@@ -592,21 +589,18 @@ fn clean_generic_param<'tcx>(
             };
             (
                 param.name.ident().name,
-                GenericParamDefKind::Type {
-                    bounds,
-                    default: default.map(|t| clean_ty(t, cx)).map(Box::new),
-                    synthetic,
-                },
+                TypeParam { bounds, default: default.map(|t| clean_ty(t, cx)), synthetic }.into(),
             )
         }
         hir::GenericParamKind::Const { ty, default, is_host_effect } => (
             param.name.ident().name,
-            GenericParamDefKind::Const {
-                ty: Box::new(clean_ty(ty, cx)),
+            ConstParam {
+                ty: clean_ty(ty, cx),
                 default: default
-                    .map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())),
+                    .map(|ct| ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string()),
                 is_host_effect,
-            },
+            }
+            .into(),
         ),
     };
 
@@ -641,8 +635,8 @@ pub(crate) fn clean_generics<'tcx>(
         .filter(|param| is_impl_trait(param))
         .map(|param| {
             let param = clean_generic_param(cx, Some(generics), param);
-            let GenericParamDefKind::Type { bounds, .. } = &param.kind else { unreachable!() };
-            cx.impl_trait_bounds.insert(param.def_id.into(), bounds.to_vec());
+            let GenericParamDefKind::Type(ty_param) = &param.kind else { unreachable!() };
+            cx.impl_trait_bounds.insert(param.def_id.into(), ty_param.bounds.to_vec());
             param
         })
         .collect::<Vec<_>>();
@@ -700,10 +694,10 @@ pub(crate) fn clean_generics<'tcx>(
     for param in generics.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
         let mut param = clean_generic_param(cx, Some(generics), param);
         match &mut param.kind {
-            GenericParamDefKind::Lifetime { outlives } => {
+            GenericParamDefKind::Lifetime(lt_param) => {
                 if let Some(region_pred) = region_predicates.get_mut(&Lifetime(param.name)) {
                     // We merge bounds in the `where` clause.
-                    for outlive in outlives.drain(..) {
+                    for outlive in lt_param.outlives.drain(..) {
                         let outlive = GenericBound::Outlives(outlive);
                         if !region_pred.contains(&outlive) {
                             region_pred.push(outlive);
@@ -711,10 +705,10 @@ pub(crate) fn clean_generics<'tcx>(
                     }
                 }
             }
-            GenericParamDefKind::Type { bounds, synthetic: false, .. } => {
+            GenericParamDefKind::Type(ty_param) if !ty_param.synthetic => {
                 if let Some(bound_pred) = bound_predicates.get_mut(&Type::Generic(param.name)) {
                     // We merge bounds in the `where` clause.
-                    for bound in bounds.drain(..) {
+                    for bound in ty_param.bounds.drain(..) {
                         if !bound_pred.0.contains(&bound) {
                             bound_pred.0.push(bound);
                         }
@@ -1062,17 +1056,15 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attrib
         for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() {
             match literal.kind {
                 ast::LitKind::Int(a, _) => {
-                    let gen = func.generics.params.remove(0);
+                    let param = func.generics.params.remove(0);
                     if let GenericParamDef {
-                        name,
-                        kind: GenericParamDefKind::Const { ty, .. },
-                        ..
-                    } = gen
+                        name, kind: GenericParamDefKind::Const(param), ..
+                    } = param
                     {
-                        func.decl
-                            .inputs
-                            .values
-                            .insert(a.get() as _, Argument { name, type_: *ty, is_const: true });
+                        func.decl.inputs.values.insert(
+                            a.get() as _,
+                            Argument { name, type_: param.ty, is_const: true },
+                        );
                     } else {
                         panic!("unexpected non const in position {pos}");
                     }
@@ -3160,11 +3152,8 @@ fn clean_bound_vars<'tcx>(
                 Some(GenericParamDef {
                     name,
                     def_id,
-                    kind: GenericParamDefKind::Type {
-                        bounds: ThinVec::new(),
-                        default: None,
-                        synthetic: false,
-                    },
+                    kind: TypeParam { bounds: ThinVec::new(), default: None, synthetic: false }
+                        .into(),
                 })
             }
             // FIXME(non_lifetime_binders): Support higher-ranked const parameters.
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index c35fb9ec78875..910fbbcfaffbf 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -140,20 +140,16 @@ pub(crate) fn move_bounds_to_generic_parameters(generics: &mut clean::Generics)
     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)
+            && let Some(GenericParamDef { kind: GenericParamDefKind::Type(param), .. }) =
+                generics.params.iter_mut().find(|param| &param.name == arg)
         {
-            param_bounds.extend(bounds.drain(..));
+            param.bounds.extend(bounds.drain(..));
         } 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)
+            && let Some(GenericParamDef { kind: GenericParamDefKind::Lifetime(param), .. }) =
+                generics.params.iter_mut().find(|param| &param.name == arg)
         {
-            param_bounds.extend(bounds.drain(..).map(|bound| match bound {
+            param.outlives.extend(bounds.drain(..).map(|bound| match bound {
                 GenericBound::Outlives(lifetime) => lifetime,
                 _ => unreachable!(),
             }));
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index c35baeb4cf587..978e0ddbf7790 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1325,10 +1325,9 @@ impl WherePredicate {
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub(crate) enum GenericParamDefKind {
-    Lifetime { outlives: ThinVec<Lifetime> },
-    Type { bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
-    // Option<Box<String>> makes this type smaller than `Option<String>` would.
-    Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool },
+    Lifetime(Box<LifetimeParam>),
+    Type(Box<TypeParam>),
+    Const(Box<ConstParam>),
 }
 
 impl GenericParamDefKind {
@@ -1337,6 +1336,43 @@ impl GenericParamDefKind {
     }
 }
 
+impl From<LifetimeParam> for GenericParamDefKind {
+    fn from(param: LifetimeParam) -> Self {
+        Self::Lifetime(Box::new(param))
+    }
+}
+
+impl From<TypeParam> for GenericParamDefKind {
+    fn from(param: TypeParam) -> Self {
+        Self::Type(Box::new(param))
+    }
+}
+
+impl From<ConstParam> for GenericParamDefKind {
+    fn from(param: ConstParam) -> Self {
+        Self::Const(Box::new(param))
+    }
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub(crate) struct LifetimeParam {
+    pub(crate) outlives: ThinVec<Lifetime>,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub(crate) struct TypeParam {
+    pub(crate) bounds: ThinVec<GenericBound>,
+    pub(crate) default: Option<Type>,
+    pub(crate) synthetic: bool,
+}
+
+#[derive(Clone, PartialEq, Eq, Debug, Hash)]
+pub(crate) struct ConstParam {
+    pub(crate) ty: Type,
+    pub(crate) default: Option<String>,
+    pub(crate) is_host_effect: bool,
+}
+
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub(crate) struct GenericParamDef {
     pub(crate) name: Symbol,
@@ -1346,14 +1382,14 @@ pub(crate) struct GenericParamDef {
 
 impl GenericParamDef {
     pub(crate) fn lifetime(def_id: DefId, name: Symbol) -> Self {
-        Self { name, def_id, kind: GenericParamDefKind::Lifetime { outlives: ThinVec::new() } }
+        Self { name, def_id, kind: LifetimeParam { outlives: ThinVec::new() }.into() }
     }
 
     pub(crate) fn is_synthetic_param(&self) -> bool {
-        match self.kind {
-            GenericParamDefKind::Lifetime { .. } => false,
-            GenericParamDefKind::Const { is_host_effect, .. } => is_host_effect,
-            GenericParamDefKind::Type { synthetic, .. } => synthetic,
+        match &self.kind {
+            GenericParamDefKind::Lifetime(_) => false,
+            GenericParamDefKind::Const(param) => param.is_host_effect,
+            GenericParamDefKind::Type(param) => param.synthetic,
         }
     }
 
@@ -1362,8 +1398,8 @@ impl GenericParamDef {
     }
 
     pub(crate) fn get_bounds(&self) -> Option<&[GenericBound]> {
-        match self.kind {
-            GenericParamDefKind::Type { ref bounds, .. } => Some(bounds),
+        match &self.kind {
+            GenericParamDefKind::Type(param) => Some(&param.bounds),
             _ => None,
         }
     }
@@ -2561,7 +2597,7 @@ mod size_asserts {
     static_assert_size!(DocFragment, 32);
     static_assert_size!(GenericArg, 32);
     static_assert_size!(GenericArgs, 32);
-    static_assert_size!(GenericParamDef, 40);
+    static_assert_size!(GenericParamDef, 32);
     static_assert_size!(Generics, 16);
     static_assert_size!(Item, 56);
     static_assert_size!(ItemKind, 56);
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index afd5eb42d019d..5629d8a0842ea 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -188,12 +188,12 @@ impl clean::GenericParamDef {
         cx: &'a Context<'tcx>,
     ) -> impl Display + 'a + Captures<'tcx> {
         display_fn(move |f| match &self.kind {
-            clean::GenericParamDefKind::Lifetime { outlives } => {
+            clean::GenericParamDefKind::Lifetime(param) => {
                 write!(f, "{}", self.name)?;
 
-                if !outlives.is_empty() {
+                if !param.outlives.is_empty() {
                     f.write_str(": ")?;
-                    for (i, lt) in outlives.iter().enumerate() {
+                    for (i, lt) in param.outlives.iter().enumerate() {
                         if i != 0 {
                             f.write_str(" + ")?;
                         }
@@ -203,26 +203,26 @@ impl clean::GenericParamDef {
 
                 Ok(())
             }
-            clean::GenericParamDefKind::Type { bounds, default, .. } => {
+            clean::GenericParamDefKind::Type(param) => {
                 f.write_str(self.name.as_str())?;
 
-                if !bounds.is_empty() {
+                if !param.bounds.is_empty() {
                     f.write_str(": ")?;
-                    print_generic_bounds(bounds, cx).fmt(f)?;
+                    print_generic_bounds(&param.bounds, cx).fmt(f)?;
                 }
 
-                if let Some(ref ty) = default {
+                if let Some(ref ty) = param.default {
                     f.write_str(" = ")?;
                     ty.print(cx).fmt(f)?;
                 }
 
                 Ok(())
             }
-            clean::GenericParamDefKind::Const { ty, default, .. } => {
+            clean::GenericParamDefKind::Const(param) => {
                 write!(f, "const {}: ", self.name)?;
-                ty.print(cx).fmt(f)?;
+                param.ty.print(cx).fmt(f)?;
 
-                if let Some(default) = default {
+                if let Some(default) = &param.default {
                     f.write_str(" = ")?;
                     if f.alternate() {
                         write!(f, "{default}")?;
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index cb50a27326fe5..e47b415d39d10 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -453,18 +453,17 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
     fn from_tcx(kind: clean::GenericParamDefKind, tcx: TyCtxt<'_>) -> Self {
         use clean::GenericParamDefKind::*;
         match kind {
-            Lifetime { outlives } => GenericParamDefKind::Lifetime {
-                outlives: outlives.into_iter().map(convert_lifetime).collect(),
+            Lifetime(param) => GenericParamDefKind::Lifetime {
+                outlives: param.outlives.into_iter().map(convert_lifetime).collect(),
             },
-            Type { bounds, default, synthetic } => GenericParamDefKind::Type {
-                bounds: bounds.into_tcx(tcx),
-                default: default.map(|x| (*x).into_tcx(tcx)),
-                synthetic,
-            },
-            Const { ty, default, is_host_effect: _ } => GenericParamDefKind::Const {
-                type_: (*ty).into_tcx(tcx),
-                default: default.map(|x| *x),
+            Type(param) => GenericParamDefKind::Type {
+                bounds: param.bounds.into_tcx(tcx),
+                default: param.default.map(|ty| ty.into_tcx(tcx)),
+                synthetic: param.synthetic,
             },
+            Const(param) => {
+                GenericParamDefKind::Const { type_: param.ty.into_tcx(tcx), default: param.default }
+            }
         }
     }
 }
@@ -476,38 +475,7 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
             BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate {
                 type_: ty.into_tcx(tcx),
                 bounds: bounds.into_tcx(tcx),
-                generic_params: bound_params
-                    .into_iter()
-                    .map(|x| {
-                        let name = x.name.to_string();
-                        let kind = match x.kind {
-                            clean::GenericParamDefKind::Lifetime { outlives } => {
-                                GenericParamDefKind::Lifetime {
-                                    outlives: outlives.iter().map(|lt| lt.0.to_string()).collect(),
-                                }
-                            }
-                            clean::GenericParamDefKind::Type { bounds, default, synthetic } => {
-                                GenericParamDefKind::Type {
-                                    bounds: bounds
-                                        .into_iter()
-                                        .map(|bound| bound.into_tcx(tcx))
-                                        .collect(),
-                                    default: default.map(|ty| (*ty).into_tcx(tcx)),
-                                    synthetic,
-                                }
-                            }
-                            clean::GenericParamDefKind::Const {
-                                ty,
-                                default,
-                                is_host_effect: _,
-                            } => GenericParamDefKind::Const {
-                                type_: (*ty).into_tcx(tcx),
-                                default: default.map(|d| *d),
-                            },
-                        };
-                        GenericParamDef { name, kind }
-                    })
-                    .collect(),
+                generic_params: bound_params.into_iter().map(|param| param.into_tcx(tcx)).collect(),
             },
             RegionPredicate { lifetime, bounds } => WherePredicate::RegionPredicate {
                 lifetime: convert_lifetime(lifetime),

From 48f25fb2f00259041697d9f53efeb5ee29b5d806 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev>
Date: Wed, 28 Feb 2024 08:15:05 +0100
Subject: [PATCH 3/3] [wip] rustdoc: variances

---
 src/librustdoc/clean/auto_trait.rs |   4 +-
 src/librustdoc/clean/mod.rs        | 117 +++++++++++++++++++++++------
 src/librustdoc/clean/types.rs      |   6 +-
 src/librustdoc/html/format.rs      |  19 ++++-
 4 files changed, 119 insertions(+), 27 deletions(-)

diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index e7c964769a2ed..03dbb2338d927 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -106,7 +106,7 @@ where
                     self.cx,
                     tcx.generics_of(item_def_id),
                     ty::GenericPredicates::default(),
-                    item_def_id,
+                    item_def_id, // FIXME: ofc its DefKind is not an impl confusing the variance compution
                 );
                 let params = raw_generics.params;
 
@@ -457,7 +457,7 @@ where
             self.cx,
             tcx.generics_of(item_def_id),
             tcx.explicit_predicates_of(item_def_id),
-            item_def_id,
+            item_def_id, // FIXME: ofc its DefKind is not an impl confusing the variance compution
         );
         let mut generic_params = raw_generics.params;
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 75d1e93d925b2..49acabb6e538f 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -322,7 +322,7 @@ fn clean_where_predicate<'tcx>(
             let bound_params = wbp
                 .bound_generic_params
                 .iter()
-                .map(|param| clean_generic_param(cx, None, param))
+                .map(|param| clean_generic_param(cx, param, None, None))
                 .collect();
             WherePredicate::BoundPredicate {
                 ty: clean_ty(wbp.bounded_ty, cx),
@@ -503,11 +503,12 @@ fn projection_to_path_segment<'tcx>(
 
 fn clean_generic_param_def<'tcx>(
     def: &ty::GenericParamDef,
+    variance: Option<ty::Variance>,
     cx: &mut DocContext<'tcx>,
 ) -> GenericParamDef {
     let (name, kind) = match def.kind {
         ty::GenericParamDefKind::Lifetime => {
-            (def.name, LifetimeParam { outlives: ThinVec::new() }.into())
+            (def.name, LifetimeParam { variance, outlives: ThinVec::new() }.into())
         }
         ty::GenericParamDefKind::Type { has_default, synthetic, .. } => {
             let default = has_default.then(|| {
@@ -521,6 +522,7 @@ fn clean_generic_param_def<'tcx>(
             (
                 def.name,
                 TypeParam {
+                    variance,
                     bounds: ThinVec::new(), // These are filled in from the where-clause.
                     default,
                     synthetic,
@@ -556,8 +558,9 @@ fn clean_generic_param_def<'tcx>(
 
 fn clean_generic_param<'tcx>(
     cx: &mut DocContext<'tcx>,
-    generics: Option<&hir::Generics<'tcx>>,
     param: &hir::GenericParam<'tcx>,
+    variance: Option<ty::Variance>,
+    generics: Option<&hir::Generics<'tcx>>,
 ) -> GenericParamDef {
     let (name, kind) = match param.kind {
         hir::GenericParamKind::Lifetime { .. } => {
@@ -574,7 +577,7 @@ fn clean_generic_param<'tcx>(
             } else {
                 ThinVec::new()
             };
-            (param.name.ident().name, LifetimeParam { outlives }.into())
+            (param.name.ident().name, LifetimeParam { variance, outlives }.into())
         }
         hir::GenericParamKind::Type { ref default, synthetic } => {
             let bounds = if let Some(generics) = generics {
@@ -589,7 +592,13 @@ fn clean_generic_param<'tcx>(
             };
             (
                 param.name.ident().name,
-                TypeParam { bounds, default: default.map(|t| clean_ty(t, cx)), synthetic }.into(),
+                TypeParam {
+                    variance,
+                    bounds,
+                    default: default.map(|t| clean_ty(t, cx)),
+                    synthetic,
+                }
+                .into(),
             )
         }
         hir::GenericParamKind::Const { ty, default, is_host_effect } => (
@@ -627,14 +636,42 @@ fn is_elided_lifetime(param: &hir::GenericParam<'_>) -> bool {
 pub(crate) fn clean_generics<'tcx>(
     generics: &hir::Generics<'tcx>,
     cx: &mut DocContext<'tcx>,
-    _item_def_id: DefId,
+    item_def_id: DefId,
 ) -> Generics {
+    // FIXME(fmease): Instead of querying the defkind, we could instead let the caller make the decision
+    //                NOTE: This would also fix us rendering variances in synthetic impls which use the
+    //                DefId of the ADT.
+    // FIXME(fmease): Add DefKind::TyAlias if type_alias_is_lazy()
+    let def_kind = cx.tcx.def_kind(item_def_id);
+    let variances = if !generics.params.is_empty()
+        && let DefKind::Fn
+        | DefKind::AssocFn
+        | DefKind::Enum
+        | DefKind::Struct
+        | DefKind::Union
+        | DefKind::OpaqueTy = def_kind
+    {
+        let variances = cx.tcx.variances_of(item_def_id);
+        let generics = cx.tcx.generics_of(item_def_id);
+
+        eprintln!("::: item={item_def_id:?}  params={:?}", generics.params);
+        eprintln!("--> variances_raw={variances:?}");
+
+        Some((variances, generics))
+    } else {
+        None
+    };
+
     let impl_trait_params = generics
         .params
         .iter()
-        .filter(|param| is_impl_trait(param))
-        .map(|param| {
-            let param = clean_generic_param(cx, Some(generics), param);
+        .enumerate()
+        .filter(|(_, param)| is_impl_trait(param))
+        .map(|(_index, param)| {
+            // FIXME: this isn't correct for AssocFn since the variances also contain the parent variances, cut them off
+            // let variance = variances.map(|variances| variances[index]); // FIXME(fmease): impl
+            let variance = None;
+            let param = clean_generic_param(cx, param, variance, Some(generics));
             let GenericParamDefKind::Type(ty_param) = &param.kind else { unreachable!() };
             cx.impl_trait_bounds.insert(param.def_id.into(), ty_param.bounds.to_vec());
             param
@@ -692,7 +729,12 @@ pub(crate) fn clean_generics<'tcx>(
     // bounds in the where predicates. If so, we move their bounds into the where predicates
     // while also preventing duplicates.
     for param in generics.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
-        let mut param = clean_generic_param(cx, Some(generics), param);
+        let variance = variances.and_then(|(variances, generics)| {
+            let index = generics.param_def_id_to_index.get(&param.def_id.to_def_id())?;
+            Some(variances[*index as usize])
+        });
+        let mut param = clean_generic_param(cx, param, variance, Some(generics));
+
         match &mut param.kind {
             GenericParamDefKind::Lifetime(lt_param) => {
                 if let Some(region_pred) = region_predicates.get_mut(&Lifetime(param.name)) {
@@ -746,8 +788,25 @@ fn clean_ty_generics<'tcx>(
     cx: &mut DocContext<'tcx>,
     generics: &ty::Generics,
     predicates: ty::GenericPredicates<'tcx>,
-    _item_def_id: DefId,
+    item_def_id: DefId,
 ) -> Generics {
+    // FIXME(fmease): Instead of querying the defkind, we could instead let the caller make the decision
+    //                NOTE: This would also fix us rendering variances in synthetic impls which use the
+    //                DefId of the ADT.
+    // FIXME(fmease): Add DefKind::TyAlias if type_alias_is_lazy()
+    let variances = if !generics.params.is_empty()
+        && let DefKind::Fn
+        | DefKind::AssocFn
+        | DefKind::Enum
+        | DefKind::Struct
+        | DefKind::Union
+        | DefKind::OpaqueTy = cx.tcx.def_kind(item_def_id)
+    {
+        Some(cx.tcx.variances_of(item_def_id))
+    } else {
+        None
+    };
+
     // Don't populate `cx.impl_trait_bounds` before `clean`ning `where` clauses,
     // since `Clean for ty::Predicate` would consume them.
     let mut impl_trait = BTreeMap::<ImplTraitParam, Vec<GenericBound>>::default();
@@ -758,22 +817,28 @@ fn clean_ty_generics<'tcx>(
     let stripped_params = generics
         .params
         .iter()
-        .filter_map(|param| match param.kind {
-            ty::GenericParamDefKind::Lifetime if param.is_anonymous_lifetime() => None,
-            ty::GenericParamDefKind::Lifetime => Some(clean_generic_param_def(param, cx)),
+        .filter(|param| match param.kind {
+            ty::GenericParamDefKind::Lifetime => !param.is_anonymous_lifetime(),
             ty::GenericParamDefKind::Type { synthetic, .. } => {
                 if param.name == kw::SelfUpper {
                     assert_eq!(param.index, 0);
-                    return None;
+                    return false;
                 }
                 if synthetic {
                     impl_trait.insert(param.index.into(), vec![]);
-                    return None;
+                    return false;
                 }
-                Some(clean_generic_param_def(param, cx))
+                true
             }
-            ty::GenericParamDefKind::Const { is_host_effect: true, .. } => None,
-            ty::GenericParamDefKind::Const { .. } => Some(clean_generic_param_def(param, cx)),
+            ty::GenericParamDefKind::Const { is_host_effect, .. } => !is_host_effect,
+        })
+        .map(|param| {
+            // FIXME(fmease): We will probably handle AssocFn incorrectly since it has
+            // parent generics (we prob need to offset shit)
+            let variance = variances
+                .map(|variances| variances[generics.param_def_id_to_index[&param.def_id] as usize]);
+
+            clean_generic_param_def(param, variance, cx)
         })
         .collect::<ThinVec<GenericParamDef>>();
 
@@ -1222,7 +1287,7 @@ fn clean_poly_trait_ref<'tcx>(
             .bound_generic_params
             .iter()
             .filter(|p| !is_elided_lifetime(p))
-            .map(|x| clean_generic_param(cx, None, x))
+            .map(|x| clean_generic_param(cx, x, None, None))
             .collect(),
     }
 }
@@ -2566,7 +2631,7 @@ fn clean_bare_fn_ty<'tcx>(
             .generic_params
             .iter()
             .filter(|p| !is_elided_lifetime(p))
-            .map(|x| clean_generic_param(cx, None, x))
+            .map(|x| clean_generic_param(cx, x, None, None)) // FIXME(fmease): variance?
             .collect();
         let args = clean_args_from_types_and_names(cx, bare_fn.decl.inputs, bare_fn.param_names);
         let decl = clean_fn_decl_with_args(cx, bare_fn.decl, None, args);
@@ -3152,8 +3217,14 @@ fn clean_bound_vars<'tcx>(
                 Some(GenericParamDef {
                     name,
                     def_id,
-                    kind: TypeParam { bounds: ThinVec::new(), default: None, synthetic: false }
-                        .into(),
+                    // FIXME(fmease): higher-ranked generic params don't have a variance, right?
+                    kind: TypeParam {
+                        variance: None,
+                        bounds: ThinVec::new(),
+                        default: None,
+                        synthetic: false,
+                    }
+                    .into(),
                 })
             }
             // FIXME(non_lifetime_binders): Support higher-ranked const parameters.
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 978e0ddbf7790..2c324b6391eea 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1356,11 +1356,13 @@ impl From<ConstParam> for GenericParamDefKind {
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub(crate) struct LifetimeParam {
+    pub(crate) variance: Option<ty::Variance>,
     pub(crate) outlives: ThinVec<Lifetime>,
 }
 
 #[derive(Clone, PartialEq, Eq, Debug, Hash)]
 pub(crate) struct TypeParam {
+    pub(crate) variance: Option<ty::Variance>,
     pub(crate) bounds: ThinVec<GenericBound>,
     pub(crate) default: Option<Type>,
     pub(crate) synthetic: bool,
@@ -1382,7 +1384,9 @@ pub(crate) struct GenericParamDef {
 
 impl GenericParamDef {
     pub(crate) fn lifetime(def_id: DefId, name: Symbol) -> Self {
-        Self { name, def_id, kind: LifetimeParam { outlives: ThinVec::new() }.into() }
+        // FIXME(fmease): Don't use a fixed variance.
+        let param = LifetimeParam { variance: None, outlives: ThinVec::new() };
+        Self { name, def_id, kind: param.into() }
     }
 
     pub(crate) fn is_synthetic_param(&self) -> bool {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 5629d8a0842ea..52cbe10b5958f 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -189,7 +189,10 @@ impl clean::GenericParamDef {
     ) -> impl Display + 'a + Captures<'tcx> {
         display_fn(move |f| match &self.kind {
             clean::GenericParamDefKind::Lifetime(param) => {
-                write!(f, "{}", self.name)?;
+                if let Some(variance) = param.variance {
+                    f.write_str(print_variance(variance))?;
+                }
+                f.write_str(self.name.as_str())?;
 
                 if !param.outlives.is_empty() {
                     f.write_str(": ")?;
@@ -204,6 +207,9 @@ impl clean::GenericParamDef {
                 Ok(())
             }
             clean::GenericParamDefKind::Type(param) => {
+                if let Some(variance) = param.variance {
+                    f.write_str(print_variance(variance))?;
+                }
                 f.write_str(self.name.as_str())?;
 
                 if !param.bounds.is_empty() {
@@ -1749,6 +1755,17 @@ impl clean::Term {
     }
 }
 
+// FIXME(fmease): Do we need to care about alternate?
+fn print_variance(variance: ty::Variance) -> &'static str {
+    match variance {
+        // ty::Variance::Covariant => "",
+        ty::Variance::Covariant => "<sub class=\"variance\">+</sub>", // FIXME: temporary
+        ty::Variance::Invariant => r#"<sub class="variance" title="invariant">∘</sub>"#,
+        ty::Variance::Contravariant => r#"<sub class="variance" title="contravariant">−</sub>"#,
+        ty::Variance::Bivariant => r#"<sub class="variance" title="bivariant">∗</sub>"#,
+    }
+}
+
 pub(crate) fn display_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl Display {
     struct WithFormatter<F>(Cell<Option<F>>);