diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index 9c2cf58efed4a..54858b52008f9 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -1,11 +1,5 @@
 use super::*;
-use std::{
-    borrow::Borrow,
-    cmp,
-    fmt::Debug,
-    iter,
-    ops::{Bound, Deref},
-};
+use std::{borrow::Borrow, cmp, iter, ops::Bound};
 
 #[cfg(feature = "randomize")]
 use rand::{seq::SliceRandom, SeedableRng};
@@ -33,7 +27,7 @@ pub trait LayoutCalculator {
     fn delay_bug(&self, txt: &str);
     fn current_data_layout(&self) -> Self::TargetDataLayoutRef;
 
-    fn scalar_pair<V: Idx>(&self, a: Scalar, b: Scalar) -> LayoutS<V> {
+    fn scalar_pair(&self, a: Scalar, b: Scalar) -> LayoutS {
         let dl = self.current_data_layout();
         let dl = dl.borrow();
         let b_align = b.align(dl);
@@ -49,7 +43,7 @@ pub trait LayoutCalculator {
             .max_by_key(|niche| niche.available(dl));
 
         LayoutS {
-            variants: Variants::Single { index: V::new(0) },
+            variants: Variants::Single { index: VariantIdx::new(0) },
             fields: FieldsShape::Arbitrary {
                 offsets: vec![Size::ZERO, b_offset],
                 memory_index: vec![0, 1],
@@ -61,13 +55,13 @@ pub trait LayoutCalculator {
         }
     }
 
-    fn univariant<'a, V: Idx, F: Deref<Target = &'a LayoutS<V>> + Debug>(
+    fn univariant(
         &self,
         dl: &TargetDataLayout,
-        fields: &[F],
+        fields: &[Layout<'_>],
         repr: &ReprOptions,
         kind: StructKind,
-    ) -> Option<LayoutS<V>> {
+    ) -> Option<LayoutS> {
         let pack = repr.pack;
         let mut align = if pack.is_some() { dl.i8_align } else { dl.aggregate_align };
         let mut inverse_memory_index: Vec<u32> = (0..fields.len() as u32).collect();
@@ -76,17 +70,17 @@ pub trait LayoutCalculator {
             let end =
                 if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() };
             let optimizing = &mut inverse_memory_index[..end];
-            let effective_field_align = |f: &F| {
+            let effective_field_align = |layout: Layout<'_>| {
                 if let Some(pack) = pack {
                     // return the packed alignment in bytes
-                    f.align.abi.min(pack).bytes()
+                    layout.align().abi.min(pack).bytes()
                 } else {
                     // returns log2(effective-align).
                     // This is ok since `pack` applies to all fields equally.
                     // The calculation assumes that size is an integer multiple of align, except for ZSTs.
                     //
                     // group [u8; 4] with align-4 or [u8; 6] with align-2 fields
-                    f.align.abi.bytes().max(f.size.bytes()).trailing_zeros() as u64
+                    layout.align().abi.bytes().max(layout.size().bytes()).trailing_zeros() as u64
                 }
             };
 
@@ -111,9 +105,9 @@ pub trait LayoutCalculator {
                             // Place ZSTs first to avoid "interesting offsets",
                             // especially with only one or two non-ZST fields.
                             // Then place largest alignments first, largest niches within an alignment group last
-                            let f = &fields[x as usize];
-                            let niche_size = f.largest_niche.map_or(0, |n| n.available(dl));
-                            (!f.is_zst(), cmp::Reverse(effective_field_align(f)), niche_size)
+                            let f = fields[x as usize];
+                            let niche_size = f.largest_niche().map_or(0, |n| n.available(dl));
+                            (!f.0.is_zst(), cmp::Reverse(effective_field_align(f)), niche_size)
                         });
                     }
 
@@ -123,8 +117,8 @@ pub trait LayoutCalculator {
                         // And put the largest niche in an alignment group at the end
                         // so it can be used as discriminant in jagged enums
                         optimizing.sort_by_key(|&x| {
-                            let f = &fields[x as usize];
-                            let niche_size = f.largest_niche.map_or(0, |n| n.available(dl));
+                            let f = fields[x as usize];
+                            let niche_size = f.largest_niche().map_or(0, |n| n.available(dl));
                             (effective_field_align(f), niche_size)
                         });
                     }
@@ -160,15 +154,15 @@ pub trait LayoutCalculator {
                 ));
             }
 
-            if field.is_unsized() {
+            if field.0.is_unsized() {
                 sized = false;
             }
 
             // Invariant: offset < dl.obj_size_bound() <= 1<<61
             let field_align = if let Some(pack) = pack {
-                field.align.min(AbiAndPrefAlign::new(pack))
+                field.align().min(AbiAndPrefAlign::new(pack))
             } else {
-                field.align
+                field.align()
             };
             offset = offset.align_to(field_align.abi);
             align = align.max(field_align);
@@ -176,7 +170,7 @@ pub trait LayoutCalculator {
             debug!("univariant offset: {:?} field: {:#?}", offset, field);
             offsets[i as usize] = offset;
 
-            if let Some(mut niche) = field.largest_niche {
+            if let Some(mut niche) = field.largest_niche() {
                 let available = niche.available(dl);
                 if available > largest_niche_available {
                     largest_niche_available = available;
@@ -185,7 +179,7 @@ pub trait LayoutCalculator {
                 }
             }
 
-            offset = offset.checked_add(field.size, dl)?;
+            offset = offset.checked_add(field.size(), dl)?;
         }
         if let Some(repr_align) = repr.align {
             align = align.max(AbiAndPrefAlign::new(repr_align));
@@ -205,24 +199,26 @@ pub trait LayoutCalculator {
         // Unpack newtype ABIs and find scalar pairs.
         if sized && size.bytes() > 0 {
             // All other fields must be ZSTs.
-            let mut non_zst_fields = fields.iter().enumerate().filter(|&(_, f)| !f.is_zst());
+            let mut non_zst_fields = fields.iter().enumerate().filter(|&(_, f)| !f.0.is_zst());
 
             match (non_zst_fields.next(), non_zst_fields.next(), non_zst_fields.next()) {
                 // We have exactly one non-ZST field.
                 (Some((i, field)), None, None) => {
                     // Field fills the struct and it has a scalar or scalar pair ABI.
-                    if offsets[i].bytes() == 0 && align.abi == field.align.abi && size == field.size
+                    if offsets[i].bytes() == 0
+                        && align.abi == field.align().abi
+                        && size == field.size()
                     {
-                        match field.abi {
+                        match field.abi() {
                             // For plain scalars, or vectors of them, we can't unpack
                             // newtypes for `#[repr(C)]`, as that affects C ABIs.
                             Abi::Scalar(_) | Abi::Vector { .. } if optimize => {
-                                abi = field.abi;
+                                abi = field.abi();
                             }
                             // But scalar pairs are Rust-specific and get
                             // treated as aggregates by C ABIs anyway.
                             Abi::ScalarPair(..) => {
-                                abi = field.abi;
+                                abi = field.abi();
                             }
                             _ => {}
                         }
@@ -231,7 +227,7 @@ pub trait LayoutCalculator {
 
                 // Two non-ZST fields, and they're both scalars.
                 (Some((i, a)), Some((j, b)), None) => {
-                    match (a.abi, b.abi) {
+                    match (a.abi(), b.abi()) {
                         (Abi::Scalar(a), Abi::Scalar(b)) => {
                             // Order by the memory placement, not source order.
                             let ((i, a), (j, b)) = if offsets[i] < offsets[j] {
@@ -239,7 +235,7 @@ pub trait LayoutCalculator {
                             } else {
                                 ((j, b), (i, a))
                             };
-                            let pair = self.scalar_pair::<V>(a, b);
+                            let pair = self.scalar_pair(a, b);
                             let pair_offsets = match pair.fields {
                                 FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
                                     assert_eq!(memory_index, &[0, 1]);
@@ -264,11 +260,11 @@ pub trait LayoutCalculator {
                 _ => {}
             }
         }
-        if fields.iter().any(|f| f.abi.is_uninhabited()) {
+        if fields.iter().any(|f| f.abi().is_uninhabited()) {
             abi = Abi::Uninhabited;
         }
         Some(LayoutS {
-            variants: Variants::Single { index: V::new(0) },
+            variants: Variants::Single { index: VariantIdx::new(0) },
             fields: FieldsShape::Arbitrary { offsets, memory_index },
             abi,
             largest_niche,
@@ -277,11 +273,11 @@ pub trait LayoutCalculator {
         })
     }
 
-    fn layout_of_never_type<V: Idx>(&self) -> LayoutS<V> {
+    fn layout_of_never_type(&self) -> LayoutS {
         let dl = self.current_data_layout();
         let dl = dl.borrow();
         LayoutS {
-            variants: Variants::Single { index: V::new(0) },
+            variants: Variants::Single { index: VariantIdx::new(0) },
             fields: FieldsShape::Primitive,
             abi: Abi::Uninhabited,
             largest_niche: None,
@@ -290,18 +286,18 @@ pub trait LayoutCalculator {
         }
     }
 
-    fn layout_of_struct_or_enum<'a, V: Idx, F: Deref<Target = &'a LayoutS<V>> + Debug>(
+    fn layout_of_struct_or_enum(
         &self,
         repr: &ReprOptions,
-        variants: &IndexVec<V, Vec<F>>,
+        variants: &IndexVec<VariantIdx, Vec<Layout<'_>>>,
         is_enum: bool,
         is_unsafe_cell: bool,
         scalar_valid_range: (Bound<u128>, Bound<u128>),
         discr_range_of_repr: impl Fn(i128, i128) -> (Integer, bool),
-        discriminants: impl Iterator<Item = (V, i128)>,
+        discriminants: impl Iterator<Item = (VariantIdx, i128)>,
         niche_optimize_enum: bool,
         always_sized: bool,
-    ) -> Option<LayoutS<V>> {
+    ) -> Option<LayoutS> {
         let dl = self.current_data_layout();
         let dl = dl.borrow();
 
@@ -316,9 +312,9 @@ pub trait LayoutCalculator {
         // but *not* an encoding of the discriminant (e.g., a tag value).
         // See issue #49298 for more details on the need to leave space
         // for non-ZST uninhabited data (mostly partial initialization).
-        let absent = |fields: &[F]| {
-            let uninhabited = fields.iter().any(|f| f.abi.is_uninhabited());
-            let is_zst = fields.iter().all(|f| f.is_zst());
+        let absent = |fields: &[Layout<'_>]| {
+            let uninhabited = fields.iter().any(|f| f.abi().is_uninhabited());
+            let is_zst = fields.iter().all(|f| f.0.is_zst());
             uninhabited && is_zst
         };
         let (present_first, present_second) = {
@@ -335,7 +331,7 @@ pub trait LayoutCalculator {
             }
             // If it's a struct, still compute a layout so that we can still compute the
             // field offsets.
-            None => V::new(0),
+            None => VariantIdx::new(0),
         };
 
         let is_struct = !is_enum ||
@@ -439,12 +435,12 @@ pub trait LayoutCalculator {
         // variant layouts, so we can't store them in the
         // overall LayoutS. Store the overall LayoutS
         // and the variant LayoutSs here until then.
-        struct TmpLayout<V: Idx> {
-            layout: LayoutS<V>,
-            variants: IndexVec<V, LayoutS<V>>,
+        struct TmpLayout {
+            layout: LayoutS,
+            variants: IndexVec<VariantIdx, LayoutS>,
         }
 
-        let calculate_niche_filling_layout = || -> Option<TmpLayout<V>> {
+        let calculate_niche_filling_layout = || -> Option<TmpLayout> {
             if niche_optimize_enum {
                 return None;
             }
@@ -464,15 +460,16 @@ pub trait LayoutCalculator {
 
                     Some(st)
                 })
-                .collect::<Option<IndexVec<V, _>>>()?;
+                .collect::<Option<IndexVec<VariantIdx, _>>>()?;
 
             let largest_variant_index = variant_layouts
                 .iter_enumerated()
                 .max_by_key(|(_i, layout)| layout.size.bytes())
                 .map(|(i, _layout)| i)?;
 
-            let all_indices = (0..=variants.len() - 1).map(V::new);
-            let needs_disc = |index: V| index != largest_variant_index && !absent(&variants[index]);
+            let all_indices = (0..=variants.len() - 1).map(VariantIdx::new);
+            let needs_disc =
+                |index: VariantIdx| index != largest_variant_index && !absent(&variants[index]);
             let niche_variants = all_indices.clone().find(|v| needs_disc(*v)).unwrap().index()
                 ..=all_indices.rev().find(|v| needs_disc(*v)).unwrap().index();
 
@@ -482,7 +479,7 @@ pub trait LayoutCalculator {
             let (field_index, niche, (niche_start, niche_scalar)) = variants[largest_variant_index]
                 .iter()
                 .enumerate()
-                .filter_map(|(j, field)| Some((j, field.largest_niche?)))
+                .filter_map(|(j, field)| Some((j, field.largest_niche()?)))
                 .max_by_key(|(_, niche)| niche.available(dl))
                 .and_then(|(j, niche)| Some((j, niche, niche.reserve(dl, count)?)))?;
             let niche_offset =
@@ -514,7 +511,7 @@ pub trait LayoutCalculator {
                 match layout.fields {
                     FieldsShape::Arbitrary { ref mut offsets, .. } => {
                         for (j, offset) in offsets.iter_mut().enumerate() {
-                            if !variants[i][j].is_zst() {
+                            if !variants[i][j].0.is_zst() {
                                 *offset += this_offset;
                             }
                         }
@@ -572,8 +569,8 @@ pub trait LayoutCalculator {
                     tag: niche_scalar,
                     tag_encoding: TagEncoding::Niche {
                         untagged_variant: largest_variant_index,
-                        niche_variants: (V::new(*niche_variants.start())
-                            ..=V::new(*niche_variants.end())),
+                        niche_variants: (VariantIdx::new(*niche_variants.start())
+                            ..=VariantIdx::new(*niche_variants.end())),
                         niche_start,
                     },
                     tag_field: 0,
@@ -598,7 +595,7 @@ pub trait LayoutCalculator {
         let discr_type = repr.discr_type();
         let bits = Integer::from_attr(dl, discr_type).size().bits();
         for (i, mut val) in discriminants {
-            if variants[i].iter().any(|f| f.abi.is_uninhabited()) {
+            if variants[i].iter().any(|f| f.abi().is_uninhabited()) {
                 continue;
             }
             if discr_type.is_signed() {
@@ -636,7 +633,7 @@ pub trait LayoutCalculator {
         if repr.c() {
             for fields in variants {
                 for field in fields {
-                    prefix_align = prefix_align.max(field.align.abi);
+                    prefix_align = prefix_align.max(field.align().abi);
                 }
             }
         }
@@ -655,8 +652,8 @@ pub trait LayoutCalculator {
                 // Find the first field we can't move later
                 // to make room for a larger discriminant.
                 for field in st.fields.index_by_increasing_offset().map(|j| &field_layouts[j]) {
-                    if !field.is_zst() || field.align.abi.bytes() != 1 {
-                        start_align = start_align.min(field.align.abi);
+                    if !field.0.is_zst() || field.align().abi.bytes() != 1 {
+                        start_align = start_align.min(field.align().abi);
                         break;
                     }
                 }
@@ -664,7 +661,7 @@ pub trait LayoutCalculator {
                 align = align.max(st.align);
                 Some(st)
             })
-            .collect::<Option<IndexVec<V, _>>>()?;
+            .collect::<Option<IndexVec<VariantIdx, _>>>()?;
 
         // Align the maximum variant size to the largest alignment.
         size = size.align_to(align.abi);
@@ -759,7 +756,7 @@ pub trait LayoutCalculator {
                 let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
                     panic!();
                 };
-                let mut fields = iter::zip(field_layouts, offsets).filter(|p| !p.0.is_zst());
+                let mut fields = iter::zip(field_layouts, offsets).filter(|p| !p.0.0.is_zst());
                 let (field, offset) = match (fields.next(), fields.next()) {
                     (None, None) => {
                         common_prim_initialized_in_all_variants = false;
@@ -771,7 +768,7 @@ pub trait LayoutCalculator {
                         break;
                     }
                 };
-                let prim = match field.abi {
+                let prim = match field.abi() {
                     Abi::Scalar(scalar) => {
                         common_prim_initialized_in_all_variants &=
                             matches!(scalar, Scalar::Initialized { .. });
@@ -802,7 +799,7 @@ pub trait LayoutCalculator {
                     // Common prim might be uninit.
                     Scalar::Union { value: prim }
                 };
-                let pair = self.scalar_pair::<V>(tag, prim_scalar);
+                let pair = self.scalar_pair(tag, prim_scalar);
                 let pair_offsets = match pair.fields {
                     FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
                         assert_eq!(memory_index, &[0, 1]);
@@ -862,9 +859,8 @@ pub trait LayoutCalculator {
                 // pick the layout with the larger niche; otherwise,
                 // pick tagged as it has simpler codegen.
                 use cmp::Ordering::*;
-                let niche_size = |tmp_l: &TmpLayout<V>| {
-                    tmp_l.layout.largest_niche.map_or(0, |n| n.available(dl))
-                };
+                let niche_size =
+                    |tmp_l: &TmpLayout| tmp_l.layout.largest_niche.map_or(0, |n| n.available(dl));
                 match (tl.layout.size.cmp(&nl.layout.size), niche_size(&tl).cmp(&niche_size(&nl))) {
                     (Greater, _) => nl,
                     (Equal, Less) => nl,
@@ -884,11 +880,11 @@ pub trait LayoutCalculator {
         Some(best_layout.layout)
     }
 
-    fn layout_of_union<'a, V: Idx, F: Deref<Target = &'a LayoutS<V>> + Debug>(
+    fn layout_of_union(
         &self,
         repr: &ReprOptions,
-        variants: &IndexVec<V, Vec<F>>,
-    ) -> Option<LayoutS<V>> {
+        variants: &IndexVec<VariantIdx, Vec<Layout<'_>>>,
+    ) -> Option<LayoutS> {
         let dl = self.current_data_layout();
         let dl = dl.borrow();
         let mut align = if repr.pack.is_some() { dl.i8_align } else { dl.aggregate_align };
@@ -900,15 +896,15 @@ pub trait LayoutCalculator {
         let optimize = !repr.inhibit_union_abi_opt();
         let mut size = Size::ZERO;
         let mut abi = Abi::Aggregate { sized: true };
-        let index = V::new(0);
+        let index = VariantIdx::new(0);
         for field in &variants[index] {
-            assert!(field.is_sized());
-            align = align.max(field.align);
+            assert!(field.0.is_sized());
+            align = align.max(field.align());
 
             // If all non-ZST fields have the same ABI, forward this ABI
-            if optimize && !field.is_zst() {
+            if optimize && !field.0.is_zst() {
                 // Discard valid range information and allow undef
-                let field_abi = match field.abi {
+                let field_abi = match field.abi() {
                     Abi::Scalar(x) => Abi::Scalar(x.to_union()),
                     Abi::ScalarPair(x, y) => Abi::ScalarPair(x.to_union(), y.to_union()),
                     Abi::Vector { element: x, count } => {
@@ -926,7 +922,7 @@ pub trait LayoutCalculator {
                 }
             }
 
-            size = cmp::max(size, field.size);
+            size = cmp::max(size, field.size());
         }
 
         if let Some(pack) = repr.pack {
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 0306cb5ce6abd..c88a60c62b9df 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -8,6 +8,7 @@ use std::ops::{Add, AddAssign, Mul, RangeInclusive, Sub};
 use std::str::FromStr;
 
 use bitflags::bitflags;
+use rustc_data_structures::intern::Interned;
 #[cfg(feature = "nightly")]
 use rustc_data_structures::stable_hasher::StableOrd;
 use rustc_index::vec::{Idx, IndexVec};
@@ -1250,9 +1251,9 @@ impl Abi {
 
 #[derive(PartialEq, Eq, Hash, Clone, Debug)]
 #[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
-pub enum Variants<V: Idx> {
+pub enum Variants {
     /// Single enum variants, structs/tuples, unions, and all non-ADTs.
-    Single { index: V },
+    Single { index: VariantIdx },
 
     /// Enum-likes with more than one inhabited variant: each variant comes with
     /// a *discriminant* (usually the same as the variant index but the user can
@@ -1262,15 +1263,15 @@ pub enum Variants<V: Idx> {
     /// For enums, the tag is the sole field of the layout.
     Multiple {
         tag: Scalar,
-        tag_encoding: TagEncoding<V>,
+        tag_encoding: TagEncoding,
         tag_field: usize,
-        variants: IndexVec<V, LayoutS<V>>,
+        variants: IndexVec<VariantIdx, LayoutS>,
     },
 }
 
 #[derive(PartialEq, Eq, Hash, Clone, Debug)]
 #[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
-pub enum TagEncoding<V: Idx> {
+pub enum TagEncoding {
     /// The tag directly stores the discriminant, but possibly with a smaller layout
     /// (so converting the tag to the discriminant can require sign extension).
     Direct,
@@ -1285,7 +1286,11 @@ pub enum TagEncoding<V: Idx> {
     /// For example, `Option<(usize, &T)>`  is represented such that
     /// `None` has a null pointer for the second tuple field, and
     /// `Some` is the identity function (with a non-null reference).
-    Niche { untagged_variant: V, niche_variants: RangeInclusive<V>, niche_start: u128 },
+    Niche {
+        untagged_variant: VariantIdx,
+        niche_variants: RangeInclusive<VariantIdx>,
+        niche_start: u128,
+    },
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
@@ -1372,9 +1377,14 @@ impl Niche {
     }
 }
 
+rustc_index::newtype_index! {
+    #[derive(HashStable_Generic)]
+    pub struct VariantIdx {}
+}
+
 #[derive(PartialEq, Eq, Hash, Clone)]
 #[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
-pub struct LayoutS<V: Idx> {
+pub struct LayoutS {
     /// Says where the fields are located within the layout.
     pub fields: FieldsShape,
 
@@ -1385,7 +1395,7 @@ pub struct LayoutS<V: Idx> {
     ///
     /// To access all fields of this layout, both `fields` and the fields of the active variant
     /// must be taken into account.
-    pub variants: Variants<V>,
+    pub variants: Variants,
 
     /// The `abi` defines how this data is passed between functions, and it defines
     /// value restrictions via `valid_range`.
@@ -1404,13 +1414,13 @@ pub struct LayoutS<V: Idx> {
     pub size: Size,
 }
 
-impl<V: Idx> LayoutS<V> {
+impl LayoutS {
     pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
         let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
         let size = scalar.size(cx);
         let align = scalar.align(cx);
         LayoutS {
-            variants: Variants::Single { index: V::new(0) },
+            variants: Variants::Single { index: VariantIdx::new(0) },
             fields: FieldsShape::Primitive,
             abi: Abi::Scalar(scalar),
             largest_niche,
@@ -1420,7 +1430,7 @@ impl<V: Idx> LayoutS<V> {
     }
 }
 
-impl<V: Idx> fmt::Debug for LayoutS<V> {
+impl fmt::Debug for LayoutS {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // This is how `Layout` used to print before it become
         // `Interned<LayoutS>`. We print it like this to avoid having to update
@@ -1437,6 +1447,43 @@ impl<V: Idx> fmt::Debug for LayoutS<V> {
     }
 }
 
+#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
+#[rustc_pass_by_value]
+pub struct Layout<'a>(pub Interned<'a, LayoutS>);
+
+impl<'a> fmt::Debug for Layout<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // See comment on `<LayoutS as Debug>::fmt` above.
+        self.0.0.fmt(f)
+    }
+}
+
+impl<'a> Layout<'a> {
+    pub fn fields(self) -> &'a FieldsShape {
+        &self.0.0.fields
+    }
+
+    pub fn variants(self) -> &'a Variants {
+        &self.0.0.variants
+    }
+
+    pub fn abi(self) -> Abi {
+        self.0.0.abi
+    }
+
+    pub fn largest_niche(self) -> Option<Niche> {
+        self.0.0.largest_niche
+    }
+
+    pub fn align(self) -> AbiAndPrefAlign {
+        self.0.0.align
+    }
+
+    pub fn size(self) -> Size {
+        self.0.0.size
+    }
+}
+
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum PointerKind {
     /// Shared reference. `frozen` indicates the absence of any `UnsafeCell`.
@@ -1464,7 +1511,7 @@ pub enum InitKind {
     UninitMitigated0x01Fill,
 }
 
-impl<V: Idx> LayoutS<V> {
+impl LayoutS {
     /// Returns `true` if the layout corresponds to an unsized type.
     pub fn is_unsized(&self) -> bool {
         self.abi.is_unsized()
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index 9d2144c443b4a..38a559d892a35 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -8,7 +8,7 @@
 macro_rules! arena_types {
     ($macro:path) => (
         $macro!([
-            [] layout: rustc_target::abi::LayoutS<rustc_target::abi::VariantIdx>,
+            [] layout: rustc_target::abi::LayoutS,
             [] fn_abi: rustc_target::abi::call::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>,
             // AdtDef are interned and compared by address
             [decode] adt_def: rustc_middle::ty::AdtDefData,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 02088fffeb6c3..2fad657ee3306 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -149,7 +149,7 @@ pub struct CtxtInterners<'tcx> {
     const_: InternedSet<'tcx, ConstData<'tcx>>,
     const_allocation: InternedSet<'tcx, Allocation>,
     bound_variable_kinds: InternedSet<'tcx, List<ty::BoundVariableKind>>,
-    layout: InternedSet<'tcx, LayoutS<VariantIdx>>,
+    layout: InternedSet<'tcx, LayoutS>,
     adt_def: InternedSet<'tcx, AdtDefData>,
     external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>,
 }
@@ -1520,7 +1520,7 @@ direct_interners! {
     region: mk_region(RegionKind<'tcx>): Region -> Region<'tcx>,
     const_: mk_const_internal(ConstData<'tcx>): Const -> Const<'tcx>,
     const_allocation: intern_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>,
-    layout: intern_layout(LayoutS<VariantIdx>): Layout -> Layout<'tcx>,
+    layout: intern_layout(LayoutS): Layout -> Layout<'tcx>,
     adt_def: intern_adt_def(AdtDefData): AdtDef -> AdtDef<'tcx>,
     external_constraints: intern_external_constraints(ExternalConstraintsData<'tcx>): ExternalConstraints -> ExternalConstraints<'tcx>,
 }
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index 39761baf1bc29..8d2e92cc76c6f 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -3,10 +3,8 @@ pub use Primitive::*;
 
 use crate::json::{Json, ToJson};
 
-use std::fmt;
 use std::ops::Deref;
 
-use rustc_data_structures::intern::Interned;
 use rustc_macros::HashStable_Generic;
 
 pub mod call;
@@ -19,48 +17,6 @@ impl ToJson for Endian {
     }
 }
 
-rustc_index::newtype_index! {
-    #[derive(HashStable_Generic)]
-    pub struct VariantIdx {}
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
-#[rustc_pass_by_value]
-pub struct Layout<'a>(pub Interned<'a, LayoutS<VariantIdx>>);
-
-impl<'a> fmt::Debug for Layout<'a> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        // See comment on `<LayoutS as Debug>::fmt` above.
-        self.0.0.fmt(f)
-    }
-}
-
-impl<'a> Layout<'a> {
-    pub fn fields(self) -> &'a FieldsShape {
-        &self.0.0.fields
-    }
-
-    pub fn variants(self) -> &'a Variants<VariantIdx> {
-        &self.0.0.variants
-    }
-
-    pub fn abi(self) -> Abi {
-        self.0.0.abi
-    }
-
-    pub fn largest_niche(self) -> Option<Niche> {
-        self.0.0.largest_niche
-    }
-
-    pub fn align(self) -> AbiAndPrefAlign {
-        self.0.0.align
-    }
-
-    pub fn size(self) -> Size {
-        self.0.0.size
-    }
-}
-
 /// The layout of a type, alongside the type itself.
 /// Provides various type traversal APIs (e.g., recursing into fields).
 ///
@@ -75,8 +31,8 @@ pub struct TyAndLayout<'a, Ty> {
 }
 
 impl<'a, Ty> Deref for TyAndLayout<'a, Ty> {
-    type Target = &'a LayoutS<VariantIdx>;
-    fn deref(&self) -> &&'a LayoutS<VariantIdx> {
+    type Target = &'a LayoutS;
+    fn deref(&self) -> &&'a LayoutS {
         &self.layout.0.0
     }
 }
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 137289198786e..ebc2b7e5ba017 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -78,10 +78,10 @@ fn invert_mapping(map: &[u32]) -> Vec<u32> {
 fn univariant_uninterned<'tcx>(
     cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
     ty: Ty<'tcx>,
-    fields: &[TyAndLayout<'_>],
+    fields: &[Layout<'_>],
     repr: &ReprOptions,
     kind: StructKind,
-) -> Result<LayoutS<VariantIdx>, LayoutError<'tcx>> {
+) -> Result<LayoutS, LayoutError<'tcx>> {
     let dl = cx.data_layout();
     let pack = repr.pack;
     if pack.is_some() && repr.align.is_some() {
@@ -106,7 +106,7 @@ fn layout_of_uncached<'tcx>(
     };
     let scalar = |value: Primitive| tcx.intern_layout(LayoutS::scalar(cx, scalar_unit(value)));
 
-    let univariant = |fields: &[TyAndLayout<'_>], repr: &ReprOptions, kind| {
+    let univariant = |fields: &[Layout<'_>], repr: &ReprOptions, kind| {
         Ok(tcx.intern_layout(univariant_uninterned(cx, ty, fields, repr, kind)?))
     };
     debug_assert!(!ty.has_non_region_infer());
@@ -272,7 +272,7 @@ fn layout_of_uncached<'tcx>(
         ty::Closure(_, ref substs) => {
             let tys = substs.as_closure().upvar_tys();
             univariant(
-                &tys.map(|ty| cx.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
+                &tys.map(|ty| Ok(cx.layout_of(ty)?.layout)).collect::<Result<Vec<_>, _>>()?,
                 &ReprOptions::default(),
                 StructKind::AlwaysSized,
             )?
@@ -283,7 +283,7 @@ fn layout_of_uncached<'tcx>(
                 if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };
 
             univariant(
-                &tys.iter().map(|k| cx.layout_of(k)).collect::<Result<Vec<_>, _>>()?,
+                &tys.iter().map(|k| Ok(cx.layout_of(k)?.layout)).collect::<Result<Vec<_>, _>>()?,
                 &ReprOptions::default(),
                 kind,
             )?
@@ -412,7 +412,7 @@ fn layout_of_uncached<'tcx>(
                 .map(|v| {
                     v.fields
                         .iter()
-                        .map(|field| cx.layout_of(field.ty(tcx, substs)))
+                        .map(|field| Ok(cx.layout_of(field.ty(tcx, substs))?.layout))
                         .collect::<Result<Vec<_>, _>>()
                 })
                 .collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
@@ -630,23 +630,21 @@ fn generator_layout<'tcx>(
     // `info.variant_fields` already accounts for the reserved variants, so no need to add them.
     let max_discr = (info.variant_fields.len() - 1) as u128;
     let discr_int = Integer::fit_unsigned(max_discr);
-    let discr_int_ty = discr_int.to_ty(tcx, false);
     let tag = Scalar::Initialized {
         value: Primitive::Int(discr_int, false),
         valid_range: WrappingRange { start: 0, end: max_discr },
     };
     let tag_layout = cx.tcx.intern_layout(LayoutS::scalar(cx, tag));
-    let tag_layout = TyAndLayout { ty: discr_int_ty, layout: tag_layout };
 
     let promoted_layouts = ineligible_locals
         .iter()
         .map(|local| subst_field(info.field_tys[local].ty))
         .map(|ty| tcx.mk_maybe_uninit(ty))
-        .map(|ty| cx.layout_of(ty));
+        .map(|ty| Ok(cx.layout_of(ty)?.layout));
     let prefix_layouts = substs
         .as_generator()
         .prefix_tys()
-        .map(|ty| cx.layout_of(ty))
+        .map(|ty| Ok(cx.layout_of(ty)?.layout))
         .chain(iter::once(Ok(tag_layout)))
         .chain(promoted_layouts)
         .collect::<Result<Vec<_>, _>>()?;
@@ -715,7 +713,9 @@ fn generator_layout<'tcx>(
             let mut variant = univariant_uninterned(
                 cx,
                 ty,
-                &variant_only_tys.map(|ty| cx.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
+                &variant_only_tys
+                    .map(|ty| Ok(cx.layout_of(ty)?.layout))
+                    .collect::<Result<Vec<_>, _>>()?,
                 &ReprOptions::default(),
                 StructKind::Prefixed(prefix_size, prefix_align.abi),
             )?;
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index d0f497321abf5..4ff1c93dc5d55 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -10,7 +10,7 @@ use rustc_middle::ty::layout::LayoutError;
 use rustc_middle::ty::{self, Adt, TyCtxt};
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_target::abi::{LayoutS, Primitive, TagEncoding, VariantIdx, Variants};
+use rustc_target::abi::{LayoutS, Primitive, TagEncoding, Variants};
 use std::cmp::Ordering;
 use std::fmt;
 use std::rc::Rc;
@@ -1833,7 +1833,7 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
 }
 
 fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
-    fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS<VariantIdx>, tag_size: u64) {
+    fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS, tag_size: u64) {
         if layout.abi.is_unsized() {
             write!(w, "(unsized)");
         } else {