From 8f1cec8d8472c3ffacedd4783c64182a407c72df Mon Sep 17 00:00:00 2001
From: Bryan Garza <1396101+bryangarza@users.noreply.github.com>
Date: Fri, 21 Apr 2023 16:49:36 -0700
Subject: [PATCH] Safe Transmute: Enable handling references, including
 recursive types

This patch enables support for references in Safe Transmute, by generating
nested obligations during trait selection. Specifically, when we call
`confirm_transmutability_candidate(...)`, we now recursively traverse the
`rustc_transmute::Answer` tree and create obligations for all the `Answer`
variants, some of which include multiple nested `Answer`s.

Also, to handle recursive types, enable support for coinduction for the Safe
Transmute trait (`BikeshedIntrinsicFrom`) by adding the `#[rustc_coinduction]`
annotation.

Also fix some small logic issues when reducing the `or` and `and` combinations
in `rustc_transmute`, so that we don't end up with additional redundant
`Answer`s in the tree.

Co-authored-by: Jack Wrenn <jack@wrenn.fyi>
---
 .../src/traits/error_reporting/mod.rs         |  10 +-
 .../src/traits/select/confirmation.rs         |  61 ++++++-
 compiler/rustc_transmute/src/layout/mod.rs    |  37 ++--
 compiler/rustc_transmute/src/layout/tree.rs   |  11 ++
 compiler/rustc_transmute/src/lib.rs           |  11 +-
 .../src/maybe_transmutable/mod.rs             | 170 ++++++++++++++----
 library/core/src/mem/transmutability.rs       |   1 +
 .../should_require_well_defined_layout.stderr |   6 +-
 ...ve_reprs_should_have_correct_length.stderr |  20 +--
 .../should_require_well_defined_layout.stderr |   6 +-
 .../enums/should_pad_variants.stderr          |   2 +-
 .../recursive-wrapper-types-bit-compatible.rs |  27 +++
 ...ursive-wrapper-types-bit-compatible.stderr |  25 +++
 ...ecursive-wrapper-types-bit-incompatible.rs |  25 +++
 ...sive-wrapper-types-bit-incompatible.stderr |  25 +++
 .../references/recursive-wrapper-types.rs     |  26 +++
 .../u8-to-unit.rs}                            |  12 +-
 .../u8-to-unit.stderr}                        |  10 +-
 .../references/unit-to-itself.rs              |  24 +++
 .../transmutability/references/unit-to-u8.rs  |  24 +++
 .../unit-to-u8.stderr}                        |  10 +-
 tests/ui/transmutability/region-infer.stderr  |   2 +-
 .../should_require_well_defined_layout.stderr |  12 +-
 .../should_require_well_defined_layout.stderr |   2 +-
 .../unions/should_pad_variants.stderr         |   2 +-
 .../ui/transmute/transmute-padding-ice.stderr |   2 +-
 26 files changed, 460 insertions(+), 103 deletions(-)
 create mode 100644 tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs
 create mode 100644 tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.stderr
 create mode 100644 tests/ui/transmutability/references/recursive-wrapper-types-bit-incompatible.rs
 create mode 100644 tests/ui/transmutability/references/recursive-wrapper-types-bit-incompatible.stderr
 create mode 100644 tests/ui/transmutability/references/recursive-wrapper-types.rs
 rename tests/ui/transmutability/{references.rs => references/u8-to-unit.rs} (55%)
 rename tests/ui/transmutability/{references.next.stderr => references/u8-to-unit.stderr} (63%)
 create mode 100644 tests/ui/transmutability/references/unit-to-itself.rs
 create mode 100644 tests/ui/transmutability/references/unit-to-u8.rs
 rename tests/ui/transmutability/{references.current.stderr => references/unit-to-u8.stderr} (76%)

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 dc43a3d154a..7e132e0ab60 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -2783,6 +2783,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     rustc_transmute::Reason::DstIsTooBig => {
                         format!("The size of `{src}` is smaller than the size of `{dst}`")
                     }
+                    rustc_transmute::Reason::DstHasStricterAlignment => {
+                        format!(
+                            "The alignment of `{src}` should be stricter than that of `{dst}`, but it is not"
+                        )
+                    }
+                    rustc_transmute::Reason::DstIsMoreUnique => {
+                        format!("`{src}` is a shared reference, but `{dst}` is a unique reference")
+                    }
                 };
                 (custom_err_msg, Some(reason_msg))
             }
@@ -2791,7 +2799,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 span,
                 "Inconsistent rustc_transmute::is_transmutable(...) result, got Yes",
             ),
-            _ => span_bug!(span, "Unsupported rustc_transmute::Reason variant"),
+            other => span_bug!(span, "Unsupported rustc_transmute::Answer variant: `{other:?}`"),
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 0d9f55d4c2e..9b28873f709 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -13,7 +13,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
 use rustc_middle::traits::SelectionOutputTypeParameterMismatch;
 use rustc_middle::ty::{
     self, Binder, GenericParamDefKind, InternalSubsts, SubstsRef, ToPolyTraitRef, ToPredicate,
-    TraitRef, Ty, TyCtxt, TypeVisitableExt,
+    TraitPredicate, TraitRef, Ty, TyCtxt, TypeVisitableExt,
 };
 use rustc_session::config::TraitSolver;
 use rustc_span::def_id::DefId;
@@ -279,10 +279,61 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         ImplSourceBuiltinData { nested: obligations }
     }
 
+    #[instrument(skip(self))]
     fn confirm_transmutability_candidate(
         &mut self,
         obligation: &TraitObligation<'tcx>,
     ) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        fn flatten_answer_tree<'tcx>(
+            tcx: TyCtxt<'tcx>,
+            obligation: &TraitObligation<'tcx>,
+            predicate: TraitPredicate<'tcx>,
+            answer: rustc_transmute::Answer<rustc_transmute::layout::rustc::Ref<'tcx>>,
+        ) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+            match answer {
+                rustc_transmute::Answer::Yes => Ok(vec![]),
+                rustc_transmute::Answer::No(_) => Err(Unimplemented),
+                // FIXME(bryangarza): Add separate `IfAny` case, instead of treating as `IfAll`
+                rustc_transmute::Answer::IfAll(answers)
+                | rustc_transmute::Answer::IfAny(answers) => {
+                    let mut nested = vec![];
+                    for flattened in answers
+                        .into_iter()
+                        .map(|answer| flatten_answer_tree(tcx, obligation, predicate, answer))
+                    {
+                        nested.extend(flattened?);
+                    }
+                    Ok(nested)
+                }
+                rustc_transmute::Answer::IfTransmutable { src, dst } => {
+                    let trait_def_id = obligation.predicate.def_id();
+                    let scope = predicate.trait_ref.substs.type_at(2);
+                    let assume_const = predicate.trait_ref.substs.const_at(3);
+                    let make_obl = |from_ty, to_ty| {
+                        let trait_ref1 = tcx.mk_trait_ref(
+                            trait_def_id,
+                            [
+                                ty::GenericArg::from(to_ty),
+                                ty::GenericArg::from(from_ty),
+                                ty::GenericArg::from(scope),
+                                ty::GenericArg::from(assume_const),
+                            ],
+                        );
+                        Obligation::with_depth(
+                            tcx,
+                            obligation.cause.clone(),
+                            obligation.recursion_depth + 1,
+                            obligation.param_env,
+                            trait_ref1,
+                        )
+                    };
+
+                    // // FIXME(bryangarza): Check src.mutability or dst.mutability to know whether dst -> src obligation is needed
+                    Ok(vec![make_obl(src.ty, dst.ty), make_obl(dst.ty, src.ty)])
+                }
+            }
+        }
+
         debug!(?obligation, "confirm_transmutability_candidate");
 
         // We erase regions here because transmutability calls layout queries,
@@ -312,10 +363,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             assume,
         );
 
-        match maybe_transmutable {
-            rustc_transmute::Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
-            _ => Err(Unimplemented),
-        }
+        info!(?maybe_transmutable);
+        let nested = flatten_answer_tree(self.tcx(), obligation, predicate, maybe_transmutable)?;
+        info!(?nested);
+        Ok(ImplSourceBuiltinData { nested })
     }
 
     /// This handles the case where an `auto trait Foo` impl is being used.
diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs
index f8d05bc89d2..b318447e581 100644
--- a/compiler/rustc_transmute/src/layout/mod.rs
+++ b/compiler/rustc_transmute/src/layout/mod.rs
@@ -30,33 +30,46 @@ impl fmt::Debug for Byte {
 }
 
 pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {}
-pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {}
+pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
+    fn min_align(&self) -> usize {
+        1
+    }
+
+    fn is_mutable(&self) -> bool {
+        false
+    }
+}
 
 impl Def for ! {}
 impl Ref for ! {}
 
 #[cfg(feature = "rustc")]
-pub(crate) mod rustc {
+pub mod rustc {
     use rustc_middle::mir::Mutability;
-    use rustc_middle::ty;
-    use rustc_middle::ty::Region;
-    use rustc_middle::ty::Ty;
+    use rustc_middle::ty::{self, Ty};
 
     /// A reference in the layout.
     #[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
     pub struct Ref<'tcx> {
-        lifetime: Region<'tcx>,
-        ty: Ty<'tcx>,
-        mutability: Mutability,
+        pub lifetime: ty::Region<'tcx>,
+        pub ty: Ty<'tcx>,
+        pub mutability: Mutability,
+        pub align: usize,
     }
 
-    impl<'tcx> super::Ref for Ref<'tcx> {}
+    impl<'tcx> super::Ref for Ref<'tcx> {
+        fn min_align(&self) -> usize {
+            self.align
+        }
 
-    impl<'tcx> Ref<'tcx> {
-        pub fn min_align(&self) -> usize {
-            todo!()
+        fn is_mutable(&self) -> bool {
+            match self.mutability {
+                Mutability::Mut => true,
+                Mutability::Not => false,
+            }
         }
     }
+    impl<'tcx> Ref<'tcx> {}
 
     /// A visibility node in the layout.
     #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index a6d88b1342a..ed9309b015d 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -365,6 +365,17 @@ pub(crate) mod rustc {
                         }
                     }))
                 }
+
+                ty::Ref(lifetime, ty, mutability) => {
+                    let align = layout_of(tcx, *ty)?.align();
+                    Ok(Tree::Ref(Ref {
+                        lifetime: *lifetime,
+                        ty: *ty,
+                        mutability: *mutability,
+                        align,
+                    }))
+                }
+
                 _ => Err(Err::Unspecified),
             }
         }
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index 77c0526e3aa..c4a99d9eb89 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -8,7 +8,7 @@ extern crate tracing;
 
 pub(crate) use rustc_data_structures::fx::{FxIndexMap as Map, FxIndexSet as Set};
 
-pub(crate) mod layout;
+pub mod layout;
 pub(crate) mod maybe_transmutable;
 
 #[derive(Default)]
@@ -21,10 +21,7 @@ pub struct Assume {
 
 /// The type encodes answers to the question: "Are these types transmutable?"
 #[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
-pub enum Answer<R>
-where
-    R: layout::Ref,
-{
+pub enum Answer<R> {
     /// `Src` is transmutable into `Dst`.
     Yes,
 
@@ -54,6 +51,10 @@ pub enum Reason {
     DstIsPrivate,
     /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
     DstIsTooBig,
+    /// Src should have a stricter alignment than Dst, but it does not.
+    DstHasStricterAlignment,
+    /// Can't go from shared pointer to unique pointer
+    DstIsMoreUnique,
 }
 
 #[cfg(feature = "rustc")]
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
index 2e2fb90e71c..d1077488c79 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
@@ -1,13 +1,13 @@
-use crate::Map;
-use crate::{Answer, Reason};
-
+pub(crate) mod query_context;
 #[cfg(test)]
 mod tests;
 
-mod query_context;
-use query_context::QueryContext;
+use crate::{
+    layout::{self, dfa, Byte, Dfa, Nfa, Ref, Tree, Uninhabited},
+    maybe_transmutable::query_context::QueryContext,
+    Answer, Map, Reason,
+};
 
-use crate::layout::{self, dfa, Byte, Dfa, Nfa, Tree, Uninhabited};
 pub(crate) struct MaybeTransmutableQuery<L, C>
 where
     C: QueryContext,
@@ -53,6 +53,7 @@ where
     }
 }
 
+// FIXME: Nix this cfg, so we can write unit tests independently of rustc
 #[cfg(feature = "rustc")]
 mod rustc {
     use super::*;
@@ -77,12 +78,11 @@ mod rustc {
                 match (src, dst) {
                     // Answer `Yes` here, because 'unknown layout' and type errors will already
                     // be reported by rustc. No need to spam the user with more errors.
-                    (Err(Err::TypeError(_)), _) => Err(Answer::Yes),
-                    (_, Err(Err::TypeError(_))) => Err(Answer::Yes),
-                    (Err(Err::Unknown), _) => Err(Answer::Yes),
-                    (_, Err(Err::Unknown)) => Err(Answer::Yes),
-                    (Err(Err::Unspecified), _) => Err(Answer::No(Reason::SrcIsUnspecified)),
-                    (_, Err(Err::Unspecified)) => Err(Answer::No(Reason::DstIsUnspecified)),
+                    (Err(Err::TypeError(_)), _) | (_, Err(Err::TypeError(_))) => Err(Answer::Yes),
+                    (Err(Err::Unknown), _) | (_, Err(Err::Unknown)) => Err(Answer::Yes),
+                    (Err(Err::Unspecified), _) | (_, Err(Err::Unspecified)) => {
+                        Err(Answer::No(Reason::SrcIsUnspecified))
+                    }
                     (Ok(src), Ok(dst)) => Ok((src, dst)),
                 }
             });
@@ -214,34 +214,99 @@ where
                     Answer::No(Reason::DstIsTooBig)
                 }
             } else {
-                let src_quantification = if self.assume.validity {
+                let src_quantifier = if self.assume.validity {
                     // if the compiler may assume that the programmer is doing additional validity checks,
                     // (e.g.: that `src != 3u8` when the destination type is `bool`)
                     // then there must exist at least one transition out of `src_state` such that the transmute is viable...
-                    there_exists
+                    Quantifier::ThereExists
                 } else {
                     // if the compiler cannot assume that the programmer is doing additional validity checks,
                     // then for all transitions out of `src_state`, such that the transmute is viable...
-                    // then there must exist at least one transition out of `src_state` such that the transmute is viable...
-                    for_all
+                    // then there must exist at least one transition out of `dst_state` such that the transmute is viable...
+                    Quantifier::ForAll
                 };
 
-                src_quantification(
-                    self.src.bytes_from(src_state).unwrap_or(&Map::default()),
-                    |(&src_validity, &src_state_prime)| {
-                        if let Some(dst_state_prime) = self.dst.byte_from(dst_state, src_validity) {
-                            self.answer_memo(cache, src_state_prime, dst_state_prime)
-                        } else if let Some(dst_state_prime) =
-                            self.dst.byte_from(dst_state, Byte::Uninit)
-                        {
-                            self.answer_memo(cache, src_state_prime, dst_state_prime)
-                        } else {
-                            Answer::No(Reason::DstIsBitIncompatible)
-                        }
-                    },
-                )
+                let bytes_answer = src_quantifier.apply(
+                    // for each of the byte transitions out of the `src_state`...
+                    self.src.bytes_from(src_state).unwrap_or(&Map::default()).into_iter().map(
+                        |(&src_validity, &src_state_prime)| {
+                            // ...try to find a matching transition out of `dst_state`.
+                            if let Some(dst_state_prime) =
+                                self.dst.byte_from(dst_state, src_validity)
+                            {
+                                self.answer_memo(cache, src_state_prime, dst_state_prime)
+                            } else if let Some(dst_state_prime) =
+                                // otherwise, see if `dst_state` has any outgoing `Uninit` transitions
+                                // (any init byte is a valid uninit byte)
+                                self.dst.byte_from(dst_state, Byte::Uninit)
+                            {
+                                self.answer_memo(cache, src_state_prime, dst_state_prime)
+                            } else {
+                                // otherwise, we've exhausted our options.
+                                // the DFAs, from this point onwards, are bit-incompatible.
+                                Answer::No(Reason::DstIsBitIncompatible)
+                            }
+                        },
+                    ),
+                );
+
+                // The below early returns reflect how this code would behave:
+                //   if self.assume.validity {
+                //       bytes_answer.or(refs_answer)
+                //   } else {
+                //       bytes_answer.and(refs_answer)
+                //   }
+                // ...if `refs_answer` was computed lazily. The below early
+                // returns can be deleted without impacting the correctness of
+                // the algoritm; only its performance.
+                match bytes_answer {
+                    Answer::No(..) if !self.assume.validity => return bytes_answer,
+                    Answer::Yes if self.assume.validity => return bytes_answer,
+                    _ => {}
+                };
+
+                let refs_answer = src_quantifier.apply(
+                    // for each reference transition out of `src_state`...
+                    self.src.refs_from(src_state).unwrap_or(&Map::default()).into_iter().map(
+                        |(&src_ref, &src_state_prime)| {
+                            // ...there exists a reference transition out of `dst_state`...
+                            Quantifier::ThereExists.apply(
+                                self.dst
+                                    .refs_from(dst_state)
+                                    .unwrap_or(&Map::default())
+                                    .into_iter()
+                                    .map(|(&dst_ref, &dst_state_prime)| {
+                                        if !src_ref.is_mutable() && dst_ref.is_mutable() {
+                                            Answer::No(Reason::DstIsMoreUnique)
+                                        } else if !self.assume.alignment
+                                            && src_ref.min_align() < dst_ref.min_align()
+                                        {
+                                            Answer::No(Reason::DstHasStricterAlignment)
+                                        } else {
+                                            // ...such that `src` is transmutable into `dst`, if
+                                            // `src_ref` is transmutability into `dst_ref`.
+                                            Answer::IfTransmutable { src: src_ref, dst: dst_ref }
+                                                .and(self.answer_memo(
+                                                    cache,
+                                                    src_state_prime,
+                                                    dst_state_prime,
+                                                ))
+                                        }
+                                    }),
+                            )
+                        },
+                    ),
+                );
+
+                if self.assume.validity {
+                    bytes_answer.or(refs_answer)
+                } else {
+                    bytes_answer.and(refs_answer)
+                }
             };
-            cache.insert((src_state, dst_state), answer.clone());
+            if let Some(..) = cache.insert((src_state, dst_state), answer.clone()) {
+                panic!("failed to correctly cache transmutability")
+            }
             answer
         }
     }
@@ -253,17 +318,21 @@ where
 {
     pub(crate) fn and(self, rhs: Self) -> Self {
         match (self, rhs) {
-            (Self::No(reason), _) | (_, Self::No(reason)) => Self::No(reason),
-            (Self::Yes, Self::Yes) => Self::Yes,
+            (_, Self::No(reason)) | (Self::No(reason), _) => Self::No(reason),
+
+            (Self::Yes, other) | (other, Self::Yes) => other,
+
             (Self::IfAll(mut lhs), Self::IfAll(ref mut rhs)) => {
                 lhs.append(rhs);
                 Self::IfAll(lhs)
             }
+
             (constraint, Self::IfAll(mut constraints))
             | (Self::IfAll(mut constraints), constraint) => {
                 constraints.push(constraint);
                 Self::IfAll(constraints)
             }
+
             (lhs, rhs) => Self::IfAll(vec![lhs, rhs]),
         }
     }
@@ -271,7 +340,7 @@ where
     pub(crate) fn or(self, rhs: Self) -> Self {
         match (self, rhs) {
             (Self::Yes, _) | (_, Self::Yes) => Self::Yes,
-            (Self::No(lhr), Self::No(rhr)) => Self::No(lhr),
+            (other, Self::No(reason)) | (Self::No(reason), other) => other,
             (Self::IfAny(mut lhs), Self::IfAny(ref mut rhs)) => {
                 lhs.append(rhs);
                 Self::IfAny(lhs)
@@ -319,3 +388,36 @@ where
     );
     result
 }
+
+pub enum Quantifier {
+    ThereExists,
+    ForAll,
+}
+
+impl Quantifier {
+    pub fn apply<R, I>(&self, iter: I) -> Answer<R>
+    where
+        R: layout::Ref,
+        I: IntoIterator<Item = Answer<R>>,
+    {
+        use std::ops::ControlFlow::{Break, Continue};
+
+        let (init, try_fold_f): (_, fn(_, _) -> _) = match self {
+            Self::ThereExists => {
+                (Answer::No(Reason::DstIsBitIncompatible), |accum: Answer<R>, next| {
+                    match accum.or(next) {
+                        Answer::Yes => Break(Answer::Yes),
+                        maybe => Continue(maybe),
+                    }
+                })
+            }
+            Self::ForAll => (Answer::Yes, |accum: Answer<R>, next| match accum.and(next) {
+                Answer::No(reason) => Break(Answer::No(reason)),
+                maybe => Continue(maybe),
+            }),
+        };
+
+        let (Continue(result) | Break(result)) = iter.into_iter().try_fold(init, try_fold_f);
+        result
+    }
+}
diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs
index 87ae30619c6..d0c30e715d5 100644
--- a/library/core/src/mem/transmutability.rs
+++ b/library/core/src/mem/transmutability.rs
@@ -5,6 +5,7 @@
 /// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied.
 #[unstable(feature = "transmutability", issue = "99571")]
 #[lang = "transmute_trait"]
+#[rustc_coinductive]
 pub unsafe trait BikeshedIntrinsicFrom<Src, Context, const ASSUME: Assume = { Assume::NOTHING }>
 where
     Src: ?Sized,
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 1a0a5d3ae94..4a0cd176408 100644
--- a/tests/ui/transmutability/arrays/should_require_well_defined_layout.stderr
+++ b/tests/ui/transmutability/arrays/should_require_well_defined_layout.stderr
@@ -23,7 +23,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 0]` in the defin
   --> $DIR/should_require_well_defined_layout.rs:27:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `[String; 0]` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:13:14
@@ -65,7 +65,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 1]` in the defin
   --> $DIR/should_require_well_defined_layout.rs:33:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `[String; 1]` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:13:14
@@ -107,7 +107,7 @@ error[E0277]: `u128` cannot be safely transmuted into `[String; 2]` in the defin
   --> $DIR/should_require_well_defined_layout.rs:39:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `[String; 2]` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:13:14
diff --git a/tests/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.stderr b/tests/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.stderr
index 9877a6606a9..77d2dc4f50c 100644
--- a/tests/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.stderr
+++ b/tests/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.stderr
@@ -24,7 +24,7 @@ error[E0277]: `V0i8` cannot be safely transmuted into `u16` in the defining scop
   --> $DIR/primitive_reprs_should_have_correct_length.rs:50:44
    |
 LL |         assert::is_transmutable::<Current, Larger, Context>();
-   |                                            ^^^^^^ The size of `V0i8` is smaller than the size of `u16`
+   |                                            ^^^^^^ At least one value of `V0i8` isn't a bit-valid value of `u16`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
@@ -68,7 +68,7 @@ error[E0277]: `V0u8` cannot be safely transmuted into `u16` in the defining scop
   --> $DIR/primitive_reprs_should_have_correct_length.rs:58:44
    |
 LL |         assert::is_transmutable::<Current, Larger, Context>();
-   |                                            ^^^^^^ The size of `V0u8` is smaller than the size of `u16`
+   |                                            ^^^^^^ At least one value of `V0u8` isn't a bit-valid value of `u16`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
@@ -112,7 +112,7 @@ error[E0277]: `V0i16` cannot be safely transmuted into `u32` in the defining sco
   --> $DIR/primitive_reprs_should_have_correct_length.rs:74:44
    |
 LL |         assert::is_transmutable::<Current, Larger, Context>();
-   |                                            ^^^^^^ The size of `V0i16` is smaller than the size of `u32`
+   |                                            ^^^^^^ At least one value of `V0i16` isn't a bit-valid value of `u32`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
@@ -156,7 +156,7 @@ error[E0277]: `V0u16` cannot be safely transmuted into `u32` in the defining sco
   --> $DIR/primitive_reprs_should_have_correct_length.rs:82:44
    |
 LL |         assert::is_transmutable::<Current, Larger, Context>();
-   |                                            ^^^^^^ The size of `V0u16` is smaller than the size of `u32`
+   |                                            ^^^^^^ At least one value of `V0u16` isn't a bit-valid value of `u32`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
@@ -200,7 +200,7 @@ error[E0277]: `V0i32` cannot be safely transmuted into `u64` in the defining sco
   --> $DIR/primitive_reprs_should_have_correct_length.rs:98:44
    |
 LL |         assert::is_transmutable::<Current, Larger, Context>();
-   |                                            ^^^^^^ The size of `V0i32` is smaller than the size of `u64`
+   |                                            ^^^^^^ At least one value of `V0i32` isn't a bit-valid value of `u64`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
@@ -244,7 +244,7 @@ error[E0277]: `V0u32` cannot be safely transmuted into `u64` in the defining sco
   --> $DIR/primitive_reprs_should_have_correct_length.rs:106:44
    |
 LL |         assert::is_transmutable::<Current, Larger, Context>();
-   |                                            ^^^^^^ The size of `V0u32` is smaller than the size of `u64`
+   |                                            ^^^^^^ At least one value of `V0u32` isn't a bit-valid value of `u64`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
@@ -288,7 +288,7 @@ error[E0277]: `V0i64` cannot be safely transmuted into `u128` in the defining sc
   --> $DIR/primitive_reprs_should_have_correct_length.rs:122:44
    |
 LL |         assert::is_transmutable::<Current, Larger, Context>();
-   |                                            ^^^^^^ The size of `V0i64` is smaller than the size of `u128`
+   |                                            ^^^^^^ At least one value of `V0i64` isn't a bit-valid value of `u128`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
@@ -332,7 +332,7 @@ error[E0277]: `V0u64` cannot be safely transmuted into `u128` in the defining sc
   --> $DIR/primitive_reprs_should_have_correct_length.rs:130:44
    |
 LL |         assert::is_transmutable::<Current, Larger, Context>();
-   |                                            ^^^^^^ The size of `V0u64` is smaller than the size of `u128`
+   |                                            ^^^^^^ At least one value of `V0u64` isn't a bit-valid value of `u128`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
@@ -376,7 +376,7 @@ error[E0277]: `V0isize` cannot be safely transmuted into `[usize; 2]` in the def
   --> $DIR/primitive_reprs_should_have_correct_length.rs:146:44
    |
 LL |         assert::is_transmutable::<Current, Larger, Context>();
-   |                                            ^^^^^^ The size of `V0isize` is smaller than the size of `[usize; 2]`
+   |                                            ^^^^^^ At least one value of `V0isize` isn't a bit-valid value of `[usize; 2]`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
@@ -420,7 +420,7 @@ error[E0277]: `V0usize` cannot be safely transmuted into `[usize; 2]` in the def
   --> $DIR/primitive_reprs_should_have_correct_length.rs:154:44
    |
 LL |         assert::is_transmutable::<Current, Larger, Context>();
-   |                                            ^^^^^^ The size of `V0usize` is smaller than the size of `[usize; 2]`
+   |                                            ^^^^^^ At least one value of `V0usize` isn't a bit-valid value of `[usize; 2]`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
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
index 1612b6b3661..6508d535b05 100644
--- a/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr
+++ b/tests/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr
@@ -24,7 +24,7 @@ error[E0277]: `u128` cannot be safely transmuted into `void::repr_rust` in the d
   --> $DIR/should_require_well_defined_layout.rs:29:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `void::repr_rust` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:14:14
@@ -68,7 +68,7 @@ error[E0277]: `u128` cannot be safely transmuted into `singleton::repr_rust` in
   --> $DIR/should_require_well_defined_layout.rs:35:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `singleton::repr_rust` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:14:14
@@ -112,7 +112,7 @@ error[E0277]: `u128` cannot be safely transmuted into `duplex::repr_rust` in the
   --> $DIR/should_require_well_defined_layout.rs:41:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `duplex::repr_rust` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:14:14
diff --git a/tests/ui/transmutability/enums/should_pad_variants.stderr b/tests/ui/transmutability/enums/should_pad_variants.stderr
index bfbef8b25fc..c82a7968022 100644
--- a/tests/ui/transmutability/enums/should_pad_variants.stderr
+++ b/tests/ui/transmutability/enums/should_pad_variants.stderr
@@ -2,7 +2,7 @@ error[E0277]: `Src` cannot be safely transmuted into `Dst` in the defining scope
   --> $DIR/should_pad_variants.rs:44:36
    |
 LL |     assert::is_transmutable::<Src, Dst, Context>();
-   |                                    ^^^ The size of `Src` is smaller than the size of `Dst`
+   |                                    ^^^ At least one value of `Src` isn't a bit-valid value of `Dst`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/should_pad_variants.rs:13:14
diff --git a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs
new file mode 100644
index 00000000000..918147a0862
--- /dev/null
+++ b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.rs
@@ -0,0 +1,27 @@
+// check-fail
+#![feature(transmutability)]
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, {
+            Assume {
+                alignment: true,
+                lifetimes: false,
+                safety: true,
+                validity: false,
+            }
+        }>
+    {}
+}
+
+fn main() {
+    #[repr(C)] struct A(bool, &'static A);
+    #[repr(C)] struct B(u8, &'static B);
+    // FIXME(bryangarza): Make 2 variants of this test, depending on mutability.
+    // Right now, we are being strict by default and checking A->B and B->A both.
+    assert::is_maybe_transmutable::<&'static A, &'static B>(); //~ ERROR `B` cannot be safely transmuted into `A`
+}
diff --git a/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.stderr b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.stderr
new file mode 100644
index 00000000000..fac0e4f032e
--- /dev/null
+++ b/tests/ui/transmutability/references/recursive-wrapper-types-bit-compatible.stderr
@@ -0,0 +1,25 @@
+error[E0277]: `B` cannot be safely transmuted into `A` in the defining scope of `assert::Context`
+  --> $DIR/recursive-wrapper-types-bit-compatible.rs:26:49
+   |
+LL |     assert::is_maybe_transmutable::<&'static A, &'static B>();
+   |                                                 ^^^^^^^^^^ At least one value of `B` isn't a bit-valid value of `A`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/recursive-wrapper-types-bit-compatible.rs:10:14
+   |
+LL |       pub fn is_maybe_transmutable<Src, Dst>()
+   |              --------------------- required by a bound in this function
+LL |       where
+LL |           Dst: BikeshedIntrinsicFrom<Src, Context, {
+   |  ______________^
+LL | |             Assume {
+LL | |                 alignment: true,
+LL | |                 lifetimes: false,
+...  |
+LL | |             }
+LL | |         }>
+   | |__________^ required by this bound in `is_maybe_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/transmutability/references/recursive-wrapper-types-bit-incompatible.rs b/tests/ui/transmutability/references/recursive-wrapper-types-bit-incompatible.rs
new file mode 100644
index 00000000000..6dcb7df9feb
--- /dev/null
+++ b/tests/ui/transmutability/references/recursive-wrapper-types-bit-incompatible.rs
@@ -0,0 +1,25 @@
+// check-fail
+#![feature(transmutability)]
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, {
+            Assume {
+                alignment: true,
+                lifetimes: false,
+                safety: true,
+                validity: false,
+            }
+        }>
+    {}
+}
+
+fn main() {
+    #[repr(C)] struct A(bool, &'static A);
+    #[repr(C)] struct B(u8, &'static B);
+    assert::is_maybe_transmutable::<&'static B, &'static A>(); //~ ERROR `B` cannot be safely transmuted into `A`
+}
diff --git a/tests/ui/transmutability/references/recursive-wrapper-types-bit-incompatible.stderr b/tests/ui/transmutability/references/recursive-wrapper-types-bit-incompatible.stderr
new file mode 100644
index 00000000000..ecfe4865962
--- /dev/null
+++ b/tests/ui/transmutability/references/recursive-wrapper-types-bit-incompatible.stderr
@@ -0,0 +1,25 @@
+error[E0277]: `B` cannot be safely transmuted into `A` in the defining scope of `assert::Context`
+  --> $DIR/recursive-wrapper-types-bit-incompatible.rs:24:49
+   |
+LL |     assert::is_maybe_transmutable::<&'static B, &'static A>();
+   |                                                 ^^^^^^^^^^ At least one value of `B` isn't a bit-valid value of `A`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/recursive-wrapper-types-bit-incompatible.rs:10:14
+   |
+LL |       pub fn is_maybe_transmutable<Src, Dst>()
+   |              --------------------- required by a bound in this function
+LL |       where
+LL |           Dst: BikeshedIntrinsicFrom<Src, Context, {
+   |  ______________^
+LL | |             Assume {
+LL | |                 alignment: true,
+LL | |                 lifetimes: false,
+...  |
+LL | |             }
+LL | |         }>
+   | |__________^ required by this bound in `is_maybe_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/transmutability/references/recursive-wrapper-types.rs b/tests/ui/transmutability/references/recursive-wrapper-types.rs
new file mode 100644
index 00000000000..090c1fea6db
--- /dev/null
+++ b/tests/ui/transmutability/references/recursive-wrapper-types.rs
@@ -0,0 +1,26 @@
+// check-pass
+#![feature(transmutability)]
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, {
+            Assume {
+                alignment: true,
+                lifetimes: false,
+                safety: true,
+                validity: false,
+            }
+        }>
+    {}
+}
+
+fn main() {
+    #[repr(C)] struct A(&'static B);
+    #[repr(C)] struct B(&'static A);
+    assert::is_maybe_transmutable::<&'static A, &'static B>();
+    assert::is_maybe_transmutable::<&'static B, &'static A>();
+}
diff --git a/tests/ui/transmutability/references.rs b/tests/ui/transmutability/references/u8-to-unit.rs
similarity index 55%
rename from tests/ui/transmutability/references.rs
rename to tests/ui/transmutability/references/u8-to-unit.rs
index 8c2b25ebba1..b7dd638b952 100644
--- a/tests/ui/transmutability/references.rs
+++ b/tests/ui/transmutability/references/u8-to-unit.rs
@@ -1,11 +1,5 @@
-// revisions: current next
-//[next] compile-flags: -Ztrait-solver=next
-
-//! Transmutations involving references are not yet supported.
-
-#![crate_type = "lib"]
+// check-fail
 #![feature(transmutability)]
-#![allow(dead_code, incomplete_features, non_camel_case_types)]
 
 mod assert {
     use std::mem::{Assume, BikeshedIntrinsicFrom};
@@ -24,7 +18,7 @@ mod assert {
     {}
 }
 
-fn not_yet_implemented() {
+fn main() {
     #[repr(C)] struct Unit;
-    assert::is_maybe_transmutable::<&'static Unit, &'static Unit>(); //~ ERROR cannot be safely transmuted
+    assert::is_maybe_transmutable::<&'static u8, &'static Unit>(); //~ ERROR `Unit` cannot be safely transmuted into `u8`
 }
diff --git a/tests/ui/transmutability/references.next.stderr b/tests/ui/transmutability/references/u8-to-unit.stderr
similarity index 63%
rename from tests/ui/transmutability/references.next.stderr
rename to tests/ui/transmutability/references/u8-to-unit.stderr
index 819c9b92bc8..81b0b57f0cf 100644
--- a/tests/ui/transmutability/references.next.stderr
+++ b/tests/ui/transmutability/references/u8-to-unit.stderr
@@ -1,11 +1,11 @@
-error[E0277]: `&Unit` cannot be safely transmuted into `&Unit` in the defining scope of `assert::Context`
-  --> $DIR/references.rs:29:52
+error[E0277]: `Unit` cannot be safely transmuted into `u8` in the defining scope of `assert::Context`
+  --> $DIR/u8-to-unit.rs:23:50
    |
-LL |     assert::is_maybe_transmutable::<&'static Unit, &'static Unit>();
-   |                                                    ^^^^^^^^^^^^^ `&Unit` does not have a well-specified layout
+LL |     assert::is_maybe_transmutable::<&'static u8, &'static Unit>();
+   |                                                  ^^^^^^^^^^^^^ The size of `Unit` is smaller than the size of `u8`
    |
 note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/references.rs:16:14
+  --> $DIR/u8-to-unit.rs:10:14
    |
 LL |       pub fn is_maybe_transmutable<Src, Dst>()
    |              --------------------- required by a bound in this function
diff --git a/tests/ui/transmutability/references/unit-to-itself.rs b/tests/ui/transmutability/references/unit-to-itself.rs
new file mode 100644
index 00000000000..04a7e16d7cc
--- /dev/null
+++ b/tests/ui/transmutability/references/unit-to-itself.rs
@@ -0,0 +1,24 @@
+// check-pass
+#![feature(transmutability)]
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, {
+            Assume {
+                alignment: true,
+                lifetimes: false,
+                safety: true,
+                validity: false,
+            }
+        }>
+    {}
+}
+
+fn main() {
+    #[repr(C)] struct Unit;
+    assert::is_maybe_transmutable::<&'static Unit, &'static Unit>();
+}
diff --git a/tests/ui/transmutability/references/unit-to-u8.rs b/tests/ui/transmutability/references/unit-to-u8.rs
new file mode 100644
index 00000000000..73e1db3d2d5
--- /dev/null
+++ b/tests/ui/transmutability/references/unit-to-u8.rs
@@ -0,0 +1,24 @@
+// check-fail
+#![feature(transmutability)]
+
+mod assert {
+    use std::mem::{Assume, BikeshedIntrinsicFrom};
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, {
+            Assume {
+                alignment: true,
+                lifetimes: true,
+                safety: true,
+                validity: true,
+            }
+        }>
+    {}
+}
+
+fn main() {
+    #[repr(C)] struct Unit;
+    assert::is_maybe_transmutable::<&'static Unit, &'static u8>(); //~ ERROR `Unit` cannot be safely transmuted into `u8`
+}
diff --git a/tests/ui/transmutability/references.current.stderr b/tests/ui/transmutability/references/unit-to-u8.stderr
similarity index 76%
rename from tests/ui/transmutability/references.current.stderr
rename to tests/ui/transmutability/references/unit-to-u8.stderr
index 819c9b92bc8..f2b72357f79 100644
--- a/tests/ui/transmutability/references.current.stderr
+++ b/tests/ui/transmutability/references/unit-to-u8.stderr
@@ -1,11 +1,11 @@
-error[E0277]: `&Unit` cannot be safely transmuted into `&Unit` in the defining scope of `assert::Context`
-  --> $DIR/references.rs:29:52
+error[E0277]: `Unit` cannot be safely transmuted into `u8` in the defining scope of `assert::Context`
+  --> $DIR/unit-to-u8.rs:23:52
    |
-LL |     assert::is_maybe_transmutable::<&'static Unit, &'static Unit>();
-   |                                                    ^^^^^^^^^^^^^ `&Unit` does not have a well-specified layout
+LL |     assert::is_maybe_transmutable::<&'static Unit, &'static u8>();
+   |                                                    ^^^^^^^^^^^ The size of `Unit` is smaller than the size of `u8`
    |
 note: required by a bound in `is_maybe_transmutable`
-  --> $DIR/references.rs:16:14
+  --> $DIR/unit-to-u8.rs:10:14
    |
 LL |       pub fn is_maybe_transmutable<Src, Dst>()
    |              --------------------- required by a bound in this function
diff --git a/tests/ui/transmutability/region-infer.stderr b/tests/ui/transmutability/region-infer.stderr
index d6b65e9e4a0..307d0dfe50d 100644
--- a/tests/ui/transmutability/region-infer.stderr
+++ b/tests/ui/transmutability/region-infer.stderr
@@ -2,7 +2,7 @@ error[E0277]: `()` cannot be safely transmuted into `W<'_>` in the defining scop
   --> $DIR/region-infer.rs:20:5
    |
 LL |     test();
-   |     ^^^^ `W<'_>` does not have a well-specified layout
+   |     ^^^^ The size of `()` is smaller than the size of `W<'_>`
    |
 note: required by a bound in `test`
   --> $DIR/region-infer.rs:11:12
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
index 4c5062cd3b3..7f26bc49817 100644
--- a/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr
+++ b/tests/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr
@@ -24,7 +24,7 @@ error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::
   --> $DIR/should_require_well_defined_layout.rs:29:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `should_reject_repr_rust::unit::repr_rust` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:13:14
@@ -68,7 +68,7 @@ error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::
   --> $DIR/should_require_well_defined_layout.rs:35:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `should_reject_repr_rust::tuple::repr_rust` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:13:14
@@ -112,7 +112,7 @@ error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::
   --> $DIR/should_require_well_defined_layout.rs:41:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `should_reject_repr_rust::braces::repr_rust` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:13:14
@@ -156,7 +156,7 @@ error[E0277]: `u128` cannot be safely transmuted into `aligned::repr_rust` in th
   --> $DIR/should_require_well_defined_layout.rs:47:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `aligned::repr_rust` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:13:14
@@ -200,7 +200,7 @@ error[E0277]: `u128` cannot be safely transmuted into `packed::repr_rust` in the
   --> $DIR/should_require_well_defined_layout.rs:53:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                               ^^^^^^^^^ `packed::repr_rust` does not have a well-specified layout
+   |                                               ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:13:14
@@ -244,7 +244,7 @@ error[E0277]: `u128` cannot be safely transmuted into `nested::repr_c` in the de
   --> $DIR/should_require_well_defined_layout.rs:60:47
    |
 LL |         assert::is_maybe_transmutable::<u128, repr_c>();
-   |                                               ^^^^^^ `nested::repr_c` does not have a well-specified layout
+   |                                               ^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:13:14
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
index 4293d34f47b..305ecc71733 100644
--- a/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr
+++ b/tests/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr
@@ -24,7 +24,7 @@ error[E0277]: `u128` cannot be safely transmuted into `should_reject_repr_rust::
   --> $DIR/should_require_well_defined_layout.rs:31:43
    |
 LL |     assert::is_maybe_transmutable::<u128, repr_rust>();
-   |                                           ^^^^^^^^^ `should_reject_repr_rust::repr_rust` does not have a well-specified layout
+   |                                           ^^^^^^^^^ `u128` does not have a well-specified layout
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/should_require_well_defined_layout.rs:13:14
diff --git a/tests/ui/transmutability/unions/should_pad_variants.stderr b/tests/ui/transmutability/unions/should_pad_variants.stderr
index bfbef8b25fc..c82a7968022 100644
--- a/tests/ui/transmutability/unions/should_pad_variants.stderr
+++ b/tests/ui/transmutability/unions/should_pad_variants.stderr
@@ -2,7 +2,7 @@ error[E0277]: `Src` cannot be safely transmuted into `Dst` in the defining scope
   --> $DIR/should_pad_variants.rs:44:36
    |
 LL |     assert::is_transmutable::<Src, Dst, Context>();
-   |                                    ^^^ The size of `Src` is smaller than the size of `Dst`
+   |                                    ^^^ At least one value of `Src` isn't a bit-valid value of `Dst`
    |
 note: required by a bound in `is_transmutable`
   --> $DIR/should_pad_variants.rs:13:14
diff --git a/tests/ui/transmute/transmute-padding-ice.stderr b/tests/ui/transmute/transmute-padding-ice.stderr
index f5480e0b9fb..2739fd16645 100644
--- a/tests/ui/transmute/transmute-padding-ice.stderr
+++ b/tests/ui/transmute/transmute-padding-ice.stderr
@@ -2,7 +2,7 @@ error[E0277]: `B` cannot be safely transmuted into `A` in the defining scope of
   --> $DIR/transmute-padding-ice.rs:27:40
    |
 LL |     assert::is_maybe_transmutable::<B, A>();
-   |                                        ^ The size of `B` is smaller than the size of `A`
+   |                                        ^ At least one value of `B` isn't a bit-valid value of `A`
    |
 note: required by a bound in `is_maybe_transmutable`
   --> $DIR/transmute-padding-ice.rs:11:14