From 3aa14e3b2e61739e8d0ec7883f9c185821ca5da2 Mon Sep 17 00:00:00 2001
From: Jack Wrenn <jack@wrenn.fyi>
Date: Tue, 19 Mar 2024 14:49:13 +0000
Subject: [PATCH] Compute transmutability from `rustc_target::abi::Layout`

In its first step of computing transmutability, `rustc_transmutability`
constructs a byte-level representation of type layout (`Tree`). Previously, this
representation was computed for ADTs by inspecting the ADT definition and
performing our own layout computations. This process was error-prone, verbose,
and limited our ability to analyze many types (particularly default-repr types).

In this PR, we instead construct `Tree`s from `rustc_target::abi::Layout`s. This
helps ensure that layout optimizations are reflected our analyses, and increases
the kinds of types we can now analyze, including:
- default repr ADTs
- transparent unions
- `UnsafeCell`-containing types

Overall, this PR expands the expressvity of `rustc_transmutability` to be much
closer to the transmutability analysis performed by miri. Future PRs will work
to close the remaining gaps (e.g., support for `Box`, raw pointers, `NonZero*`,
coroutines, etc.).
---
 .../src/traits/error_reporting/mod.rs         |   2 +-
 .../error_reporting/type_err_ctxt_ext.rs      |  30 +-
 .../src/traits/select/confirmation.rs         |  42 +-
 compiler/rustc_transmute/src/layout/dfa.rs    |   2 +
 compiler/rustc_transmute/src/layout/nfa.rs    |   1 +
 compiler/rustc_transmute/src/layout/tree.rs   | 453 ++++++++----------
 compiler/rustc_transmute/src/lib.rs           |   4 +-
 .../src/maybe_transmutable/mod.rs             |  19 +-
 .../src/maybe_transmutable/query_context.rs   |  10 -
 .../ui/transmutability/arrays/huge-len.stderr |   4 +-
 .../should_require_well_defined_layout.stderr |  12 +-
 .../enums/niche_optimization.rs               | 156 ++++++
 .../enums/repr/padding_differences.rs         |  80 ++++
 ...defined_layout.rs => should_handle_all.rs} |  21 +-
 .../should_require_well_defined_layout.stderr | 135 ------
 .../unknown_src_field.stderr                  |   2 +-
 tests/ui/transmutability/maybeuninit.rs       |  43 ++
 tests/ui/transmutability/maybeuninit.stderr   |  18 +
 .../transmutability/references/unsafecell.rs  |  47 ++
 .../references/unsafecell.stderr              |  33 ++
 ...defined_layout.rs => should_handle_all.rs} |  29 +-
 .../should_require_well_defined_layout.stderr | 267 -----------
 .../transmute_infinitely_recursive_type.rs    |   1 +
 ...transmute_infinitely_recursive_type.stderr |  19 +-
 .../transmutability/transmute-padding-ice.rs  |   9 +-
 .../transmute-padding-ice.stderr              |  22 -
 tests/ui/transmutability/uninhabited.rs       |  71 +++
 tests/ui/transmutability/uninhabited.stderr   |  86 ++++
 .../unions/repr/should_handle_align.rs        |   4 +-
 ...defined_layout.rs => should_handle_all.rs} |  24 +-
 .../unions/repr/should_handle_packed.rs       |   1 -
 .../should_require_well_defined_layout.stderr |  47 --
 32 files changed, 905 insertions(+), 789 deletions(-)
 create mode 100644 tests/ui/transmutability/enums/niche_optimization.rs
 create mode 100644 tests/ui/transmutability/enums/repr/padding_differences.rs
 rename tests/ui/transmutability/enums/repr/{should_require_well_defined_layout.rs => should_handle_all.rs} (80%)
 delete mode 100644 tests/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr
 create mode 100644 tests/ui/transmutability/maybeuninit.rs
 create mode 100644 tests/ui/transmutability/maybeuninit.stderr
 create mode 100644 tests/ui/transmutability/references/unsafecell.rs
 create mode 100644 tests/ui/transmutability/references/unsafecell.stderr
 rename tests/ui/transmutability/structs/repr/{should_require_well_defined_layout.rs => should_handle_all.rs} (54%)
 delete mode 100644 tests/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr
 delete mode 100644 tests/ui/transmutability/transmute-padding-ice.stderr
 create mode 100644 tests/ui/transmutability/uninhabited.rs
 create mode 100644 tests/ui/transmutability/uninhabited.stderr
 rename tests/ui/transmutability/unions/repr/{should_require_well_defined_layout.rs => should_handle_all.rs} (58%)
 delete mode 100644 tests/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr

diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 0515b09ae46..cd3383dbfc2 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -40,7 +40,7 @@ pub struct ImplCandidate<'tcx> {
 
 enum GetSafeTransmuteErrorAndReason {
     Silent,
-    Error { err_msg: String, safe_transmute_explanation: String },
+    Error { err_msg: String, safe_transmute_explanation: Option<String> },
 }
 
 struct UnsatisfiedConst(pub bool);
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 01f9c8bb5d1..3a989c46fc1 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -557,7 +557,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                                 GetSafeTransmuteErrorAndReason::Error {
                                     err_msg,
                                     safe_transmute_explanation,
-                                } => (err_msg, Some(safe_transmute_explanation)),
+                                } => (err_msg, safe_transmute_explanation),
                             }
                         } else {
                             (err_msg, None)
@@ -3061,28 +3061,33 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             return GetSafeTransmuteErrorAndReason::Silent;
         };
 
+        let dst = trait_ref.args.type_at(0);
+        let src = trait_ref.args.type_at(1);
+        let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
+
         match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
             obligation.cause,
             src_and_dst,
             assume,
         ) {
             Answer::No(reason) => {
-                let dst = trait_ref.args.type_at(0);
-                let src = trait_ref.args.type_at(1);
-                let err_msg = format!("`{src}` cannot be safely transmuted into `{dst}`");
                 let safe_transmute_explanation = match reason {
                     rustc_transmute::Reason::SrcIsNotYetSupported => {
-                        format!("analyzing the transmutability of `{src}` is not yet supported.")
+                        format!("analyzing the transmutability of `{src}` is not yet supported")
                     }
 
                     rustc_transmute::Reason::DstIsNotYetSupported => {
-                        format!("analyzing the transmutability of `{dst}` is not yet supported.")
+                        format!("analyzing the transmutability of `{dst}` is not yet supported")
                     }
 
                     rustc_transmute::Reason::DstIsBitIncompatible => {
                         format!("at least one value of `{src}` isn't a bit-valid value of `{dst}`")
                     }
 
+                    rustc_transmute::Reason::DstUninhabited => {
+                        format!("`{dst}` is uninhabited")
+                    }
+
                     rustc_transmute::Reason::DstMayHaveSafetyInvariants => {
                         format!("`{dst}` may carry safety invariants")
                     }
@@ -3128,14 +3133,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         format!("`{dst}` has an unknown layout")
                     }
                 };
-                GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation }
+                GetSafeTransmuteErrorAndReason::Error {
+                    err_msg,
+                    safe_transmute_explanation: Some(safe_transmute_explanation),
+                }
             }
             // Should never get a Yes at this point! We already ran it before, and did not get a Yes.
             Answer::Yes => span_bug!(
                 span,
                 "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
             ),
-            other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"),
+            // Reached when a different obligation (namely `Freeze`) causes the
+            // transmutability analysis to fail. In this case, silence the
+            // transmutability error message in favor of that more specific
+            // error.
+            Answer::If(_) => {
+                GetSafeTransmuteErrorAndReason::Error { err_msg, safe_transmute_explanation: None }
+            }
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 6f512a1173f..a7cfcf6b366 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -314,12 +314,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                     .flat_map(|cond| flatten_answer_tree(tcx, obligation, predicate, cond))
                     .collect(),
                 Condition::IfTransmutable { src, dst } => {
-                    let trait_def_id = obligation.predicate.def_id();
+                    let transmute_trait = obligation.predicate.def_id();
                     let assume_const = predicate.trait_ref.args.const_at(2);
-                    let make_obl = |from_ty, to_ty| {
-                        let trait_ref1 = ty::TraitRef::new(
+                    let make_transmute_obl = |from_ty, to_ty| {
+                        let trait_ref = ty::TraitRef::new(
                             tcx,
-                            trait_def_id,
+                            transmute_trait,
                             [
                                 ty::GenericArg::from(to_ty),
                                 ty::GenericArg::from(from_ty),
@@ -331,17 +331,45 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             obligation.cause.clone(),
                             obligation.recursion_depth + 1,
                             obligation.param_env,
-                            trait_ref1,
+                            trait_ref,
                         )
                     };
 
+                    let make_freeze_obl = |ty| {
+                        let trait_ref = ty::TraitRef::new(
+                            tcx,
+                            tcx.lang_items().freeze_trait().unwrap(),
+                            [ty::GenericArg::from(ty)],
+                        );
+                        Obligation::with_depth(
+                            tcx,
+                            obligation.cause.clone(),
+                            obligation.recursion_depth + 1,
+                            obligation.param_env,
+                            trait_ref,
+                        )
+                    };
+
+                    let mut obls = vec![];
+
+                    // If the source is a shared reference, it must be `Freeze`;
+                    // otherwise, transmuting could lead to data races.
+                    if src.mutability == Mutability::Not {
+                        obls.extend([make_freeze_obl(src.ty), make_freeze_obl(dst.ty)])
+                    }
+
                     // If Dst is mutable, check bidirectionally.
                     // For example, transmuting bool -> u8 is OK as long as you can't update that u8
                     // to be > 1, because you could later transmute the u8 back to a bool and get UB.
                     match dst.mutability {
-                        Mutability::Not => vec![make_obl(src.ty, dst.ty)],
-                        Mutability::Mut => vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)],
+                        Mutability::Not => obls.push(make_transmute_obl(src.ty, dst.ty)),
+                        Mutability::Mut => obls.extend([
+                            make_transmute_obl(src.ty, dst.ty),
+                            make_transmute_obl(dst.ty, src.ty),
+                        ]),
                     }
+
+                    obls
                 }
             }
         }
diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs
index b8922696e30..77d5a48f158 100644
--- a/compiler/rustc_transmute/src/layout/dfa.rs
+++ b/compiler/rustc_transmute/src/layout/dfa.rs
@@ -35,6 +35,7 @@ impl<R> Transitions<R>
 where
     R: Ref,
 {
+    #[allow(dead_code)]
     fn insert(&mut self, transition: Transition<R>, state: State) {
         match transition {
             Transition::Byte(b) => {
@@ -82,6 +83,7 @@ impl<R> Dfa<R>
 where
     R: Ref,
 {
+    #[allow(dead_code)]
     pub(crate) fn unit() -> Self {
         let transitions: Map<State, Transitions<R>> = Map::default();
         let start = State::new();
diff --git a/compiler/rustc_transmute/src/layout/nfa.rs b/compiler/rustc_transmute/src/layout/nfa.rs
index 78fcceb5f2c..3c5963202c6 100644
--- a/compiler/rustc_transmute/src/layout/nfa.rs
+++ b/compiler/rustc_transmute/src/layout/nfa.rs
@@ -160,6 +160,7 @@ where
         Self { transitions, start, accepting }
     }
 
+    #[allow(dead_code)]
     pub(crate) fn edges_from(&self, start: State) -> Option<&Map<Transition<R>, Set<State>>> {
         self.transitions.get(&start)
     }
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index f6bc224c7e7..12c984f1603 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -173,16 +173,20 @@ pub(crate) mod rustc {
     use super::Tree;
     use crate::layout::rustc::{Def, Ref};
 
+    use rustc_middle::ty::layout::HasTyCtxt;
+    use rustc_middle::ty::layout::LayoutCx;
     use rustc_middle::ty::layout::LayoutError;
+    use rustc_middle::ty::layout::LayoutOf;
     use rustc_middle::ty::AdtDef;
-    use rustc_middle::ty::GenericArgsRef;
-    use rustc_middle::ty::ParamEnv;
+    use rustc_middle::ty::AdtKind;
+    use rustc_middle::ty::List;
     use rustc_middle::ty::ScalarInt;
-    use rustc_middle::ty::VariantDef;
     use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
     use rustc_span::ErrorGuaranteed;
-    use rustc_target::abi::Align;
-    use std::alloc;
+    use rustc_target::abi::FieldsShape;
+    use rustc_target::abi::Size;
+    use rustc_target::abi::TyAndLayout;
+    use rustc_target::abi::Variants;
 
     #[derive(Debug, Copy, Clone)]
     pub(crate) enum Err {
@@ -206,176 +210,64 @@ pub(crate) mod rustc {
         }
     }
 
-    trait LayoutExt {
-        fn clamp_align(&self, min_align: Align, max_align: Align) -> Self;
-    }
-
-    impl LayoutExt for alloc::Layout {
-        fn clamp_align(&self, min_align: Align, max_align: Align) -> Self {
-            let min_align = min_align.bytes().try_into().unwrap();
-            let max_align = max_align.bytes().try_into().unwrap();
-            Self::from_size_align(self.size(), self.align().clamp(min_align, max_align)).unwrap()
-        }
-    }
-
-    struct LayoutSummary {
-        total_align: Align,
-        total_size: usize,
-        discriminant_size: usize,
-        discriminant_align: Align,
-    }
-
-    impl LayoutSummary {
-        fn from_ty<'tcx>(ty: Ty<'tcx>, ctx: TyCtxt<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
-            use rustc_middle::ty::ParamEnvAnd;
-            use rustc_target::abi::{TyAndLayout, Variants};
-
-            let param_env = ParamEnv::reveal_all();
-            let param_env_and_type = ParamEnvAnd { param_env, value: ty };
-            let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?;
-
-            let total_size: usize = layout.size().bytes_usize();
-            let total_align: Align = layout.align().abi;
-            let discriminant_align: Align;
-            let discriminant_size: usize;
-
-            if let Variants::Multiple { tag, .. } = layout.variants() {
-                discriminant_align = tag.align(&ctx).abi;
-                discriminant_size = tag.size(&ctx).bytes_usize();
-            } else {
-                discriminant_align = Align::ONE;
-                discriminant_size = 0;
-            };
-
-            Ok(Self { total_align, total_size, discriminant_align, discriminant_size })
-        }
-
-        fn into(&self) -> alloc::Layout {
-            alloc::Layout::from_size_align(
-                self.total_size,
-                self.total_align.bytes().try_into().unwrap(),
-            )
-            .unwrap()
-        }
-    }
-
     impl<'tcx> Tree<Def<'tcx>, Ref<'tcx>> {
-        pub fn from_ty(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Result<Self, Err> {
-            use rustc_middle::ty::FloatTy::*;
-            use rustc_middle::ty::IntTy::*;
-            use rustc_middle::ty::UintTy::*;
+        pub fn from_ty(
+            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
+        ) -> Result<Self, Err> {
             use rustc_target::abi::HasDataLayout;
 
-            if let Err(e) = ty.error_reported() {
+            if let Err(e) = ty_and_layout.ty.error_reported() {
                 return Err(Err::TypeError(e));
             }
 
-            let target = tcx.data_layout();
+            let target = cx.tcx.data_layout();
+            let pointer_size = target.pointer_size;
 
-            match ty.kind() {
+            match ty_and_layout.ty.kind() {
                 ty::Bool => Ok(Self::bool()),
 
-                ty::Int(I8) | ty::Uint(U8) => Ok(Self::u8()),
-                ty::Int(I16) | ty::Uint(U16) => Ok(Self::number(2)),
-                ty::Int(I32) | ty::Uint(U32) | ty::Float(F32) => Ok(Self::number(4)),
-                ty::Int(I64) | ty::Uint(U64) | ty::Float(F64) => Ok(Self::number(8)),
-                ty::Int(I128) | ty::Uint(U128) => Ok(Self::number(16)),
-                ty::Int(Isize) | ty::Uint(Usize) => {
-                    Ok(Self::number(target.pointer_size.bytes_usize()))
+                ty::Float(nty) => {
+                    let width = nty.bit_width() / 8;
+                    Ok(Self::number(width as _))
                 }
 
-                ty::Tuple(members) => {
-                    if members.len() == 0 {
-                        Ok(Tree::unit())
-                    } else {
-                        Err(Err::NotYetSupported)
-                    }
+                ty::Int(nty) => {
+                    let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8;
+                    Ok(Self::number(width as _))
                 }
 
-                ty::Array(ty, len) => {
-                    let len = len
-                        .try_eval_target_usize(tcx, ParamEnv::reveal_all())
-                        .ok_or(Err::NotYetSupported)?;
-                    let elt = Tree::from_ty(*ty, tcx)?;
+                ty::Uint(nty) => {
+                    let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8;
+                    Ok(Self::number(width as _))
+                }
+
+                ty::Tuple(members) => Self::from_tuple(ty_and_layout, members, cx),
+
+                ty::Array(inner_ty, len) => {
+                    let FieldsShape::Array { stride, count } = &ty_and_layout.fields else {
+                        return Err(Err::NotYetSupported);
+                    };
+                    let inner_ty_and_layout = cx.layout_of(*inner_ty)?;
+                    assert_eq!(*stride, inner_ty_and_layout.size);
+                    let elt = Tree::from_ty(inner_ty_and_layout, cx)?;
                     Ok(std::iter::repeat(elt)
-                        .take(len as usize)
+                        .take(*count as usize)
                         .fold(Tree::unit(), |tree, elt| tree.then(elt)))
                 }
 
-                ty::Adt(adt_def, args_ref) => {
-                    use rustc_middle::ty::AdtKind;
-
-                    // If the layout is ill-specified, halt.
-                    if !(adt_def.repr().c() || adt_def.repr().int.is_some()) {
-                        return Err(Err::NotYetSupported);
+                ty::Adt(adt_def, _args_ref) if !ty_and_layout.ty.is_box() => {
+                    match adt_def.adt_kind() {
+                        AdtKind::Struct => Self::from_struct(ty_and_layout, *adt_def, cx),
+                        AdtKind::Enum => Self::from_enum(ty_and_layout, *adt_def, cx),
+                        AdtKind::Union => Self::from_union(ty_and_layout, *adt_def, cx),
                     }
-
-                    // Compute a summary of the type's layout.
-                    let layout_summary = LayoutSummary::from_ty(ty, tcx)?;
-
-                    // The layout begins with this adt's visibility.
-                    let vis = Self::def(Def::Adt(*adt_def));
-
-                    // And is followed the layout(s) of its variants
-                    Ok(vis.then(match adt_def.adt_kind() {
-                        AdtKind::Struct => Self::from_repr_c_variant(
-                            ty,
-                            *adt_def,
-                            args_ref,
-                            &layout_summary,
-                            None,
-                            adt_def.non_enum_variant(),
-                            tcx,
-                        )?,
-                        AdtKind::Enum => {
-                            trace!(?adt_def, "treeifying enum");
-                            let mut tree = Tree::uninhabited();
-
-                            for (idx, variant) in adt_def.variants().iter_enumerated() {
-                                let tag = tcx.tag_for_variant((ty, idx));
-                                tree = tree.or(Self::from_repr_c_variant(
-                                    ty,
-                                    *adt_def,
-                                    args_ref,
-                                    &layout_summary,
-                                    tag,
-                                    variant,
-                                    tcx,
-                                )?);
-                            }
-
-                            tree
-                        }
-                        AdtKind::Union => {
-                            // is the layout well-defined?
-                            if !adt_def.repr().c() {
-                                return Err(Err::NotYetSupported);
-                            }
-
-                            let ty_layout = layout_of(tcx, ty)?;
-
-                            let mut tree = Tree::uninhabited();
-
-                            for field in adt_def.all_fields() {
-                                let variant_ty = field.ty(tcx, args_ref);
-                                let variant_layout = layout_of(tcx, variant_ty)?;
-                                let padding_needed = ty_layout.size() - variant_layout.size();
-                                let variant = Self::def(Def::Field(field))
-                                    .then(Self::from_ty(variant_ty, tcx)?)
-                                    .then(Self::padding(padding_needed));
-
-                                tree = tree.or(variant);
-                            }
-
-                            tree
-                        }
-                    }))
                 }
 
                 ty::Ref(lifetime, ty, mutability) => {
-                    let layout = layout_of(tcx, *ty)?;
-                    let align = layout.align();
-                    let size = layout.size();
+                    let ty_and_layout = cx.layout_of(*ty)?;
+                    let align = ty_and_layout.align.abi.bytes() as usize;
+                    let size = ty_and_layout.size.bytes_usize();
                     Ok(Tree::Ref(Ref {
                         lifetime: *lifetime,
                         ty: *ty,
@@ -389,80 +281,143 @@ pub(crate) mod rustc {
             }
         }
 
-        fn from_repr_c_variant(
-            ty: Ty<'tcx>,
-            adt_def: AdtDef<'tcx>,
-            args_ref: GenericArgsRef<'tcx>,
-            layout_summary: &LayoutSummary,
-            tag: Option<ScalarInt>,
-            variant_def: &'tcx VariantDef,
-            tcx: TyCtxt<'tcx>,
+        /// Constructs a `Tree` from a tuple.
+        fn from_tuple(
+            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            members: &'tcx List<Ty<'tcx>>,
+            cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
         ) -> Result<Self, Err> {
-            let mut tree = Tree::unit();
-
-            let repr = adt_def.repr();
-            let min_align = repr.align.unwrap_or(Align::ONE);
-            let max_align = repr.pack.unwrap_or(Align::MAX);
-
-            let variant_span = trace_span!(
-                "treeifying variant",
-                min_align = ?min_align,
-                max_align = ?max_align,
-            )
-            .entered();
-
-            let mut variant_layout = alloc::Layout::from_size_align(
-                0,
-                layout_summary.total_align.bytes().try_into().unwrap(),
-            )
-            .unwrap();
-
-            // The layout of the variant is prefixed by the tag, if any.
-            if let Some(tag) = tag {
-                let tag_layout =
-                    alloc::Layout::from_size_align(tag.size().bytes_usize(), 1).unwrap();
-                tree = tree.then(Self::from_tag(tag, tcx));
-                variant_layout = variant_layout.extend(tag_layout).unwrap().0;
-            }
-
-            // Next come fields.
-            let fields_span = trace_span!("treeifying fields").entered();
-            for field_def in variant_def.fields.iter() {
-                let field_ty = field_def.ty(tcx, args_ref);
-                let _span = trace_span!("treeifying field", field = ?field_ty).entered();
-
-                // begin with the field's visibility
-                tree = tree.then(Self::def(Def::Field(field_def)));
-
-                // compute the field's layout characteristics
-                let field_layout = layout_of(tcx, field_ty)?.clamp_align(min_align, max_align);
-
-                // next comes the field's padding
-                let padding_needed = variant_layout.padding_needed_for(field_layout.align());
-                if padding_needed > 0 {
-                    tree = tree.then(Self::padding(padding_needed));
+            match &ty_and_layout.fields {
+                FieldsShape::Primitive => {
+                    assert_eq!(members.len(), 1);
+                    let inner_ty = members[0];
+                    let inner_ty_and_layout = cx.layout_of(inner_ty)?;
+                    assert_eq!(ty_and_layout.layout, inner_ty_and_layout.layout);
+                    Self::from_ty(inner_ty_and_layout, cx)
                 }
-
-                // finally, the field's layout
-                tree = tree.then(Self::from_ty(field_ty, tcx)?);
-
-                // extend the variant layout with the field layout
-                variant_layout = variant_layout.extend(field_layout).unwrap().0;
+                FieldsShape::Arbitrary { offsets, .. } => {
+                    assert_eq!(offsets.len(), members.len());
+                    Self::from_variant(Def::Primitive, None, ty_and_layout, ty_and_layout.size, cx)
+                }
+                FieldsShape::Array { .. } | FieldsShape::Union(_) => Err(Err::NotYetSupported),
             }
-            drop(fields_span);
-
-            // finally: padding
-            let padding_span = trace_span!("adding trailing padding").entered();
-            if layout_summary.total_size > variant_layout.size() {
-                let padding_needed = layout_summary.total_size - variant_layout.size();
-                tree = tree.then(Self::padding(padding_needed));
-            };
-            drop(padding_span);
-            drop(variant_span);
-            Ok(tree)
         }
 
-        pub fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
+        /// Constructs a `Tree` from a struct.
+        ///
+        /// # Panics
+        ///
+        /// Panics if `def` is not a struct definition.
+        fn from_struct(
+            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            def: AdtDef<'tcx>,
+            cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
+        ) -> Result<Self, Err> {
+            assert!(def.is_struct());
+            let def = Def::Adt(def);
+            Self::from_variant(def, None, ty_and_layout, ty_and_layout.size, cx)
+        }
+
+        /// Constructs a `Tree` from an enum.
+        ///
+        /// # Panics
+        ///
+        /// Panics if `def` is not an enum definition.
+        fn from_enum(
+            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            def: AdtDef<'tcx>,
+            cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
+        ) -> Result<Self, Err> {
+            assert!(def.is_enum());
+            let layout = ty_and_layout.layout;
+
+            if let Variants::Multiple { tag_field, .. } = layout.variants() {
+                // For enums (but not coroutines), the tag field is
+                // currently always the first field of the layout.
+                assert_eq!(*tag_field, 0);
+            }
+
+            let variants = def.discriminants(cx.tcx()).try_fold(
+                Self::uninhabited(),
+                |variants, (idx, ref discriminant)| {
+                    let tag = cx.tcx.tag_for_variant((ty_and_layout.ty, idx));
+                    let variant_def = Def::Variant(def.variant(idx));
+                    let variant_ty_and_layout = ty_and_layout.for_variant(&cx, idx);
+                    let variant = Self::from_variant(
+                        variant_def,
+                        tag,
+                        variant_ty_and_layout,
+                        layout.size,
+                        cx,
+                    )?;
+                    Result::<Self, Err>::Ok(variants.or(variant))
+                },
+            )?;
+
+            return Ok(Self::def(Def::Adt(def)).then(variants));
+        }
+
+        /// Constructs a `Tree` from a 'variant-like' layout.
+        ///
+        /// A 'variant-like' layout includes those of structs and, of course,
+        /// enum variants. Pragmatically speaking, this method supports anything
+        /// with `FieldsShape::Arbitrary`.
+        ///
+        /// Note: This routine assumes that the optional `tag` is the first
+        /// field, and enum callers should check that `tag_field` is, in fact,
+        /// `0`.
+        fn from_variant(
+            def: Def<'tcx>,
+            tag: Option<ScalarInt>,
+            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            total_size: Size,
+            cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
+        ) -> Result<Self, Err> {
+            // This constructor does not support non-`FieldsShape::Arbitrary`
+            // layouts.
+            let FieldsShape::Arbitrary { offsets, memory_index } = ty_and_layout.layout.fields()
+            else {
+                return Err(Err::NotYetSupported);
+            };
+
+            // When this function is invoked with enum variants,
+            // `ty_and_layout.size` does not encompass the entire size of the
+            // enum. We rely on `total_size` for this.
+            assert!(ty_and_layout.size <= total_size);
+
+            let mut size = Size::ZERO;
+            let mut struct_tree = Self::def(def);
+
+            // If a `tag` is provided, place it at the start of the layout.
+            if let Some(tag) = tag {
+                size += tag.size();
+                struct_tree = struct_tree.then(Self::from_tag(tag, cx.tcx));
+            }
+
+            // Append the fields, in memory order, to the layout.
+            let inverse_memory_index = memory_index.invert_bijective_mapping();
+            for (memory_idx, field_idx) in inverse_memory_index.iter_enumerated() {
+                // Add interfield padding.
+                let padding_needed = offsets[*field_idx] - size;
+                let padding = Self::padding(padding_needed.bytes_usize());
+
+                let field_ty_and_layout = ty_and_layout.field(&cx, field_idx.as_usize());
+                let field_tree = Self::from_ty(field_ty_and_layout, cx)?;
+
+                struct_tree = struct_tree.then(padding).then(field_tree);
+
+                size += padding_needed + field_ty_and_layout.size;
+            }
+
+            // Add trailing padding.
+            let padding_needed = total_size - size;
+            let trailing_padding = Self::padding(padding_needed.bytes_usize());
+
+            Ok(struct_tree.then(trailing_padding))
+        }
+
+        /// Constructs a `Tree` representing the value of a enum tag.
+        fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
             use rustc_target::abi::Endian;
             let size = tag.size();
             let bits = tag.to_bits(size).unwrap();
@@ -479,24 +434,42 @@ pub(crate) mod rustc {
             };
             Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect())
         }
-    }
 
-    fn layout_of<'tcx>(
-        ctx: TyCtxt<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> Result<alloc::Layout, &'tcx LayoutError<'tcx>> {
-        use rustc_middle::ty::ParamEnvAnd;
-        use rustc_target::abi::TyAndLayout;
+        /// Constructs a `Tree` from a union.
+        ///
+        /// # Panics
+        ///
+        /// Panics if `def` is not a union definition.
+        fn from_union(
+            ty_and_layout: TyAndLayout<'tcx, Ty<'tcx>>,
+            def: AdtDef<'tcx>,
+            cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
+        ) -> Result<Self, Err> {
+            assert!(def.is_union());
 
-        let param_env = ParamEnv::reveal_all();
-        let param_env_and_type = ParamEnvAnd { param_env, value: ty };
-        let TyAndLayout { layout, .. } = ctx.layout_of(param_env_and_type)?;
-        let layout = alloc::Layout::from_size_align(
-            layout.size().bytes_usize(),
-            layout.align().abi.bytes().try_into().unwrap(),
-        )
-        .unwrap();
-        trace!(?ty, ?layout, "computed layout for type");
-        Ok(layout)
+            let union_layout = ty_and_layout.layout;
+
+            // This constructor does not support non-`FieldsShape::Union`
+            // layouts. Fields of this shape are all placed at offset 0.
+            let FieldsShape::Union(fields) = union_layout.fields() else {
+                return Err(Err::NotYetSupported);
+            };
+
+            let fields = &def.non_enum_variant().fields;
+            let fields = fields.iter_enumerated().try_fold(
+                Self::uninhabited(),
+                |fields, (idx, ref field_def)| {
+                    let field_def = Def::Field(field_def);
+                    let field_ty_and_layout = ty_and_layout.field(&cx, idx.as_usize());
+                    let field = Self::from_ty(field_ty_and_layout, cx)?;
+                    let trailing_padding_needed = union_layout.size - field_ty_and_layout.size;
+                    let trailing_padding = Self::padding(trailing_padding_needed.bytes_usize());
+                    let field_and_padding = field.then(trailing_padding);
+                    Result::<Self, Err>::Ok(fields.or(field_and_padding))
+                },
+            )?;
+
+            Ok(Self::def(Def::Adt(def)).then(fields))
+        }
     }
 }
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index b6f937ebe47..12312271646 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -1,6 +1,6 @@
 #![feature(alloc_layout_extra)]
 #![feature(never_type)]
-#![allow(dead_code, unused_variables)]
+#![allow(unused_variables)]
 
 #[macro_use]
 extern crate tracing;
@@ -49,6 +49,8 @@ pub enum Reason<T> {
     DstIsNotYetSupported,
     /// The layout of the destination type is bit-incompatible with the source type.
     DstIsBitIncompatible,
+    /// The destination type is uninhabited.
+    DstUninhabited,
     /// The destination type may carry safety invariants.
     DstMayHaveSafetyInvariants,
     /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
index 16d15580a05..2789fe8f6b1 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
@@ -33,6 +33,9 @@ mod rustc {
     use super::*;
     use crate::layout::tree::rustc::Err;
 
+    use rustc_middle::ty::layout::LayoutCx;
+    use rustc_middle::ty::layout::LayoutOf;
+    use rustc_middle::ty::ParamEnv;
     use rustc_middle::ty::Ty;
     use rustc_middle::ty::TyCtxt;
 
@@ -43,12 +46,20 @@ mod rustc {
         pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> {
             let Self { src, dst, assume, context } = self;
 
+            let layout_cx = LayoutCx { tcx: context, param_env: ParamEnv::reveal_all() };
+            let layout_of = |ty| {
+                layout_cx
+                    .layout_of(ty)
+                    .map_err(|_| Err::NotYetSupported)
+                    .and_then(|tl| Tree::from_ty(tl, layout_cx))
+            };
+
             // Convert `src` and `dst` from their rustc representations, to `Tree`-based
             // representations. If these conversions fail, conclude that the transmutation is
             // unacceptable; the layouts of both the source and destination types must be
             // well-defined.
-            let src = Tree::from_ty(src, context);
-            let dst = Tree::from_ty(dst, context);
+            let src = layout_of(src);
+            let dst = layout_of(dst);
 
             match (src, dst) {
                 (Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => {
@@ -86,6 +97,10 @@ where
         // references.
         let src = src.prune(&|def| false);
 
+        if src.is_inhabited() && !dst.is_inhabited() {
+            return Answer::No(Reason::DstUninhabited);
+        }
+
         trace!(?src, "pruned src");
 
         // Remove all `Def` nodes from `dst`, additionally...
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
index 54ed03d44e6..1ccb6f36c8e 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
@@ -5,8 +5,6 @@ pub(crate) trait QueryContext {
     type Def: layout::Def;
     type Ref: layout::Ref;
     type Scope: Copy;
-
-    fn min_align(&self, reference: Self::Ref) -> usize;
 }
 
 #[cfg(test)]
@@ -31,10 +29,6 @@ pub(crate) mod test {
         type Def = Def;
         type Ref = !;
         type Scope = ();
-
-        fn min_align(&self, reference: !) -> usize {
-            unimplemented!()
-        }
     }
 }
 
@@ -48,9 +42,5 @@ mod rustc {
         type Ref = layout::rustc::Ref<'tcx>;
 
         type Scope = Ty<'tcx>;
-
-        fn min_align(&self, reference: Self::Ref) -> usize {
-            unimplemented!()
-        }
     }
 }
diff --git a/tests/ui/transmutability/arrays/huge-len.stderr b/tests/ui/transmutability/arrays/huge-len.stderr
index 37160c5c959..3fc652f47c1 100644
--- a/tests/ui/transmutability/arrays/huge-len.stderr
+++ b/tests/ui/transmutability/arrays/huge-len.stderr
@@ -2,7 +2,7 @@ error[E0277]: `()` cannot be safely transmuted into `ExplicitlyPadded`
   --> $DIR/huge-len.rs:21:41
    |
 LL |     assert::is_maybe_transmutable::<(), ExplicitlyPadded>();
-   |                                         ^^^^^^^^^^^^^^^^ values of the type `ExplicitlyPadded` are too big for the current architecture
+   |                                         ^^^^^^^^^^^^^^^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/huge-len.rs:8:14
@@ -17,7 +17,7 @@ error[E0277]: `ExplicitlyPadded` cannot be safely transmuted into `()`
   --> $DIR/huge-len.rs:24:55
    |
 LL |     assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
-   |                                                       ^^ values of the type `ExplicitlyPadded` are too big for the current architecture
+   |                                                       ^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/huge-len.rs:8:14
diff --git a/tests/ui/transmutability/arrays/should_require_well_defined_layout.stderr b/tests/ui/transmutability/arrays/should_require_well_defined_layout.stderr
index e486928a445..b4cd70142c4 100644
--- a/tests/ui/transmutability/arrays/should_require_well_defined_layout.stderr
+++ b/tests/ui/transmutability/arrays/should_require_well_defined_layout.stderr
@@ -2,7 +2,7 @@ error[E0277]: `[String; 0]` cannot be safely transmuted into `()`
   --> $DIR/should_require_well_defined_layout.rs:25:52
    |
 LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `[String; 0]` is not yet supported.
+   |                                                    ^^ analyzing the transmutability of `[String; 0]` is not yet supported
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:12:14
@@ -23,7 +23,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 0]`
   --> $DIR/should_require_well_defined_layout.rs:26:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `[String; 0]` is not yet supported.
+   |                                               ^^^^^^^^^ analyzing the transmutability of `[String; 0]` is not yet supported
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:12:14
@@ -44,7 +44,7 @@ error[E0277]: `[String; 1]` cannot be safely transmuted into `()`
   --> $DIR/should_require_well_defined_layout.rs:31:52
    |
 LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `[String; 1]` is not yet supported.
+   |                                                    ^^ analyzing the transmutability of `[String; 1]` is not yet supported
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:12:14
@@ -65,7 +65,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 1]`
   --> $DIR/should_require_well_defined_layout.rs:32:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `[String; 1]` is not yet supported.
+   |                                               ^^^^^^^^^ analyzing the transmutability of `[String; 1]` is not yet supported
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:12:14
@@ -86,7 +86,7 @@ error[E0277]: `[String; 2]` cannot be safely transmuted into `()`
   --> $DIR/should_require_well_defined_layout.rs:37:52
    |
 LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `[String; 2]` is not yet supported.
+   |                                                    ^^ analyzing the transmutability of `[String; 2]` is not yet supported
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:12:14
@@ -107,7 +107,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 2]`
   --> $DIR/should_require_well_defined_layout.rs:38:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `[String; 2]` is not yet supported.
+   |                                               ^^^^^^^^^ analyzing the transmutability of `[String; 2]` is not yet supported
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:12:14
diff --git a/tests/ui/transmutability/enums/niche_optimization.rs b/tests/ui/transmutability/enums/niche_optimization.rs
new file mode 100644
index 00000000000..23f57ecad75
--- /dev/null
+++ b/tests/ui/transmutability/enums/niche_optimization.rs
@@ -0,0 +1,156 @@
+//@ check-pass
+//! Checks that niche optimizations are encoded correctly.
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, {
+            Assume {
+                alignment: false,
+                lifetimes: false,
+                safety: true,
+                validity: false,
+            }
+        }>
+    {}
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, {
+            Assume {
+                alignment: false,
+                lifetimes: false,
+                safety: true,
+                validity: true,
+            }
+        }>
+    {}
+}
+
+#[repr(u8)] enum V0 { V = 0 }
+#[repr(u8)] enum V1 { V = 1 }
+#[repr(u8)] enum V2 { V = 2 }
+#[repr(u8)] enum V253 { V = 253 }
+#[repr(u8)] enum V254 { V = 254 }
+#[repr(u8)] enum V255 { V = 255 }
+
+fn bool() {
+    enum OptionLike {
+        A(bool),
+        B,
+    }
+
+    const _: () = {
+        assert!(std::mem::size_of::<OptionLike>() == 1);
+    };
+
+    assert::is_transmutable::<OptionLike, u8>();
+
+    assert::is_transmutable::<bool, OptionLike>();
+    assert::is_transmutable::<V0, OptionLike>();
+    assert::is_transmutable::<V1, OptionLike>();
+    assert::is_transmutable::<V2, OptionLike>();
+}
+
+fn one_niche() {
+    #[repr(u8)]
+    enum N1 {
+        S = 0,
+        E = 255 - 1,
+    }
+
+    enum OptionLike {
+        A(N1),
+        B,
+    }
+
+    const _: () = {
+        assert!(std::mem::size_of::<OptionLike>() == 1);
+    };
+
+    assert::is_transmutable::<OptionLike, u8>();
+    assert::is_transmutable::<V0, OptionLike>();
+    assert::is_transmutable::<V254, OptionLike>();
+    assert::is_transmutable::<V255, OptionLike>();
+}
+
+fn one_niche_alt() {
+    #[repr(u8)]
+    enum N1 {
+        S = 1,
+        E = 255 - 1,
+    }
+
+    enum OptionLike {
+        A(N1),
+        B,
+        C,
+    }
+
+    const _: () = {
+        assert!(std::mem::size_of::<OptionLike>() == 1);
+    };
+
+    assert::is_transmutable::<OptionLike, u8>();
+    assert::is_transmutable::<V0, OptionLike>();
+    assert::is_transmutable::<V254, OptionLike>();
+    assert::is_transmutable::<V255, OptionLike>();
+}
+
+fn two_niche() {
+    #[repr(u8)]
+    enum Niche {
+        S = 0,
+        E = 255 - 2,
+    }
+
+    enum OptionLike {
+        A(Niche),
+        B,
+        C,
+    }
+
+    const _: () = {
+        assert!(std::mem::size_of::<OptionLike>() == 1);
+    };
+
+    assert::is_transmutable::<OptionLike, u8>();
+    assert::is_transmutable::<V0, OptionLike>();
+    assert::is_transmutable::<V253, OptionLike>();
+    assert::is_transmutable::<V254, OptionLike>();
+    assert::is_transmutable::<V255, OptionLike>();
+}
+
+fn no_niche() {
+    use std::mem::MaybeUninit;
+
+    #[repr(u8)]
+    enum Niche {
+        S = 0,
+        E = 255 - 1,
+    }
+
+    enum OptionLike {
+        A(Niche),
+        B,
+        C,
+    }
+
+    const _: () = {
+        assert!(std::mem::size_of::<OptionLike>() == 2);
+    };
+
+    #[repr(C)]
+    struct Pair<T, U>(T, U);
+
+    assert::is_transmutable::<V0, Niche>();
+    assert::is_transmutable::<V254, Niche>();
+    assert::is_transmutable::<Pair<V0, Niche>, OptionLike>();
+    assert::is_transmutable::<Pair<V1, MaybeUninit<u8>>, OptionLike>();
+    assert::is_transmutable::<Pair<V2, MaybeUninit<u8>>, OptionLike>();
+}
diff --git a/tests/ui/transmutability/enums/repr/padding_differences.rs b/tests/ui/transmutability/enums/repr/padding_differences.rs
new file mode 100644
index 00000000000..d0e1502b5e2
--- /dev/null
+++ b/tests/ui/transmutability/enums/repr/padding_differences.rs
@@ -0,0 +1,80 @@
+//@ check-pass
+//! Adapted from https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html#explicit-repr-annotation-without-c-compatibility
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+use std::mem::MaybeUninit;
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, {
+            Assume {
+                alignment: false,
+                lifetimes: false,
+                safety: true,
+                validity: false,
+            }
+        }>
+    {}
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, {
+            Assume {
+                alignment: false,
+                lifetimes: false,
+                safety: true,
+                validity: true,
+            }
+        }>
+    {}
+}
+
+#[repr(u8)] enum V0 { V = 0 }
+#[repr(u8)] enum V1 { V = 1 }
+
+fn repr_u8() {
+    #[repr(u8)]
+    enum TwoCases {
+        A(u8, u16),     // 0x00 INIT INIT INIT
+        B(u16),         // 0x01 PADD INIT INIT
+    }
+
+    const _: () = {
+        assert!(std::mem::size_of::<TwoCases>() == 4);
+    };
+
+    #[repr(C)] struct TwoCasesA(V0, u8, u8, u8);
+    #[repr(C)] struct TwoCasesB(V1, MaybeUninit<u8>, u8, u8);
+
+    assert::is_transmutable::<TwoCasesA, TwoCases>();
+    assert::is_transmutable::<TwoCasesB, TwoCases>();
+
+    assert::is_maybe_transmutable::<TwoCases, TwoCasesA>();
+    assert::is_maybe_transmutable::<TwoCases, TwoCasesB>();
+}
+
+fn repr_c_u8() {
+    #[repr(C, u8)]
+    enum TwoCases {
+        A(u8, u16),     // 0x00 PADD INIT PADD INIT INIT
+        B(u16),         // 0x01 PADD INIT INIT PADD PADD
+    }
+
+    const _: () = {
+        assert!(std::mem::size_of::<TwoCases>() == 6);
+    };
+
+    #[repr(C)] struct TwoCasesA(V0, MaybeUninit<u8>, u8, MaybeUninit<u8>, u8, u8);
+    #[repr(C)] struct TwoCasesB(V1, MaybeUninit<u8>, u8, u8, MaybeUninit<u8>, MaybeUninit<u8>);
+
+    assert::is_transmutable::<TwoCasesA, TwoCases>();
+    assert::is_transmutable::<TwoCasesB, TwoCases>();
+
+    assert::is_maybe_transmutable::<TwoCases, TwoCasesA>();
+    assert::is_maybe_transmutable::<TwoCases, TwoCasesB>();
+}
diff --git a/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.rs b/tests/ui/transmutability/enums/repr/should_handle_all.rs
similarity index 80%
rename from tests/ui/transmutability/enums/repr/should_require_well_defined_layout.rs
rename to tests/ui/transmutability/enums/repr/should_handle_all.rs
index 630e662b926..a8ec86fa40d 100644
--- a/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.rs
+++ b/tests/ui/transmutability/enums/repr/should_handle_all.rs
@@ -1,5 +1,4 @@
-//! An enum must have a well-defined layout to participate in a transmutation.
-
+//@ check-pass
 #![crate_type = "lib"]
 #![feature(repr128)]
 #![feature(transmutability)]
@@ -21,23 +20,17 @@ mod assert {
     {}
 }
 
-fn should_reject_repr_rust() {
-    fn void() {
-        enum repr_rust {}
-        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
-        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
-    }
-
+fn should_accept_repr_rust() {
     fn singleton() {
         enum repr_rust { V }
-        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
-        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
+        assert::is_maybe_transmutable::<repr_rust, ()>();
+        assert::is_maybe_transmutable::<u128, repr_rust>();
     }
 
     fn duplex() {
         enum repr_rust { A, B }
-        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
-        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
+        assert::is_maybe_transmutable::<repr_rust, ()>();
+        assert::is_maybe_transmutable::<u128, repr_rust>();
     }
 }
 
@@ -116,7 +109,7 @@ fn should_accept_primitive_reprs()
     }
 }
 
-fn should_accept_repr_C() {
+fn should_accept_repr_c() {
     #[repr(C)] enum repr_c { V }
     assert::is_maybe_transmutable::<repr_c, ()>();
     assert::is_maybe_transmutable::<i128, repr_c>();
diff --git a/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr b/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr
deleted file mode 100644
index 2a683de6a65..00000000000
--- a/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr
+++ /dev/null
@@ -1,135 +0,0 @@
-error[E0277]: `void::repr_rust` cannot be safely transmuted into `()`
-  --> $DIR/should_require_well_defined_layout.rs:27:52
-   |
-LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `void::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:13:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `u128` cannot be safely transmuted into `void::repr_rust`
-  --> $DIR/should_require_well_defined_layout.rs:28:47
-   |
-LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `void::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:13:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `singleton::repr_rust` cannot be safely transmuted into `()`
-  --> $DIR/should_require_well_defined_layout.rs:33:52
-   |
-LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `singleton::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:13:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `u128` cannot be safely transmuted into `singleton::repr_rust`
-  --> $DIR/should_require_well_defined_layout.rs:34:47
-   |
-LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `singleton::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:13:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `duplex::repr_rust` cannot be safely transmuted into `()`
-  --> $DIR/should_require_well_defined_layout.rs:39:52
-   |
-LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `duplex::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:13:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `u128` cannot be safely transmuted into `duplex::repr_rust`
-  --> $DIR/should_require_well_defined_layout.rs:40:47
-   |
-LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `duplex::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:13:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error: aborting due to 6 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr b/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr
index eeed8a62a2a..cabc7bcfef7 100644
--- a/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr
+++ b/tests/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr
@@ -8,7 +8,7 @@ error[E0277]: `Src` cannot be safely transmuted into `Dst`
   --> $DIR/unknown_src_field.rs:19:36
    |
 LL |     assert::is_transmutable::<Src, Dst>();
-   |                                    ^^^ `Dst` has an unknown layout
+   |                                    ^^^ analyzing the transmutability of `Dst` is not yet supported
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/unknown_src_field.rs:12:14
diff --git a/tests/ui/transmutability/maybeuninit.rs b/tests/ui/transmutability/maybeuninit.rs
new file mode 100644
index 00000000000..77c3381c774
--- /dev/null
+++ b/tests/ui/transmutability/maybeuninit.rs
@@ -0,0 +1,43 @@
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+use std::mem::MaybeUninit;
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
+    {}
+}
+
+fn validity() {
+    // An initialized byte is a valid uninitialized byte.
+    assert::is_maybe_transmutable::<u8, MaybeUninit<u8>>();
+
+    // An uninitialized byte is never a valid initialized byte.
+    assert::is_maybe_transmutable::<MaybeUninit<u8>, u8>(); //~ ERROR: cannot be safely transmuted
+}
+
+fn padding() {
+    #[repr(align(8))]
+    struct Align8;
+
+    #[repr(u8)]
+    enum ImplicitlyPadded {
+        A(Align8),
+    }
+
+    #[repr(u8)]
+    enum V0 {
+        V0 = 0,
+    }
+
+    #[repr(C)]
+    struct ExplicitlyPadded(V0, MaybeUninit<[u8; 7]>);
+
+    assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>();
+    assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>();
+}
diff --git a/tests/ui/transmutability/maybeuninit.stderr b/tests/ui/transmutability/maybeuninit.stderr
new file mode 100644
index 00000000000..be7dcaf35ea
--- /dev/null
+++ b/tests/ui/transmutability/maybeuninit.stderr
@@ -0,0 +1,18 @@
+error[E0277]: `MaybeUninit<u8>` cannot be safely transmuted into `u8`
+  --> $DIR/maybeuninit.rs:21:54
+   |
+LL |     assert::is_maybe_transmutable::<MaybeUninit<u8>, u8>();
+   |                                                      ^^ at least one value of `MaybeUninit<u8>` isn't a bit-valid value of `u8`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/maybeuninit.rs:12:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this function
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/transmutability/references/unsafecell.rs b/tests/ui/transmutability/references/unsafecell.rs
new file mode 100644
index 00000000000..a8a1f969fb4
--- /dev/null
+++ b/tests/ui/transmutability/references/unsafecell.rs
@@ -0,0 +1,47 @@
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+use std::cell::UnsafeCell;
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
+    {}
+}
+
+fn value_to_value() {
+    // We accept value-to-value transmutations of `UnsafeCell`-containing types,
+    // because owning a value implies exclusive access.
+    assert::is_maybe_transmutable::<UnsafeCell<u8>, u8>();
+    assert::is_maybe_transmutable::<u8, UnsafeCell<u8>>();
+    assert::is_maybe_transmutable::<UnsafeCell<u8>, UnsafeCell<u8>>();
+}
+
+fn ref_to_ref() {
+    // We forbid `UnsafeCell`-containing ref-to-ref transmutations, because the
+    // two types may use different, incompatible synchronization strategies.
+    assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell<u8>>(); //~ ERROR: cannot be safely transmuted
+
+    assert::is_maybe_transmutable::<&'static UnsafeCell<u8>, &'static UnsafeCell<u8>>(); //~ ERROR: cannot be safely transmuted
+}
+
+fn mut_to_mut() {
+    // `UnsafeCell` does't matter for `&mut T` to `&mut U`, since exclusive
+    // borrows can't be used for shared access.
+    assert::is_maybe_transmutable::<&'static mut u8, &'static mut UnsafeCell<u8>>();
+    assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static mut u8>();
+    assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static mut UnsafeCell<u8>>();
+}
+
+fn mut_to_ref() {
+    // We don't care about `UnsafeCell` for transmutations in the form `&mut T
+    // -> &U`, because downgrading a `&mut T` to a `&U` deactivates `&mut T` for
+    // the lifetime of `&U`.
+    assert::is_maybe_transmutable::<&'static mut u8, &'static UnsafeCell<u8>>();
+    assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static u8>();
+    assert::is_maybe_transmutable::<&'static mut UnsafeCell<u8>, &'static UnsafeCell<u8>>();
+}
diff --git a/tests/ui/transmutability/references/unsafecell.stderr b/tests/ui/transmutability/references/unsafecell.stderr
new file mode 100644
index 00000000000..651eb8ceb26
--- /dev/null
+++ b/tests/ui/transmutability/references/unsafecell.stderr
@@ -0,0 +1,33 @@
+error[E0277]: `&u8` cannot be safely transmuted into `&UnsafeCell<u8>`
+  --> $DIR/unsafecell.rs:27:50
+   |
+LL |     assert::is_maybe_transmutable::<&'static u8, &'static UnsafeCell<u8>>();
+   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell<u8>`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/unsafecell.rs:12:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this function
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: `&UnsafeCell<u8>` cannot be safely transmuted into `&UnsafeCell<u8>`
+  --> $DIR/unsafecell.rs:29:62
+   |
+LL |     assert::is_maybe_transmutable::<&'static UnsafeCell<u8>, &'static UnsafeCell<u8>>();
+   |                                                              ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Freeze` is not implemented for `&'static UnsafeCell<u8>`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/unsafecell.rs:12:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this function
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, { Assume::SAFETY }>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.rs b/tests/ui/transmutability/structs/repr/should_handle_all.rs
similarity index 54%
rename from tests/ui/transmutability/structs/repr/should_require_well_defined_layout.rs
rename to tests/ui/transmutability/structs/repr/should_handle_all.rs
index 2e673601baf..52c24eecf12 100644
--- a/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.rs
+++ b/tests/ui/transmutability/structs/repr/should_handle_all.rs
@@ -1,3 +1,4 @@
+//@ check-pass
 //! A struct must have a well-defined layout to participate in a transmutation.
 
 #![crate_type = "lib"]
@@ -20,47 +21,47 @@ mod assert {
     {}
 }
 
-fn should_reject_repr_rust()
+fn should_accept_repr_rust()
 {
     fn unit() {
         struct repr_rust;
-        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
-        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
+        assert::is_maybe_transmutable::<repr_rust, ()>();
+        assert::is_maybe_transmutable::<u128, repr_rust>();
     }
 
     fn tuple() {
         struct repr_rust();
-        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
-        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
+        assert::is_maybe_transmutable::<repr_rust, ()>();
+        assert::is_maybe_transmutable::<u128, repr_rust>();
     }
 
     fn braces() {
         struct repr_rust{}
-        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
-        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
+        assert::is_maybe_transmutable::<repr_rust, ()>();
+        assert::is_maybe_transmutable::<u128, repr_rust>();
     }
 
     fn aligned() {
         #[repr(align(1))] struct repr_rust{}
-        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
-        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
+        assert::is_maybe_transmutable::<repr_rust, ()>();
+        assert::is_maybe_transmutable::<u128, repr_rust>();
     }
 
     fn packed() {
         #[repr(packed)] struct repr_rust{}
-        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
-        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
+        assert::is_maybe_transmutable::<repr_rust, ()>();
+        assert::is_maybe_transmutable::<u128, repr_rust>();
     }
 
     fn nested() {
         struct repr_rust;
         #[repr(C)] struct repr_c(repr_rust);
-        assert::is_maybe_transmutable::<repr_c, ()>(); //~ ERROR cannot be safely transmuted
-        assert::is_maybe_transmutable::<u128, repr_c>(); //~ ERROR cannot be safely transmuted
+        assert::is_maybe_transmutable::<repr_c, ()>();
+        assert::is_maybe_transmutable::<u128, repr_c>();
     }
 }
 
-fn should_accept_repr_C()
+fn should_accept_repr_c()
 {
     fn unit() {
         #[repr(C)] struct repr_c;
diff --git a/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr b/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr
deleted file mode 100644
index 77788f72c21..00000000000
--- a/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr
+++ /dev/null
@@ -1,267 +0,0 @@
-error[E0277]: `should_reject_repr_rust::unit::repr_rust` cannot be safely transmuted into `()`
-  --> $DIR/should_require_well_defined_layout.rs:27:52
-   |
-LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `should_reject_repr_rust::unit::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::unit::repr_rust`
-  --> $DIR/should_require_well_defined_layout.rs:28:47
-   |
-LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::unit::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `should_reject_repr_rust::tuple::repr_rust` cannot be safely transmuted into `()`
-  --> $DIR/should_require_well_defined_layout.rs:33:52
-   |
-LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `should_reject_repr_rust::tuple::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::tuple::repr_rust`
-  --> $DIR/should_require_well_defined_layout.rs:34:47
-   |
-LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::tuple::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `should_reject_repr_rust::braces::repr_rust` cannot be safely transmuted into `()`
-  --> $DIR/should_require_well_defined_layout.rs:39:52
-   |
-LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `should_reject_repr_rust::braces::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::braces::repr_rust`
-  --> $DIR/should_require_well_defined_layout.rs:40:47
-   |
-LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::braces::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `aligned::repr_rust` cannot be safely transmuted into `()`
-  --> $DIR/should_require_well_defined_layout.rs:45:52
-   |
-LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `aligned::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `u128` cannot be safely transmuted into `aligned::repr_rust`
-  --> $DIR/should_require_well_defined_layout.rs:46:47
-   |
-LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `aligned::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `packed::repr_rust` cannot be safely transmuted into `()`
-  --> $DIR/should_require_well_defined_layout.rs:51:52
-   |
-LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                    ^^ analyzing the transmutability of `packed::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `u128` cannot be safely transmuted into `packed::repr_rust`
-  --> $DIR/should_require_well_defined_layout.rs:52:47
-   |
-LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ analyzing the transmutability of `packed::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `nested::repr_c` cannot be safely transmuted into `()`
-  --> $DIR/should_require_well_defined_layout.rs:58:49
-   |
-LL |         assert::is_maybe_transmutable::<repr_c, ()>();
-   |                                                 ^^ analyzing the transmutability of `nested::repr_c` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `u128` cannot be safely transmuted into `nested::repr_c`
-  --> $DIR/should_require_well_defined_layout.rs:59:47
-   |
-LL |         assert::is_maybe_transmutable::<u128, repr_c>();
-   |                                               ^^^^^^ analyzing the transmutability of `nested::repr_c` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error: aborting due to 12 previous errors
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs
index 4c285a616b3..64110753832 100644
--- a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs
+++ b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.rs
@@ -22,4 +22,5 @@ fn should_pad_explicitly_packed_field() {
     //~^ ERROR: recursive type
 
     assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
+    //~^ ERROR: cannot be safely transmuted
 }
diff --git a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr
index 7fb051f6625..ebfb5361143 100644
--- a/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr
+++ b/tests/ui/transmutability/structs/repr/transmute_infinitely_recursive_type.stderr
@@ -15,7 +15,22 @@ error[E0391]: cycle detected when computing layout of `should_pad_explicitly_pac
    = note: cycle used when evaluating trait selection obligation `(): core::mem::transmutability::BikeshedIntrinsicFrom<should_pad_explicitly_packed_field::ExplicitlyPadded, core::mem::transmutability::Assume { alignment: false, lifetimes: false, safety: false, validity: false }>`
    = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
 
-error: aborting due to 2 previous errors
+error[E0277]: `ExplicitlyPadded` cannot be safely transmuted into `()`
+  --> $DIR/transmute_infinitely_recursive_type.rs:24:55
+   |
+LL |     assert::is_maybe_transmutable::<ExplicitlyPadded, ()>();
+   |                                                       ^^ analyzing the transmutability of `ExplicitlyPadded` is not yet supported
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/transmute_infinitely_recursive_type.rs:14:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this function
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src>,
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
 
-Some errors have detailed explanations: E0072, E0391.
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0072, E0277, E0391.
 For more information about an error, try `rustc --explain E0072`.
diff --git a/tests/ui/transmutability/transmute-padding-ice.rs b/tests/ui/transmutability/transmute-padding-ice.rs
index 3f3f75bc086..f5935a0009e 100644
--- a/tests/ui/transmutability/transmute-padding-ice.rs
+++ b/tests/ui/transmutability/transmute-padding-ice.rs
@@ -1,7 +1,13 @@
+//@ check-pass
+//! This UI test was introduced as check-fail by a buggy bug-fix for an ICE. In
+//! fact, this transmutation should be valid.
+
 #![crate_type = "lib"]
 #![feature(transmutability)]
 #![allow(dead_code)]
 
+use std::mem::size_of;
+
 mod assert {
     use std::mem::{Assume, BikeshedIntrinsicFrom};
 
@@ -22,6 +28,7 @@ fn test() {
     #[repr(C)]
     struct B(u8, u8);
 
+    assert_eq!(size_of::<A>(), size_of::<B>());
+
     assert::is_maybe_transmutable::<B, A>();
-    //~^ ERROR cannot be safely transmuted
 }
diff --git a/tests/ui/transmutability/transmute-padding-ice.stderr b/tests/ui/transmutability/transmute-padding-ice.stderr
deleted file mode 100644
index 4c121d463c6..00000000000
--- a/tests/ui/transmutability/transmute-padding-ice.stderr
+++ /dev/null
@@ -1,22 +0,0 @@
-error[E0277]: `B` cannot be safely transmuted into `A`
-  --> $DIR/transmute-padding-ice.rs:25:40
-   |
-LL |     assert::is_maybe_transmutable::<B, A>();
-   |                                        ^ the size of `B` is smaller than the size of `A`
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/transmute-padding-ice.rs:10:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<
-   |  ______________^
-LL | |             Src,
-LL | |             { Assume { alignment: true, lifetimes: true, safety: true, validity: true } },
-LL | |         >,
-   | |_________^ required by this bound in `is_maybe_transmutable`
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/transmutability/uninhabited.rs b/tests/ui/transmutability/uninhabited.rs
new file mode 100644
index 00000000000..b61b110f6a1
--- /dev/null
+++ b/tests/ui/transmutability/uninhabited.rs
@@ -0,0 +1,71 @@
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, {
+            Assume {
+                alignment: true,
+                lifetimes: true,
+                safety: true,
+                validity: true,
+            }
+        }>
+    {}
+}
+
+fn void() {
+    enum Void {}
+
+    // This transmutation is vacuously acceptable; since one cannot construct a
+    // `Void`, unsoundness cannot directly arise from transmuting a void into
+    // anything else.
+    assert::is_maybe_transmutable::<Void, u128>();
+
+    assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
+}
+
+// Non-ZST uninhabited types are, nonetheless, uninhabited.
+fn yawning_void() {
+    enum Void {}
+
+    struct YawningVoid(Void, u128);
+
+    const _: () = {
+        assert!(std::mem::size_of::<YawningVoid>() == std::mem::size_of::<u128>());
+        // Just to be sure the above constant actually evaluated:
+        assert!(false); //~ ERROR: evaluation of constant value failed
+    };
+
+    // This transmutation is vacuously acceptable; since one cannot construct a
+    // `Void`, unsoundness cannot directly arise from transmuting a void into
+    // anything else.
+    assert::is_maybe_transmutable::<YawningVoid, u128>();
+
+    assert::is_maybe_transmutable::<(), Void>(); //~ ERROR: cannot be safely transmuted
+}
+
+// References to uninhabited types are, logically, uninhabited, but for layout
+// purposes are not ZSTs, and aren't treated as uninhabited when they appear in
+// enum variants.
+fn distant_void() {
+    enum Void {}
+
+    enum DistantVoid {
+        A(&'static Void)
+    }
+
+    const _: () = {
+        assert!(std::mem::size_of::<DistantVoid>() == std::mem::size_of::<usize>());
+        // Just to be sure the above constant actually evaluated:
+        assert!(false); //~ ERROR: evaluation of constant value failed
+    };
+
+    assert::is_maybe_transmutable::<DistantVoid, ()>();
+    assert::is_maybe_transmutable::<DistantVoid, &'static Void>();
+    assert::is_maybe_transmutable::<u128, DistantVoid>(); //~ ERROR: cannot be safely transmuted
+}
diff --git a/tests/ui/transmutability/uninhabited.stderr b/tests/ui/transmutability/uninhabited.stderr
new file mode 100644
index 00000000000..60219b0f263
--- /dev/null
+++ b/tests/ui/transmutability/uninhabited.stderr
@@ -0,0 +1,86 @@
+error[E0080]: evaluation of constant value failed
+  --> $DIR/uninhabited.rs:41:9
+   |
+LL |         assert!(false);
+   |         ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:41:9
+   |
+   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0080]: evaluation of constant value failed
+  --> $DIR/uninhabited.rs:65:9
+   |
+LL |         assert!(false);
+   |         ^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: false', $DIR/uninhabited.rs:65:9
+   |
+   = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0277]: `()` cannot be safely transmuted into `void::Void`
+  --> $DIR/uninhabited.rs:29:41
+   |
+LL |     assert::is_maybe_transmutable::<(), Void>();
+   |                                         ^^^^ `void::Void` is uninhabited
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/uninhabited.rs:10:14
+   |
+LL |       pub fn is_maybe_transmutable<Src, Dst>()
+   |              --------------------- required by a bound in this function
+LL |       where
+LL |           Dst: BikeshedIntrinsicFrom<Src, {
+   |  ______________^
+LL | |             Assume {
+LL | |                 alignment: true,
+LL | |                 lifetimes: true,
+...  |
+LL | |             }
+LL | |         }>
+   | |__________^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: `()` cannot be safely transmuted into `yawning_void::Void`
+  --> $DIR/uninhabited.rs:49:41
+   |
+LL |     assert::is_maybe_transmutable::<(), Void>();
+   |                                         ^^^^ `yawning_void::Void` is uninhabited
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/uninhabited.rs:10:14
+   |
+LL |       pub fn is_maybe_transmutable<Src, Dst>()
+   |              --------------------- required by a bound in this function
+LL |       where
+LL |           Dst: BikeshedIntrinsicFrom<Src, {
+   |  ______________^
+LL | |             Assume {
+LL | |                 alignment: true,
+LL | |                 lifetimes: true,
+...  |
+LL | |             }
+LL | |         }>
+   | |__________^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: `u128` cannot be safely transmuted into `DistantVoid`
+  --> $DIR/uninhabited.rs:70:43
+   |
+LL |     assert::is_maybe_transmutable::<u128, DistantVoid>();
+   |                                           ^^^^^^^^^^^ at least one value of `u128` isn't a bit-valid value of `DistantVoid`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/uninhabited.rs:10:14
+   |
+LL |       pub fn is_maybe_transmutable<Src, Dst>()
+   |              --------------------- required by a bound in this function
+LL |       where
+LL |           Dst: BikeshedIntrinsicFrom<Src, {
+   |  ______________^
+LL | |             Assume {
+LL | |                 alignment: true,
+LL | |                 lifetimes: true,
+...  |
+LL | |             }
+LL | |         }>
+   | |__________^ required by this bound in `is_maybe_transmutable`
+
+error: aborting due to 5 previous errors
+
+Some errors have detailed explanations: E0080, E0277.
+For more information about an error, try `rustc --explain E0080`.
diff --git a/tests/ui/transmutability/unions/repr/should_handle_align.rs b/tests/ui/transmutability/unions/repr/should_handle_align.rs
index 8668cca3cb5..ba4e904e161 100644
--- a/tests/ui/transmutability/unions/repr/should_handle_align.rs
+++ b/tests/ui/transmutability/unions/repr/should_handle_align.rs
@@ -25,13 +25,13 @@ fn should_pad_explicitly_aligned_field() {
     #[derive(Clone, Copy)] #[repr(u8)] enum V0u8 { V = 0 }
     #[derive(Clone, Copy)] #[repr(u8)] enum V1u8 { V = 1 }
 
-    #[repr(C)]
+    #[repr(align(1))]
     pub union Uninit {
         a: (),
         b: V1u8,
     }
 
-    #[repr(C, align(2))]
+    #[repr(align(2))]
     pub union align_2 {
         a: V0u8,
     }
diff --git a/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.rs b/tests/ui/transmutability/unions/repr/should_handle_all.rs
similarity index 58%
rename from tests/ui/transmutability/unions/repr/should_require_well_defined_layout.rs
rename to tests/ui/transmutability/unions/repr/should_handle_all.rs
index 8495b0ea88f..85d48dd9b7f 100644
--- a/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.rs
+++ b/tests/ui/transmutability/unions/repr/should_handle_all.rs
@@ -1,7 +1,7 @@
-//! A struct must have a well-defined layout to participate in a transmutation.
+//@ check-pass
 
 #![crate_type = "lib"]
-#![feature(transmutability)]
+#![feature(transmutability, transparent_unions)]
 #![allow(dead_code, incomplete_features, non_camel_case_types)]
 
 mod assert {
@@ -20,17 +20,17 @@ mod assert {
     {}
 }
 
-fn should_reject_repr_rust()
+fn should_accept_repr_rust()
 {
     union repr_rust {
         a: u8
     }
 
-    assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR cannot be safely transmuted
-    assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR cannot be safely transmuted
+    assert::is_maybe_transmutable::<repr_rust, ()>();
+    assert::is_maybe_transmutable::<u128, repr_rust>();
 }
 
-fn should_accept_repr_C()
+fn should_accept_repr_c()
 {
     #[repr(C)]
     union repr_c {
@@ -41,3 +41,15 @@ fn should_accept_repr_C()
     assert::is_maybe_transmutable::<repr_c, ()>();
     assert::is_maybe_transmutable::<u128, repr_c>();
 }
+
+
+fn should_accept_transparent()
+{
+    #[repr(transparent)]
+    union repr_transparent {
+        a: u8
+    }
+
+    assert::is_maybe_transmutable::<repr_transparent, ()>();
+    assert::is_maybe_transmutable::<u128, repr_transparent>();
+}
diff --git a/tests/ui/transmutability/unions/repr/should_handle_packed.rs b/tests/ui/transmutability/unions/repr/should_handle_packed.rs
index 4af6c1d3a61..fc06eba4353 100644
--- a/tests/ui/transmutability/unions/repr/should_handle_packed.rs
+++ b/tests/ui/transmutability/unions/repr/should_handle_packed.rs
@@ -27,7 +27,6 @@ fn should_pad_explicitly_packed_field() {
     #[derive(Clone, Copy)] #[repr(u8)] enum V2u8 { V = 2 }
     #[derive(Clone, Copy)] #[repr(u32)] enum V3u32 { V = 3 }
 
-    #[repr(C)]
     pub union Uninit {
         a: (),
         b: V1u8,
diff --git a/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr b/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr
deleted file mode 100644
index bec07f13103..00000000000
--- a/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr
+++ /dev/null
@@ -1,47 +0,0 @@
-error[E0277]: `should_reject_repr_rust::repr_rust` cannot be safely transmuted into `()`
-  --> $DIR/should_require_well_defined_layout.rs:29:48
-   |
-LL |     assert::is_maybe_transmutable::<repr_rust, ()>();
-   |                                                ^^ analyzing the transmutability of `should_reject_repr_rust::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::repr_rust`
-  --> $DIR/should_require_well_defined_layout.rs:30:43
-   |
-LL |     assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                           ^^^^^^^^^ analyzing the transmutability of `should_reject_repr_rust::repr_rust` is not yet supported.
-   |
-note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/should_require_well_defined_layout.rs:12:14
-   |
-LL |       pub fn is_maybe_transmutable<Src, Dst>()
-   |              --------------------- required by a bound in this function
-LL |       where
-LL |           Dst: BikeshedIntrinsicFrom<Src, {
-   |  ______________^
-LL | |             Assume {
-LL | |                 alignment: true,
-LL | |                 lifetimes: true,
-...  |
-LL | |             }
-LL | |         }>
-   | |__________^ required by this bound in `is_maybe_transmutable`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0277`.