diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs
index 81c3c7baa07af..7a31682b4ad98 100644
--- a/compiler/rustc_middle/src/query/erase.rs
+++ b/compiler/rustc_middle/src/query/erase.rs
@@ -109,8 +109,6 @@ pub fn restore_val<T: Erasable>(erased_value: Erased<T>) -> T {
     unsafe { transmute_unchecked::<MaybeUninit<T::Storage>, T>(data) }
 }
 
-// FIXME(#151565): Using `T: ?Sized` here should let us remove the separate
-// impls for fat reference types.
 impl<T> Erasable for &'_ T {
     type Storage = [u8; size_of::<&'_ ()>()];
 }
@@ -119,12 +117,14 @@ impl<T> Erasable for &'_ [T] {
     type Storage = [u8; size_of::<&'_ [()]>()];
 }
 
-impl<T> Erasable for &'_ ty::List<T> {
-    type Storage = [u8; size_of::<&'_ ty::List<()>>()];
-}
-
-impl<T> Erasable for &'_ ty::ListWithCachedTypeInfo<T> {
-    type Storage = [u8; size_of::<&'_ ty::ListWithCachedTypeInfo<()>>()];
+// Note: this impl does not overlap with the impl for `&'_ T` above because `RawList` is unsized
+// and does not satisfy the implicit `T: Sized` bound.
+//
+// Furthermore, even if that implicit bound was removed (by adding `T: ?Sized`) this impl still
+// wouldn't overlap because `?Sized` is equivalent to `MetaSized` and `RawList` does not satisfy
+// `MetaSized` because it contains an extern type.
+impl<H, T> Erasable for &'_ ty::RawList<H, T> {
+    type Storage = [u8; size_of::<&'_ ty::RawList<(), ()>>()];
 }
 
 impl<T> Erasable for Result<&'_ T, traits::query::NoSolution> {
diff --git a/compiler/rustc_middle/src/ty/list.rs b/compiler/rustc_middle/src/ty/list.rs
index 82c23abefce45..a2bbc1ec75b99 100644
--- a/compiler/rustc_middle/src/ty/list.rs
+++ b/compiler/rustc_middle/src/ty/list.rs
@@ -37,7 +37,18 @@ pub type List<T> = RawList<(), T>;
 #[repr(C)]
 pub struct RawList<H, T> {
     skel: ListSkeleton<H, T>,
-    opaque: OpaqueListContents,
+
+    // `List`/`RawList` is variable-sized. So we want it to be an unsized
+    // type because calling `size_of::<List<Foo>>` would be dangerous.
+    //
+    // We also want `&List`/`&RawList` to be thin pointers.
+    //
+    // A field with an extern type is a hacky way to achieve this. (See
+    // https://github.com/rust-lang/rust/pull/154399#issuecomment-4157036415
+    // for some discussion.) This field is never directly manipulated because
+    // `RawList` instances are created with manual memory layout in
+    // `from_arena`.
+    _extern_ty: ExternTy,
 }
 
 /// A [`RawList`] without the unsized tail. This type is used for layout computation
@@ -47,7 +58,8 @@ struct ListSkeleton<H, T> {
     header: H,
     len: usize,
     /// Although this claims to be a zero-length array, in practice `len`
-    /// elements are actually present.
+    /// elements are actually present. This is achieved with manual memory
+    /// layout in `from_arena`. See also the comment on `RawList::_extern_ty`.
     data: [T; 0],
 }
 
@@ -58,9 +70,7 @@ impl<T> Default for &List<T> {
 }
 
 unsafe extern "C" {
-    /// A dummy type used to force `List` to be unsized while not requiring
-    /// references to it be wide pointers.
-    type OpaqueListContents;
+    type ExternTy;
 }
 
 impl<H, T> RawList<H, T> {
@@ -257,12 +267,13 @@ impl<'a, H, T: Copy> IntoIterator for &'a RawList<H, T> {
 
 unsafe impl<H: Sync, T: Sync> Sync for RawList<H, T> {}
 
-// We need this since `List` uses extern type `OpaqueListContents`.
+// We need this because `List` uses the extern type `ExternTy`.
 unsafe impl<H: DynSync, T: DynSync> DynSync for RawList<H, T> {}
 
 // Safety:
-// Layouts of `ListSkeleton<H, T>` and `RawList<H, T>` are the same, modulo opaque tail,
-// thus aligns of `ListSkeleton<H, T>` and `RawList<H, T>` must be the same.
+// Layouts of `ListSkeleton<H, T>` and `RawList<H, T>` are the same, modulo the
+// `_extern_ty` field (which is never instantiated in practice). Therefore,
+// aligns of `ListSkeleton<H, T>` and `RawList<H, T>` must be the same.
 unsafe impl<H, T> Aligned for RawList<H, T> {
     #[cfg(bootstrap)]
     const ALIGN: ptr::Alignment = align_of::<ListSkeleton<H, T>>();
@@ -310,3 +321,14 @@ impl<'tcx> From<FlagComputation<TyCtxt<'tcx>>> for TypeInfo {
         }
     }
 }
+
+#[cfg(target_pointer_width = "64")]
+mod size_asserts {
+    use rustc_data_structures::static_assert_size;
+
+    use super::*;
+    // tidy-alphabetical-start
+    static_assert_size!(&List<u32>, 8); // thin pointer
+    static_assert_size!(&RawList<u8, u32>, 8); // thin pointer
+    // tidy-alphabetical-end
+}
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 674799cb41bad..822bbe079327f 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -85,6 +85,7 @@ pub use self::context::{
 };
 pub use self::fold::*;
 pub use self::instance::{Instance, InstanceKind, ReifyReason};
+pub(crate) use self::list::RawList;
 pub use self::list::{List, ListWithCachedTypeInfo};
 pub use self::opaque_types::OpaqueTypeKey;
 pub use self::pattern::{Pattern, PatternKind};
