diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 28a44b09de2b1..eadac22e6526f 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -1856,6 +1856,29 @@ pub enum SizeSkeleton<'tcx> {
         /// depending on one, with regions erased.
         tail: Ty<'tcx>,
     },
+
+    /// A value whose size would be statically known after monomorphization, but may not yet be
+    /// knowable. The size must either be exactly one instance of a type, or an array of instances
+    /// of a type, because this variant isn't aware of other layout constraints such as padding.
+    ///
+    /// This can be useful for comparing two types which can be known to be the same size because,
+    /// though individually their sizes aren't known, they can be easily proven to have the same
+    /// size. For instance, checking whether two arrays of the same type parameter have the same
+    /// size (even though the type parameter's size may not yet be known), or for comparing the
+    /// size of a value of a type parameter with a repr(transaprent) type which contains a value of
+    /// the same type parameter.
+    ///
+    /// This is, however, currently very limited and conservative. For instance, two arrays of the
+    /// same type, where:
+    ///  * one's size is a const generic parameter
+    ///  * the other's is an const value which happens to be equal to that const generic parameter
+    /// will not be considered equal.
+    RepeatedTy {
+        /// The type of which this size is a known multiple (e.g. in an array).
+        ty: Ty<'tcx>,
+        /// How many copies of that type are stored.
+        repetitions: &'tcx ty::Const<'tcx>,
+    },
 }
 
 impl<'tcx> SizeSkeleton<'tcx> {
@@ -1906,7 +1929,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
                         .fields
                         .iter()
                         .map(|field| SizeSkeleton::compute(field.ty(tcx, substs), tcx, param_env));
-                    let mut ptr = None;
+                    let mut ptr_or_transparent = None;
                     for field in fields {
                         let field = field?;
                         match field {
@@ -1916,33 +1939,54 @@ impl<'tcx> SizeSkeleton<'tcx> {
                                 }
                             }
                             SizeSkeleton::Pointer { .. } => {
-                                if ptr.is_some() {
+                                if ptr_or_transparent.is_some() {
+                                    return Err(err);
+                                }
+                                ptr_or_transparent = Some(field);
+                            }
+                            SizeSkeleton::RepeatedTy { .. } => {
+                                if ptr_or_transparent.is_some() {
+                                    return Err(err);
+                                } else if def.repr.transparent() {
+                                    ptr_or_transparent = Some(field);
+                                } else {
                                     return Err(err);
                                 }
-                                ptr = Some(field);
                             }
                         }
                     }
-                    Ok(ptr)
+                    Ok(ptr_or_transparent)
                 };
 
                 let v0 = zero_or_ptr_variant(0)?;
                 // Newtype.
                 if def.variants.len() == 1 {
-                    if let Some(SizeSkeleton::Pointer { non_zero, tail }) = v0 {
-                        return Ok(SizeSkeleton::Pointer {
-                            non_zero: non_zero
-                                || match tcx.layout_scalar_valid_range(def.did) {
-                                    (Bound::Included(start), Bound::Unbounded) => start > 0,
-                                    (Bound::Included(start), Bound::Included(end)) => {
-                                        0 < start && start < end
-                                    }
-                                    _ => false,
-                                },
-                            tail,
-                        });
-                    } else {
-                        return Err(err);
+                    match v0 {
+                        Some(SizeSkeleton::Pointer { non_zero, tail }) => {
+                            return Ok(SizeSkeleton::Pointer {
+                                non_zero: non_zero
+                                    || match tcx.layout_scalar_valid_range(def.did) {
+                                        (Bound::Included(start), Bound::Unbounded) => start > 0,
+                                        (Bound::Included(start), Bound::Included(end)) => {
+                                            0 < start && start < end
+                                        }
+                                        _ => false,
+                                    },
+                                tail,
+                            });
+                        }
+                        Some(SizeSkeleton::RepeatedTy {
+                            ty: related_ty,
+                            repetitions: multiple,
+                        }) => {
+                            return Ok(SizeSkeleton::RepeatedTy {
+                                ty: related_ty,
+                                repetitions: multiple,
+                            });
+                        }
+                        _ => {
+                            return Err(err);
+                        }
                     }
                 }
 
@@ -1966,6 +2010,26 @@ impl<'tcx> SizeSkeleton<'tcx> {
                 }
             }
 
+            ty::Array(array_ty, size) => {
+                if array_ty.is_sized(tcx.at(DUMMY_SP), param_env) {
+                    Ok(SizeSkeleton::RepeatedTy { ty: array_ty, repetitions: size })
+                } else {
+                    Err(err)
+                }
+            }
+
+            ty::Param(param_ty) => {
+                let param_ty = param_ty.to_ty(tcx);
+                if param_ty.is_sized(tcx.at(DUMMY_SP), param_env) {
+                    Ok(SizeSkeleton::RepeatedTy {
+                        ty: param_ty,
+                        repetitions: ty::Const::from_usize(tcx, 1),
+                    })
+                } else {
+                    Err(err)
+                }
+            }
+
             _ => Err(err),
         }
     }
@@ -1976,6 +2040,10 @@ impl<'tcx> SizeSkeleton<'tcx> {
             (SizeSkeleton::Pointer { tail: a, .. }, SizeSkeleton::Pointer { tail: b, .. }) => {
                 a == b
             }
+            (
+                SizeSkeleton::RepeatedTy { ty: related_ty, repetitions: multiple },
+                SizeSkeleton::RepeatedTy { ty: other_related_ty, repetitions: other_multiple },
+            ) => related_ty == other_related_ty && multiple == other_multiple,
             _ => false,
         }
     }
diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs
index 012d97ef106c7..a48bab7d0fdc9 100644
--- a/compiler/rustc_passes/src/intrinsicck.rs
+++ b/compiler/rustc_passes/src/intrinsicck.rs
@@ -97,6 +97,9 @@ impl ExprVisitor<'tcx> {
         let skeleton_string = |ty: Ty<'tcx>, sk| match sk {
             Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()),
             Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{}`", tail),
+            Ok(SizeSkeleton::RepeatedTy { ty: related_ty, repetitions: _ }) => {
+                format!("size can vary because of {}", related_ty)
+            }
             Err(LayoutError::Unknown(bad)) => {
                 if bad == ty {
                     "this type does not have a fixed size".to_owned()
diff --git a/src/test/ui/transmute/transmute-related-type-parameters-with-arrays.rs b/src/test/ui/transmute/transmute-related-type-parameters-with-arrays.rs
new file mode 100644
index 0000000000000..81812eb7d6928
--- /dev/null
+++ b/src/test/ui/transmute/transmute-related-type-parameters-with-arrays.rs
@@ -0,0 +1,51 @@
+// Tests that `transmute` can be called in simple cases on types using type parameters with arrays.
+
+// run-pass
+
+use std::mem::transmute;
+
+#[repr(transparent)]
+struct Wrapper<T>([T; 10]);
+
+#[repr(transparent)]
+struct OtherWrapper<T>([T; 10]);
+
+#[repr(transparent)]
+struct ArbitrarySizedWrapper<T, const N: usize>([T; N]);
+
+fn wrap<T>(unwrapped: [T; 10]) -> Wrapper<T> {
+    unsafe {
+        transmute(unwrapped)
+    }
+}
+
+fn rewrap<T>(wrapped: Wrapper<T>) -> OtherWrapper<T> {
+    unsafe {
+        transmute(wrapped)
+    }
+}
+
+fn unwrap<T>(wrapped: OtherWrapper<T>) -> [T; 10] {
+    unsafe {
+        transmute(wrapped)
+    }
+}
+
+fn wrap_arbitrary_size<T, const N: usize>(arr: [T; N]) -> ArbitrarySizedWrapper<T, N> {
+    unsafe { transmute(arr) }
+}
+
+fn main() {
+    let unwrapped = [5_u64; 10];
+    let wrapped = wrap(unwrapped);
+    assert_eq!([5_u64; 10], wrapped.0);
+
+    let rewrapped = rewrap(wrapped);
+    assert_eq!([5_u64; 10], rewrapped.0);
+
+    let unwrapped = unwrap(rewrapped);
+    assert_eq!([5_u64; 10], unwrapped);
+
+    let arbitrary_sized_wrapper = wrap_arbitrary_size(unwrapped);
+    assert_eq!([5_u64; 10], arbitrary_sized_wrapper.0);
+}
diff --git a/src/test/ui/transmute/transmute-related-type-parameters.rs b/src/test/ui/transmute/transmute-related-type-parameters.rs
new file mode 100644
index 0000000000000..c32e2fdf060a3
--- /dev/null
+++ b/src/test/ui/transmute/transmute-related-type-parameters.rs
@@ -0,0 +1,29 @@
+// Tests that `transmute` can be called in simple cases on types using type parameters.
+
+// run-pass
+
+use std::mem::transmute;
+
+#[repr(transparent)]
+struct Wrapper<T>(T);
+
+fn wrap<T>(unwrapped: T) -> Wrapper<T> {
+    unsafe {
+        transmute(unwrapped)
+    }
+}
+
+fn unwrap<T>(wrapped: Wrapper<T>) -> T {
+    unsafe {
+        transmute(wrapped)
+    }
+}
+
+fn main() {
+    let unwrapped = 5_u64;
+    let wrapped = wrap(unwrapped);
+    assert_eq!(5_u64, wrapped.0);
+
+    let unwrapped = unwrap(wrapped);
+    assert_eq!(5_u64, unwrapped);
+}
diff --git a/src/test/ui/transmute/transmute-type-parameters.rs b/src/test/ui/transmute/transmute-type-parameters.rs
index 5f44b2d0f0163..78a0a59cf8178 100644
--- a/src/test/ui/transmute/transmute-type-parameters.rs
+++ b/src/test/ui/transmute/transmute-type-parameters.rs
@@ -1,4 +1,4 @@
-// Tests that `transmute` cannot be called on type parameters.
+// Tests that `transmute` cannot be called on arbitrary type parameters.
 
 use std::mem::transmute;
 
diff --git a/src/test/ui/transmute/transmute-type-parameters.stderr b/src/test/ui/transmute/transmute-type-parameters.stderr
index 220b929d4fd2e..8a99bb558eba7 100644
--- a/src/test/ui/transmute/transmute-type-parameters.stderr
+++ b/src/test/ui/transmute/transmute-type-parameters.stderr
@@ -4,7 +4,7 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-
 LL |     let _: i32 = transmute(x);
    |                  ^^^^^^^^^
    |
-   = note: source type: `T` (this type does not have a fixed size)
+   = note: source type: `T` (size can vary because of T)
    = note: target type: `i32` (32 bits)
 
 error[E0512]: cannot transmute between types of different sizes, or dependently-sized types