about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock15
-rw-r--r--compiler/rustc_hir/src/lang_items.rs3
-rw-r--r--compiler/rustc_middle/src/traits/select.rs4
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs42
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_target/src/abi/mod.rs3
-rw-r--r--compiler/rustc_trait_selection/Cargo.toml1
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs23
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs52
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs3
-rw-r--r--compiler/rustc_transmute/Cargo.toml28
-rw-r--r--compiler/rustc_transmute/src/layout/dfa.rs184
-rw-r--r--compiler/rustc_transmute/src/layout/mod.rs71
-rw-r--r--compiler/rustc_transmute/src/layout/nfa.rs179
-rw-r--r--compiler/rustc_transmute/src/layout/tree.rs479
-rw-r--r--compiler/rustc_transmute/src/layout/tree/tests.rs80
-rw-r--r--compiler/rustc_transmute/src/lib.rs114
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/mod.rs320
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/query_context.rs93
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/tests.rs115
-rw-r--r--library/core/src/mem/mod.rs4
-rw-r--r--library/core/src/mem/transmutability.rs39
-rw-r--r--src/test/ui/transmutability/abstraction/const_generic_fn.rs41
-rw-r--r--src/test/ui/transmutability/arrays/should_have_correct_length.rs44
-rw-r--r--src/test/ui/transmutability/arrays/should_inherit_alignment.rs55
-rw-r--r--src/test/ui/transmutability/arrays/should_require_well_defined_layout.rs61
-rw-r--r--src/test/ui/transmutability/arrays/should_require_well_defined_layout.stderr93
-rw-r--r--src/test/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.rs149
-rw-r--r--src/test/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.stderr303
-rw-r--r--src/test/ui/transmutability/enums/repr/should_require_well_defined_layout.rs117
-rw-r--r--src/test/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr93
-rw-r--r--src/test/ui/transmutability/enums/should_order_correctly.rs32
-rw-r--r--src/test/ui/transmutability/enums/should_pad_variants.rs40
-rw-r--r--src/test/ui/transmutability/enums/should_pad_variants.stderr18
-rw-r--r--src/test/ui/transmutability/enums/should_respect_endianness.rs33
-rw-r--r--src/test/ui/transmutability/enums/should_respect_endianness.stderr18
-rw-r--r--src/test/ui/transmutability/malformed-program-gracefulness/unknown_dst.rs21
-rw-r--r--src/test/ui/transmutability/malformed-program-gracefulness/unknown_dst.stderr12
-rw-r--r--src/test/ui/transmutability/malformed-program-gracefulness/unknown_src.rs21
-rw-r--r--src/test/ui/transmutability/malformed-program-gracefulness/unknown_src.stderr12
-rw-r--r--src/test/ui/transmutability/malformed-program-gracefulness/unknown_src_field.rs22
-rw-r--r--src/test/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr9
-rw-r--r--src/test/ui/transmutability/primitives/bool.rs25
-rw-r--r--src/test/ui/transmutability/primitives/bool.stderr18
-rw-r--r--src/test/ui/transmutability/primitives/numbers.rs128
-rw-r--r--src/test/ui/transmutability/primitives/numbers.stderr858
-rw-r--r--src/test/ui/transmutability/primitives/unit.rs24
-rw-r--r--src/test/ui/transmutability/primitives/unit.stderr18
-rw-r--r--src/test/ui/transmutability/references.rs20
-rw-r--r--src/test/ui/transmutability/references.stderr18
-rw-r--r--src/test/ui/transmutability/structs/repr/should_handle_align.rs36
-rw-r--r--src/test/ui/transmutability/structs/repr/should_handle_packed.rs35
-rw-r--r--src/test/ui/transmutability/structs/repr/should_require_well_defined_layout.rs76
-rw-r--r--src/test/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr183
-rw-r--r--src/test/ui/transmutability/structs/should_order_fields_correctly.rs31
-rw-r--r--src/test/ui/transmutability/unions/boolish.rs31
-rw-r--r--src/test/ui/transmutability/unions/repr/should_handle_align.rs40
-rw-r--r--src/test/ui/transmutability/unions/repr/should_handle_packed.rs41
-rw-r--r--src/test/ui/transmutability/unions/repr/should_require_well_defined_layout.rs37
-rw-r--r--src/test/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr33
-rw-r--r--src/test/ui/transmutability/unions/should_pad_variants.rs40
-rw-r--r--src/test/ui/transmutability/unions/should_pad_variants.stderr18
-rw-r--r--src/test/ui/transmutability/unions/should_permit_intersecting_if_validity_is_assumed.rs39
-rw-r--r--src/test/ui/transmutability/unions/should_reject_contraction.rs36
-rw-r--r--src/test/ui/transmutability/unions/should_reject_contraction.stderr18
-rw-r--r--src/test/ui/transmutability/unions/should_reject_disjoint.rs36
-rw-r--r--src/test/ui/transmutability/unions/should_reject_disjoint.stderr33
-rw-r--r--src/test/ui/transmutability/unions/should_reject_intersecting.rs38
-rw-r--r--src/test/ui/transmutability/unions/should_reject_intersecting.stderr33
-rw-r--r--src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_private_field.rs38
-rw-r--r--src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_private_variant.rs39
-rw-r--r--src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_tricky_unreachable_field.rs46
-rw-r--r--src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_field.rs39
-rw-r--r--src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_field.stderr12
-rw-r--r--src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_ty.rs40
-rw-r--r--src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_ty.stderr15
-rw-r--r--src/test/ui/transmutability/visibility/should_accept_if_src_has_private_field.rs38
-rw-r--r--src/test/ui/transmutability/visibility/should_accept_if_src_has_private_variant.rs39
-rw-r--r--src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_field.rs38
-rw-r--r--src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_field.stderr12
-rw-r--r--src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_ty.rs39
-rw-r--r--src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_ty.stderr15
-rw-r--r--src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_field.rs37
-rw-r--r--src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_field.stderr18
-rw-r--r--src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_variant.rs38
-rw-r--r--src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_variant.stderr18
-rw-r--r--src/test/ui/transmutability/visibility/should_reject_if_dst_has_tricky_unreachable_field.rs52
-rw-r--r--src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_field.rs39
-rw-r--r--src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_field.stderr18
-rw-r--r--src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_ty.rs42
-rw-r--r--src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_ty.stderr31
91 files changed, 5691 insertions, 2 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2325d0f3bf2..16b67526cff 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4553,6 +4553,7 @@ dependencies = [
  "rustc_session",
  "rustc_span",
  "rustc_target",
+ "rustc_transmute",
  "smallvec",
  "tracing",
 ]
@@ -4578,6 +4579,20 @@ dependencies = [
 ]
 
 [[package]]
+name = "rustc_transmute"
+version = "0.1.0"
+dependencies = [
+ "itertools",
+ "rustc_data_structures",
+ "rustc_infer",
+ "rustc_macros",
+ "rustc_middle",
+ "rustc_span",
+ "rustc_target",
+ "tracing",
+]
+
+[[package]]
 name = "rustc_ty_utils"
 version = "0.0.0"
 dependencies = [
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 13b3e954e1f..c337be12ae4 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -191,6 +191,9 @@ language_item_table! {
     CoerceUnsized,           sym::coerce_unsized,      coerce_unsized_trait,       Target::Trait,          GenericRequirement::Minimum(1);
     DispatchFromDyn,         sym::dispatch_from_dyn,   dispatch_from_dyn_trait,    Target::Trait,          GenericRequirement::Minimum(1);
 
+    // language items relating to transmutability
+    TransmuteTrait,          sym::transmute_trait,     transmute_trait,            Target::Trait,          GenericRequirement::Exact(6);
+
     Add(Op),                 sym::add,                 add_trait,                  Target::Trait,          GenericRequirement::Exact(1);
     Sub(Op),                 sym::sub,                 sub_trait,                  Target::Trait,          GenericRequirement::Exact(1);
     Mul(Op),                 sym::mul,                 mul_trait,                  Target::Trait,          GenericRequirement::Exact(1);
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index 0ca5a532b75..e836ba47eed 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -109,6 +109,10 @@ pub enum SelectionCandidate<'tcx> {
         /// `false` if there are no *further* obligations.
         has_nested: bool,
     },
+
+    /// Implementation of transmutability trait.
+    TransmutabilityCandidate,
+
     ParamCandidate(ty::PolyTraitPredicate<'tcx>),
     ImplCandidate(DefId),
     AutoImplCandidate(DefId),
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 31c523aaca9..5fda1e6538e 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -48,7 +48,7 @@ pub use subst::*;
 pub use vtable::*;
 
 use std::fmt::Debug;
-use std::hash::Hash;
+use std::hash::{Hash, Hasher};
 use std::ops::ControlFlow;
 use std::{fmt, str};
 
@@ -1724,6 +1724,26 @@ impl VariantDef {
     }
 }
 
+/// There should be only one VariantDef for each `def_id`, therefore
+/// it is fine to implement `PartialEq` only based on `def_id`.
+impl PartialEq for VariantDef {
+    #[inline]
+    fn eq(&self, other: &Self) -> bool {
+        self.def_id == other.def_id
+    }
+}
+
+impl Eq for VariantDef {}
+
+/// There should be only one VariantDef for each `def_id`, therefore
+/// it is fine to implement `Hash` only based on `def_id`.
+impl Hash for VariantDef {
+    #[inline]
+    fn hash<H: Hasher>(&self, s: &mut H) {
+        self.def_id.hash(s)
+    }
+}
+
 #[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
 pub enum VariantDiscr {
     /// Explicit value for this variant, i.e., `X = 123`.
@@ -1744,6 +1764,26 @@ pub struct FieldDef {
     pub vis: Visibility,
 }
 
+/// There should be only one FieldDef for each `did`, therefore
+/// it is fine to implement `PartialEq` only based on `did`.
+impl PartialEq for FieldDef {
+    #[inline]
+    fn eq(&self, other: &Self) -> bool {
+        self.did == other.did
+    }
+}
+
+impl Eq for FieldDef {}
+
+/// There should be only one FieldDef for each `did`, therefore
+/// it is fine to implement `Hash` only based on `did`.
+impl Hash for FieldDef {
+    #[inline]
+    fn hash<H: Hasher>(&self, s: &mut H) {
+        self.did.hash(s)
+    }
+}
+
 bitflags! {
     #[derive(TyEncodable, TyDecodable, Default, HashStable)]
     pub struct ReprFlags: u8 {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index c75b6772487..632d2c13d74 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1462,6 +1462,7 @@ symbols! {
         trait_alias,
         trait_upcasting,
         transmute,
+        transmute_trait,
         transparent,
         transparent_enums,
         transparent_unions,
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index b35502d9ee4..0758bf7c7cb 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -508,6 +508,7 @@ impl fmt::Debug for Align {
 
 impl Align {
     pub const ONE: Align = Align { pow2: 0 };
+    pub const MAX: Align = Align { pow2: 29 };
 
     #[inline]
     pub fn from_bits(bits: u64) -> Result<Align, String> {
@@ -540,7 +541,7 @@ impl Align {
         if bytes != 1 {
             return Err(not_power_of_2(align));
         }
-        if pow2 > 29 {
+        if pow2 > Self::MAX.pow2 {
             return Err(too_large(align));
         }
 
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml
index aebeb49e623..566f236f26a 100644
--- a/compiler/rustc_trait_selection/Cargo.toml
+++ b/compiler/rustc_trait_selection/Cargo.toml
@@ -23,4 +23,5 @@ rustc_query_system = { path = "../rustc_query_system" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
+rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 6e8581128dd..a18b835e70c 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -305,6 +305,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 self.assemble_candidates_for_unsizing(obligation, &mut candidates);
             } else if lang_items.destruct_trait() == Some(def_id) {
                 self.assemble_const_destruct_candidates(obligation, &mut candidates);
+            } else if lang_items.transmute_trait() == Some(def_id) {
+                // User-defined transmutability impls are permitted.
+                self.assemble_candidates_from_impls(obligation, &mut candidates);
+                self.assemble_candidates_for_transmutability(obligation, &mut candidates);
             } else {
                 if lang_items.clone_trait() == Some(def_id) {
                     // Same builtin conditions as `Copy`, i.e., every type which has builtin support
@@ -874,6 +878,25 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     }
 
     #[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
+    fn assemble_candidates_for_transmutability(
+        &mut self,
+        obligation: &TraitObligation<'tcx>,
+        candidates: &mut SelectionCandidateSet<'tcx>,
+    ) {
+        if obligation.has_param_types_or_consts() {
+            candidates.ambiguous = false;
+            return;
+        }
+
+        if obligation.has_infer_types_or_consts() {
+            candidates.ambiguous = true;
+            return;
+        }
+
+        candidates.vec.push(TransmutabilityCandidate);
+    }
+
+    #[tracing::instrument(level = "debug", skip(self, obligation, candidates))]
     fn assemble_candidates_for_trait_alias(
         &mut self,
         obligation: &TraitObligation<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index d4c9fd1c5f9..a609fb2b172 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -48,6 +48,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 ImplSource::Builtin(data)
             }
 
+            TransmutabilityCandidate => {
+                let data = self.confirm_transmutability_candidate(obligation)?;
+                ImplSource::Builtin(data)
+            }
+
             ParamCandidate(param) => {
                 let obligations =
                     self.confirm_param_candidate(obligation, param.map_bound(|t| t.trait_ref));
@@ -267,6 +272,53 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         ImplSourceBuiltinData { nested: obligations }
     }
 
+    fn confirm_transmutability_candidate(
+        &mut self,
+        obligation: &TraitObligation<'tcx>,
+    ) -> Result<ImplSourceBuiltinData<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
+        debug!(?obligation, "confirm_transmutability_candidate");
+
+        let predicate = obligation.predicate;
+
+        let type_at = |i| predicate.map_bound(|p| p.trait_ref.substs.type_at(i));
+        let bool_at = |i| {
+            predicate
+                .skip_binder()
+                .trait_ref
+                .substs
+                .const_at(i)
+                .try_eval_bool(self.tcx(), obligation.param_env)
+                .unwrap()
+        };
+
+        let src_and_dst = predicate.map_bound(|p| rustc_transmute::Types {
+            src: p.trait_ref.substs.type_at(1),
+            dst: p.trait_ref.substs.type_at(0),
+        });
+
+        let scope = type_at(2).skip_binder();
+
+        let assume = rustc_transmute::Assume {
+            alignment: bool_at(3),
+            lifetimes: bool_at(4),
+            validity: bool_at(5),
+            visibility: bool_at(6),
+        };
+
+        let cause = obligation.cause.clone();
+
+        let mut transmute_env = rustc_transmute::TransmuteTypeEnv::new(self.infcx);
+
+        let maybe_transmutable = transmute_env.is_transmutable(cause, src_and_dst, scope, assume);
+
+        use rustc_transmute::Answer;
+
+        match maybe_transmutable {
+            Answer::Yes => Ok(ImplSourceBuiltinData { nested: vec![] }),
+            _ => Err(Unimplemented),
+        }
+    }
+
     /// This handles the case where an `auto trait Foo` impl is being used.
     /// The idea is that the impl applies to `X : Foo` if the following conditions are met:
     ///
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 17f34012d1d..9ca23228b98 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1630,6 +1630,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 );
             }
 
+            // FIXME(@jswrenn): this should probably be more sophisticated
+            (TransmutabilityCandidate, _) | (_, TransmutabilityCandidate) => false,
+
             // (*)
             (
                 BuiltinCandidate { has_nested: false }
diff --git a/compiler/rustc_transmute/Cargo.toml b/compiler/rustc_transmute/Cargo.toml
new file mode 100644
index 00000000000..9dc96e08a8e
--- /dev/null
+++ b/compiler/rustc_transmute/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "rustc_transmute"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+tracing = "0.1"
+rustc_data_structures = { path = "../rustc_data_structures", optional = true}
+rustc_infer = { path = "../rustc_infer", optional = true}
+rustc_macros = { path = "../rustc_macros", optional = true}
+rustc_middle = { path = "../rustc_middle", optional = true}
+rustc_span = { path = "../rustc_span", optional = true}
+rustc_target = { path = "../rustc_target", optional = true}
+
+[features]
+rustc = [
+    "rustc_middle",
+    "rustc_data_structures",
+    "rustc_infer",
+    "rustc_macros",
+    "rustc_span",
+    "rustc_target",
+]
+
+[dev-dependencies]
+itertools = "0.10.1"
\ No newline at end of file
diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs
new file mode 100644
index 00000000000..cdd3195712d
--- /dev/null
+++ b/compiler/rustc_transmute/src/layout/dfa.rs
@@ -0,0 +1,184 @@
+use super::{nfa, Byte, Nfa, Ref};
+use crate::Map;
+use std::fmt;
+use std::sync::atomic::{AtomicU64, Ordering};
+
+#[derive(PartialEq, Clone, Debug)]
+pub(crate) struct Dfa<R>
+where
+    R: Ref,
+{
+    pub(crate) transitions: Map<State, Transitions<R>>,
+    pub(crate) start: State,
+    pub(crate) accepting: State,
+}
+
+#[derive(PartialEq, Clone, Debug)]
+pub(crate) struct Transitions<R>
+where
+    R: Ref,
+{
+    byte_transitions: Map<Byte, State>,
+    ref_transitions: Map<R, State>,
+}
+
+impl<R> Default for Transitions<R>
+where
+    R: Ref,
+{
+    fn default() -> Self {
+        Self { byte_transitions: Map::default(), ref_transitions: Map::default() }
+    }
+}
+
+impl<R> Transitions<R>
+where
+    R: Ref,
+{
+    fn insert(&mut self, transition: Transition<R>, state: State) {
+        match transition {
+            Transition::Byte(b) => {
+                self.byte_transitions.insert(b, state);
+            }
+            Transition::Ref(r) => {
+                self.ref_transitions.insert(r, state);
+            }
+        }
+    }
+}
+
+/// The states in a `Nfa` represent byte offsets.
+#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)]
+pub(crate) struct State(u64);
+
+#[derive(Hash, Eq, PartialEq, Clone, Copy)]
+pub(crate) enum Transition<R>
+where
+    R: Ref,
+{
+    Byte(Byte),
+    Ref(R),
+}
+
+impl fmt::Debug for State {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "S_{}", self.0)
+    }
+}
+
+impl<R> fmt::Debug for Transition<R>
+where
+    R: Ref,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match &self {
+            Self::Byte(b) => b.fmt(f),
+            Self::Ref(r) => r.fmt(f),
+        }
+    }
+}
+
+impl<R> Dfa<R>
+where
+    R: Ref,
+{
+    pub(crate) fn unit() -> Self {
+        let transitions: Map<State, Transitions<R>> = Map::default();
+        let start = State::new();
+        let accepting = start;
+
+        Self { transitions, start, accepting }
+    }
+
+    #[cfg(test)]
+    pub(crate) fn bool() -> Self {
+        let mut transitions: Map<State, Transitions<R>> = Map::default();
+        let start = State::new();
+        let accepting = State::new();
+
+        transitions.entry(start).or_default().insert(Transition::Byte(Byte::Init(0x00)), accepting);
+
+        transitions.entry(start).or_default().insert(Transition::Byte(Byte::Init(0x01)), accepting);
+
+        Self { transitions, start, accepting }
+    }
+
+    #[tracing::instrument]
+    #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
+    pub(crate) fn from_nfa(nfa: Nfa<R>) -> Self {
+        let Nfa { transitions: nfa_transitions, start: nfa_start, accepting: nfa_accepting } = nfa;
+
+        let mut dfa_transitions: Map<State, Transitions<R>> = Map::default();
+        let mut nfa_to_dfa: Map<nfa::State, State> = Map::default();
+        let dfa_start = State::new();
+        nfa_to_dfa.insert(nfa_start, dfa_start);
+
+        let mut queue = vec![(nfa_start, dfa_start)];
+
+        while let Some((nfa_state, dfa_state)) = queue.pop() {
+            if nfa_state == nfa_accepting {
+                continue;
+            }
+
+            for (nfa_transition, next_nfa_states) in nfa_transitions[&nfa_state].iter() {
+                let dfa_transitions =
+                    dfa_transitions.entry(dfa_state).or_insert_with(Default::default);
+
+                let mapped_state = next_nfa_states.iter().find_map(|x| nfa_to_dfa.get(x).copied());
+
+                let next_dfa_state = match nfa_transition {
+                    &nfa::Transition::Byte(b) => *dfa_transitions
+                        .byte_transitions
+                        .entry(b)
+                        .or_insert_with(|| mapped_state.unwrap_or_else(State::new)),
+                    &nfa::Transition::Ref(r) => *dfa_transitions
+                        .ref_transitions
+                        .entry(r)
+                        .or_insert_with(|| mapped_state.unwrap_or_else(State::new)),
+                };
+
+                for &next_nfa_state in next_nfa_states {
+                    nfa_to_dfa.entry(next_nfa_state).or_insert_with(|| {
+                        queue.push((next_nfa_state, next_dfa_state));
+                        next_dfa_state
+                    });
+                }
+            }
+        }
+
+        let dfa_accepting = nfa_to_dfa[&nfa_accepting];
+
+        Self { transitions: dfa_transitions, start: dfa_start, accepting: dfa_accepting }
+    }
+
+    pub(crate) fn bytes_from(&self, start: State) -> Option<&Map<Byte, State>> {
+        Some(&self.transitions.get(&start)?.byte_transitions)
+    }
+
+    pub(crate) fn byte_from(&self, start: State, byte: Byte) -> Option<State> {
+        self.transitions.get(&start)?.byte_transitions.get(&byte).copied()
+    }
+
+    pub(crate) fn refs_from(&self, start: State) -> Option<&Map<R, State>> {
+        Some(&self.transitions.get(&start)?.ref_transitions)
+    }
+}
+
+impl State {
+    pub(crate) fn new() -> Self {
+        static COUNTER: AtomicU64 = AtomicU64::new(0);
+        Self(COUNTER.fetch_add(1, Ordering::SeqCst))
+    }
+}
+
+impl<R> From<nfa::Transition<R>> for Transition<R>
+where
+    R: Ref,
+{
+    fn from(nfa_transition: nfa::Transition<R>) -> Self {
+        match nfa_transition {
+            nfa::Transition::Byte(byte) => Transition::Byte(byte),
+            nfa::Transition::Ref(r) => Transition::Ref(r),
+        }
+    }
+}
diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs
new file mode 100644
index 00000000000..cbf92bdacd6
--- /dev/null
+++ b/compiler/rustc_transmute/src/layout/mod.rs
@@ -0,0 +1,71 @@
+use std::fmt::{self, Debug};
+use std::hash::Hash;
+
+pub(crate) mod tree;
+pub(crate) use tree::Tree;
+
+pub(crate) mod nfa;
+pub(crate) use nfa::Nfa;
+
+pub(crate) mod dfa;
+pub(crate) use dfa::Dfa;
+
+#[derive(Debug)]
+pub(crate) struct Uninhabited;
+
+/// An instance of a byte is either initialized to a particular value, or uninitialized.
+#[derive(Hash, Eq, PartialEq, Clone, Copy)]
+pub(crate) enum Byte {
+    Uninit,
+    Init(u8),
+}
+
+impl fmt::Debug for Byte {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match &self {
+            Self::Uninit => f.write_str("??u8"),
+            Self::Init(b) => write!(f, "{:#04x}u8", b),
+        }
+    }
+}
+
+pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {}
+pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {}
+
+impl Def for ! {}
+impl Ref for ! {}
+
+#[cfg(feature = "rustc")]
+pub(crate) mod rustc {
+    use rustc_middle::mir::Mutability;
+    use rustc_middle::ty;
+    use rustc_middle::ty::Region;
+    use rustc_middle::ty::Ty;
+
+    /// A reference in the layout [`Nfa`].
+    #[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)]
+    pub struct Ref<'tcx> {
+        lifetime: Region<'tcx>,
+        ty: Ty<'tcx>,
+        mutability: Mutability,
+    }
+
+    impl<'tcx> super::Ref for Ref<'tcx> {}
+
+    impl<'tcx> Ref<'tcx> {
+        pub fn min_align(&self) -> usize {
+            todo!()
+        }
+    }
+
+    /// A visibility node in the layout [`Nfa`].
+    #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
+    pub enum Def<'tcx> {
+        Adt(ty::AdtDef<'tcx>),
+        Variant(&'tcx ty::VariantDef),
+        Field(&'tcx ty::FieldDef),
+        Primitive,
+    }
+
+    impl<'tcx> super::Def for Def<'tcx> {}
+}
diff --git a/compiler/rustc_transmute/src/layout/nfa.rs b/compiler/rustc_transmute/src/layout/nfa.rs
new file mode 100644
index 00000000000..817e426ba27
--- /dev/null
+++ b/compiler/rustc_transmute/src/layout/nfa.rs
@@ -0,0 +1,179 @@
+use super::{Byte, Ref, Tree, Uninhabited};
+use crate::{Map, Set};
+use std::fmt;
+use std::sync::atomic::{AtomicU64, Ordering};
+
+/// A non-deterministic finite automaton (NFA) that represents the layout of a type.
+/// The transmutability of two given types is computed by comparing their `Nfa`s.
+#[derive(PartialEq, Debug)]
+pub(crate) struct Nfa<R>
+where
+    R: Ref,
+{
+    pub(crate) transitions: Map<State, Map<Transition<R>, Set<State>>>,
+    pub(crate) start: State,
+    pub(crate) accepting: State,
+}
+
+/// The states in a `Nfa` represent byte offsets.
+#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)]
+pub(crate) struct State(u64);
+
+/// The transitions between states in a `Nfa` reflect bit validity.
+#[derive(Hash, Eq, PartialEq, Clone, Copy)]
+pub(crate) enum Transition<R>
+where
+    R: Ref,
+{
+    Byte(Byte),
+    Ref(R),
+}
+
+impl fmt::Debug for State {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "S_{}", self.0)
+    }
+}
+
+impl<R> fmt::Debug for Transition<R>
+where
+    R: Ref,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match &self {
+            Self::Byte(b) => b.fmt(f),
+            Self::Ref(r) => r.fmt(f),
+        }
+    }
+}
+
+impl<R> Nfa<R>
+where
+    R: Ref,
+{
+    pub(crate) fn unit() -> Self {
+        let transitions: Map<State, Map<Transition<R>, Set<State>>> = Map::default();
+        let start = State::new();
+        let accepting = start;
+
+        Nfa { transitions, start, accepting }
+    }
+
+    pub(crate) fn from_byte(byte: Byte) -> Self {
+        let mut transitions: Map<State, Map<Transition<R>, Set<State>>> = Map::default();
+        let start = State::new();
+        let accepting = State::new();
+
+        let source = transitions.entry(start).or_default();
+        let edge = source.entry(Transition::Byte(byte)).or_default();
+        edge.insert(accepting);
+
+        Nfa { transitions, start, accepting }
+    }
+
+    pub(crate) fn from_ref(r: R) -> Self {
+        let mut transitions: Map<State, Map<Transition<R>, Set<State>>> = Map::default();
+        let start = State::new();
+        let accepting = State::new();
+
+        let source = transitions.entry(start).or_default();
+        let edge = source.entry(Transition::Ref(r)).or_default();
+        edge.insert(accepting);
+
+        Nfa { transitions, start, accepting }
+    }
+
+    pub(crate) fn from_tree(tree: Tree<!, R>) -> Result<Self, Uninhabited> {
+        Ok(match tree {
+            Tree::Byte(b) => Self::from_byte(b),
+            Tree::Def(..) => unreachable!(),
+            Tree::Ref(r) => Self::from_ref(r),
+            Tree::Alt(alts) => {
+                let mut alts = alts.into_iter().map(Self::from_tree);
+                let mut nfa = alts.next().ok_or(Uninhabited)??;
+                for alt in alts {
+                    nfa = nfa.union(&alt?);
+                }
+                nfa
+            }
+            Tree::Seq(elts) => {
+                let mut nfa = Self::unit();
+                for elt in elts.into_iter().map(Self::from_tree) {
+                    nfa = nfa.concat(elt?);
+                }
+                nfa
+            }
+        })
+    }
+
+    /// Concatenate two `Nfa`s.
+    pub(crate) fn concat(self, other: Self) -> Self {
+        if self.start == self.accepting {
+            return other;
+        } else if other.start == other.accepting {
+            return self;
+        }
+
+        let start = self.start;
+        let accepting = other.accepting;
+
+        let mut transitions: Map<State, Map<Transition<R>, Set<State>>> = self.transitions;
+
+        // the iteration order doesn't matter
+        #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
+        for (source, transition) in other.transitions {
+            let fix_state = |state| if state == other.start { self.accepting } else { state };
+            let entry = transitions.entry(fix_state(source)).or_default();
+            for (edge, destinations) in transition {
+                let entry = entry.entry(edge.clone()).or_default();
+                for destination in destinations {
+                    entry.insert(fix_state(destination));
+                }
+            }
+        }
+
+        Self { transitions, start, accepting }
+    }
+
+    /// Compute the union of two `Nfa`s.
+    pub(crate) fn union(&self, other: &Self) -> Self {
+        let start = self.start;
+        let accepting = self.accepting;
+
+        let mut transitions: Map<State, Map<Transition<R>, Set<State>>> = self.transitions.clone();
+
+        // the iteration order doesn't matter
+        #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
+        for (&(mut source), transition) in other.transitions.iter() {
+            // if source is starting state of `other`, replace with starting state of `self`
+            if source == other.start {
+                source = self.start;
+            }
+            let entry = transitions.entry(source).or_default();
+            for (edge, destinations) in transition {
+                let entry = entry.entry(edge.clone()).or_default();
+                // the iteration order doesn't matter
+                #[cfg_attr(feature = "rustc", allow(rustc::potential_query_instability))]
+                for &(mut destination) in destinations {
+                    // if dest is accepting state of `other`, replace with accepting state of `self`
+                    if destination == other.accepting {
+                        destination = self.accepting;
+                    }
+                    entry.insert(destination);
+                }
+            }
+        }
+        Self { transitions, start, accepting }
+    }
+
+    pub(crate) fn edges_from(&self, start: State) -> Option<&Map<Transition<R>, Set<State>>> {
+        self.transitions.get(&start)
+    }
+}
+
+impl State {
+    pub(crate) fn new() -> Self {
+        static COUNTER: AtomicU64 = AtomicU64::new(0);
+        Self(COUNTER.fetch_add(1, Ordering::SeqCst))
+    }
+}
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
new file mode 100644
index 00000000000..67b401855d4
--- /dev/null
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -0,0 +1,479 @@
+use super::{Byte, Def, Ref};
+
+#[cfg(test)]
+mod tests;
+
+/// A tree-based representation of a type layout.
+///
+/// Invariants:
+/// 1. All paths through the layout have the same length (in bytes).
+///
+/// Nice-to-haves:
+/// 1. An `Alt` is never directly nested beneath another `Alt`.
+/// 2. A `Seq` is never directly nested beneath another `Seq`.
+/// 3. `Seq`s and `Alt`s with a single member do not exist.
+#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+pub(crate) enum Tree<D, R>
+where
+    D: Def,
+    R: Ref,
+{
+    /// A sequence of successive layouts.
+    Seq(Vec<Self>),
+    /// A choice between alternative layouts.
+    Alt(Vec<Self>),
+    /// A definition node.
+    Def(D),
+    /// A reference node.
+    Ref(R),
+    /// A byte node.
+    Byte(Byte),
+}
+
+impl<D, R> Tree<D, R>
+where
+    D: Def,
+    R: Ref,
+{
+    /// A `Tree` consisting only of a definition node.
+    pub(crate) fn def(def: D) -> Self {
+        Self::Def(def)
+    }
+
+    /// A `Tree` representing an uninhabited type.
+    pub(crate) fn uninhabited() -> Self {
+        Self::Alt(vec![])
+    }
+
+    /// A `Tree` representing a zero-sized type.
+    pub(crate) fn unit() -> Self {
+        Self::Seq(Vec::new())
+    }
+
+    /// A `Tree` containing a single, uninitialized byte.
+    pub(crate) fn uninit() -> Self {
+        Self::Byte(Byte::Uninit)
+    }
+
+    /// A `Tree` representing the layout of `bool`.
+    pub(crate) fn bool() -> Self {
+        Self::from_bits(0x00).or(Self::from_bits(0x01))
+    }
+
+    /// A `Tree` whose layout matches that of a `u8`.
+    pub(crate) fn u8() -> Self {
+        Self::Alt((0u8..=255).map(Self::from_bits).collect())
+    }
+
+    /// A `Tree` whose layout accepts exactly the given bit pattern.
+    pub(crate) fn from_bits(bits: u8) -> Self {
+        Self::Byte(Byte::Init(bits))
+    }
+
+    /// A `Tree` whose layout is a number of the given width.
+    pub(crate) fn number(width_in_bytes: usize) -> Self {
+        Self::Seq(vec![Self::u8(); width_in_bytes])
+    }
+
+    /// A `Tree` whose layout is entirely padding of the given width.
+    #[tracing::instrument]
+    pub(crate) fn padding(width_in_bytes: usize) -> Self {
+        Self::Seq(vec![Self::uninit(); width_in_bytes])
+    }
+
+    /// Remove all `Def` nodes, and all branches of the layout for which `f` produces false.
+    pub(crate) fn prune<F>(self, f: &F) -> Tree<!, R>
+    where
+        F: Fn(D) -> bool,
+    {
+        match self {
+            Self::Seq(elts) => elts
+                .into_iter()
+                .map(|elt| elt.prune(f))
+                .try_fold(Tree::unit(), |elts, elt| {
+                    if elt == Tree::uninhabited() {
+                        Err(Tree::uninhabited())
+                    } else {
+                        Ok(elts.then(elt))
+                    }
+                })
+                .into_ok_or_err(),
+            Self::Alt(alts) => alts
+                .into_iter()
+                .map(|alt| alt.prune(f))
+                .fold(Tree::uninhabited(), |alts, alt| alts.or(alt)),
+            Self::Byte(b) => Tree::Byte(b),
+            Self::Ref(r) => Tree::Ref(r),
+            Self::Def(d) => {
+                if !f(d) {
+                    Tree::uninhabited()
+                } else {
+                    Tree::unit()
+                }
+            }
+        }
+    }
+
+    /// Produces `true` if `Tree` is an inhabited type; otherwise false.
+    pub(crate) fn is_inhabited(&self) -> bool {
+        match self {
+            Self::Seq(elts) => elts.into_iter().all(|elt| elt.is_inhabited()),
+            Self::Alt(alts) => alts.into_iter().any(|alt| alt.is_inhabited()),
+            Self::Byte(..) | Self::Ref(..) | Self::Def(..) => true,
+        }
+    }
+}
+
+impl<D, R> Tree<D, R>
+where
+    D: Def,
+    R: Ref,
+{
+    /// Produces a new `Tree` where `other` is sequenced after `self`.
+    pub(crate) fn then(self, other: Self) -> Self {
+        match (self, other) {
+            (Self::Seq(elts), other) | (other, Self::Seq(elts)) if elts.len() == 0 => other,
+            (Self::Seq(mut lhs), Self::Seq(mut rhs)) => {
+                lhs.append(&mut rhs);
+                Self::Seq(lhs)
+            }
+            (Self::Seq(mut lhs), rhs) => {
+                lhs.push(rhs);
+                Self::Seq(lhs)
+            }
+            (lhs, Self::Seq(mut rhs)) => {
+                rhs.insert(0, lhs);
+                Self::Seq(rhs)
+            }
+            (lhs, rhs) => Self::Seq(vec![lhs, rhs]),
+        }
+    }
+
+    /// Produces a new `Tree` accepting either `self` or `other` as alternative layouts.
+    pub(crate) fn or(self, other: Self) -> Self {
+        match (self, other) {
+            (Self::Alt(alts), other) | (other, Self::Alt(alts)) if alts.len() == 0 => other,
+            (Self::Alt(mut lhs), Self::Alt(rhs)) => {
+                lhs.extend(rhs);
+                Self::Alt(lhs)
+            }
+            (Self::Alt(mut alts), alt) | (alt, Self::Alt(mut alts)) => {
+                alts.push(alt);
+                Self::Alt(alts)
+            }
+            (lhs, rhs) => Self::Alt(vec![lhs, rhs]),
+        }
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub(crate) enum Err {
+    /// The layout of the type is unspecified.
+    Unspecified,
+    /// This error will be surfaced elsewhere by rustc, so don't surface it.
+    Unknown,
+}
+
+#[cfg(feature = "rustc")]
+pub(crate) mod rustc {
+    use super::{Err, Tree};
+    use crate::layout::rustc::{Def, Ref};
+
+    use rustc_middle::ty;
+    use rustc_middle::ty::layout::LayoutError;
+    use rustc_middle::ty::util::Discr;
+    use rustc_middle::ty::AdtDef;
+    use rustc_middle::ty::ParamEnv;
+    use rustc_middle::ty::SubstsRef;
+    use rustc_middle::ty::Ty;
+    use rustc_middle::ty::TyCtxt;
+    use rustc_middle::ty::VariantDef;
+    use rustc_target::abi::Align;
+    use std::alloc;
+
+    impl<'tcx> From<LayoutError<'tcx>> for Err {
+        fn from(err: LayoutError<'tcx>) -> Self {
+            match err {
+                LayoutError::Unknown(..) => Self::Unknown,
+                err @ _ => unimplemented!("{:?}", err),
+            }
+        }
+    }
+
+    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, 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::*;
+            use rustc_target::abi::HasDataLayout;
+
+            let target = tcx.data_layout();
+
+            match 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::Tuple(members) => {
+                    if members.len() == 0 {
+                        Ok(Tree::unit())
+                    } else {
+                        Err(Err::Unspecified)
+                    }
+                }
+
+                ty::Array(ty, len) => {
+                    let len = len.try_eval_usize(tcx, ParamEnv::reveal_all()).unwrap();
+                    let elt = Tree::from_ty(*ty, tcx)?;
+                    Ok(std::iter::repeat(elt)
+                        .take(len as usize)
+                        .fold(Tree::unit(), |tree, elt| tree.then(elt)))
+                }
+
+                ty::Adt(adt_def, substs_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::Unspecified);
+                    }
+
+                    // 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,
+                            substs_ref,
+                            &layout_summary,
+                            None,
+                            adt_def.non_enum_variant(),
+                            tcx,
+                        )?,
+                        AdtKind::Enum => {
+                            tracing::trace!(
+                                adt_def = ?adt_def,
+                                "treeifying enum"
+                            );
+                            let mut tree = Tree::uninhabited();
+
+                            for (idx, discr) in adt_def.discriminants(tcx) {
+                                tree = tree.or(Self::from_repr_c_variant(
+                                    ty,
+                                    *adt_def,
+                                    substs_ref,
+                                    &layout_summary,
+                                    Some(discr),
+                                    adt_def.variant(idx),
+                                    tcx,
+                                )?);
+                            }
+
+                            tree
+                        }
+                        AdtKind::Union => {
+                            // is the layout well-defined?
+                            if !adt_def.repr().c() {
+                                return Err(Err::Unspecified);
+                            }
+
+                            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, substs_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
+                        }
+                    }))
+                }
+                _ => Err(Err::Unspecified),
+            }
+        }
+
+        fn from_repr_c_variant(
+            ty: Ty<'tcx>,
+            adt_def: AdtDef<'tcx>,
+            substs_ref: SubstsRef<'tcx>,
+            layout_summary: &LayoutSummary,
+            discr: Option<Discr<'tcx>>,
+            variant_def: &'tcx VariantDef,
+            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 clamp =
+                |align: Align| align.clamp(min_align, max_align).bytes().try_into().unwrap();
+
+            let variant_span = tracing::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 discriminant, if any.
+            if let Some(discr) = discr {
+                tracing::trace!(discr = ?discr, "treeifying discriminant");
+                let discr_layout = alloc::Layout::from_size_align(
+                    layout_summary.discriminant_size,
+                    clamp(layout_summary.discriminant_align),
+                )
+                .unwrap();
+                tracing::trace!(discr_layout = ?discr_layout, "computed discriminant layout");
+                variant_layout = variant_layout.extend(discr_layout).unwrap().0;
+                tree = tree.then(Self::from_disr(discr, tcx, layout_summary.discriminant_size));
+            }
+
+            // Next come fields.
+            let fields_span = tracing::trace_span!("treeifying fields").entered();
+            for field_def in variant_def.fields.iter() {
+                let field_ty = field_def.ty(tcx, substs_ref);
+                let _span = tracing::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 charactaristics
+                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));
+                }
+
+                // 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;
+            }
+            drop(fields_span);
+
+            // finally: padding
+            let padding_span = tracing::trace_span!("adding trailing padding").entered();
+            let padding_needed = layout_summary.total_size - variant_layout.size();
+            if padding_needed > 0 {
+                tree = tree.then(Self::padding(padding_needed));
+            };
+            drop(padding_span);
+            drop(variant_span);
+            Ok(tree)
+        }
+
+        pub fn from_disr(discr: Discr<'tcx>, tcx: TyCtxt<'tcx>, size: usize) -> Self {
+            // FIXME(@jswrenn): I'm certain this is missing needed endian nuance.
+            let bytes = discr.val.to_ne_bytes();
+            let bytes = &bytes[..size];
+            Self::Seq(bytes.into_iter().copied().map(|b| Self::from_bits(b)).collect())
+        }
+    }
+
+    fn layout_of<'tcx>(
+        ctx: TyCtxt<'tcx>,
+        ty: Ty<'tcx>,
+    ) -> Result<alloc::Layout, LayoutError<'tcx>> {
+        use rustc_middle::ty::ParamEnvAnd;
+        use rustc_target::abi::TyAndLayout;
+
+        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();
+        tracing::trace!(
+            ty = ?ty,
+            layout = ?layout,
+            "computed layout for type"
+        );
+        Ok(layout)
+    }
+}
diff --git a/compiler/rustc_transmute/src/layout/tree/tests.rs b/compiler/rustc_transmute/src/layout/tree/tests.rs
new file mode 100644
index 00000000000..90515e92f7a
--- /dev/null
+++ b/compiler/rustc_transmute/src/layout/tree/tests.rs
@@ -0,0 +1,80 @@
+use super::Tree;
+
+#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
+pub enum Def {
+    Visible,
+    Invisible,
+}
+
+impl super::Def for Def {}
+
+mod prune {
+    use super::*;
+
+    mod should_simplify {
+        use super::*;
+
+        #[test]
+        fn seq_1() {
+            let layout: Tree<Def, !> = Tree::def(Def::Visible).then(Tree::from_bits(0x00));
+            assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::from_bits(0x00));
+        }
+
+        #[test]
+        fn seq_2() {
+            let layout: Tree<Def, !> =
+                Tree::from_bits(0x00).then(Tree::def(Def::Visible)).then(Tree::from_bits(0x01));
+
+            assert_eq!(
+                layout.prune(&|d| matches!(d, Def::Visible)),
+                Tree::from_bits(0x00).then(Tree::from_bits(0x01))
+            );
+        }
+    }
+
+    mod should_reject {
+        use super::*;
+
+        #[test]
+        fn invisible_def() {
+            let layout: Tree<Def, !> = Tree::def(Def::Invisible);
+            assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::uninhabited());
+        }
+
+        #[test]
+        fn invisible_def_in_seq_len_2() {
+            let layout: Tree<Def, !> = Tree::def(Def::Visible).then(Tree::def(Def::Invisible));
+            assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::uninhabited());
+        }
+
+        #[test]
+        fn invisible_def_in_seq_len_3() {
+            let layout: Tree<Def, !> =
+                Tree::def(Def::Visible).then(Tree::from_bits(0x00)).then(Tree::def(Def::Invisible));
+            assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::uninhabited());
+        }
+    }
+
+    mod should_accept {
+        use super::*;
+
+        #[test]
+        fn visible_def() {
+            let layout: Tree<Def, !> = Tree::def(Def::Visible);
+            assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::unit());
+        }
+
+        #[test]
+        fn visible_def_in_seq_len_2() {
+            let layout: Tree<Def, !> = Tree::def(Def::Visible).then(Tree::def(Def::Visible));
+            assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::unit());
+        }
+
+        #[test]
+        fn visible_def_in_seq_len_3() {
+            let layout: Tree<Def, !> =
+                Tree::def(Def::Visible).then(Tree::from_bits(0x00)).then(Tree::def(Def::Visible));
+            assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::from_bits(0x00));
+        }
+    }
+}
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
new file mode 100644
index 00000000000..9f7508fdd71
--- /dev/null
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -0,0 +1,114 @@
+#![feature(
+    alloc_layout_extra,
+    control_flow_enum,
+    decl_macro,
+    iterator_try_reduce,
+    never_type,
+    result_into_ok_or_err
+)]
+#![allow(dead_code, unused_variables)]
+
+#[cfg(feature = "rustc")]
+pub(crate) use rustc_data_structures::fx::{FxHashMap as Map, FxHashSet as Set};
+
+#[cfg(not(feature = "rustc"))]
+pub(crate) use std::collections::{HashMap as Map, HashSet as Set};
+
+pub(crate) mod layout;
+pub(crate) mod maybe_transmutable;
+
+#[derive(Default)]
+pub struct Assume {
+    pub alignment: bool,
+    pub lifetimes: bool,
+    pub validity: bool,
+    pub visibility: bool,
+}
+
+/// 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,
+{
+    /// `Src` is transmutable into `Dst`.
+    Yes,
+
+    /// `Src` is NOT transmutable into `Dst`.
+    No(Reason),
+
+    /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`.
+    IfTransmutable { src: R, dst: R },
+
+    /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met.
+    IfAll(Vec<Answer<R>>),
+
+    /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met.
+    IfAny(Vec<Answer<R>>),
+}
+
+/// Answers: Why wasn't the source type transmutable into the destination type?
+#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone)]
+pub enum Reason {
+    /// The layout of the source type is unspecified.
+    SrcIsUnspecified,
+    /// The layout of the destination type is unspecified.
+    DstIsUnspecified,
+    /// The layout of the destination type is bit-incompatible with the source type.
+    DstIsBitIncompatible,
+    /// There aren't any public constructors for `Dst`.
+    DstIsPrivate,
+    /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
+    DstIsTooBig,
+}
+
+#[cfg(feature = "rustc")]
+mod rustc {
+    use rustc_infer::infer::InferCtxt;
+    use rustc_macros::{TypeFoldable, TypeVisitable};
+    use rustc_middle::traits::ObligationCause;
+    use rustc_middle::ty::Binder;
+    use rustc_middle::ty::Ty;
+
+    /// The source and destination types of a transmutation.
+    #[derive(TypeFoldable, TypeVisitable, Debug, Clone, Copy)]
+    pub struct Types<'tcx> {
+        /// The source type.
+        pub src: Ty<'tcx>,
+        /// The destination type.
+        pub dst: Ty<'tcx>,
+    }
+
+    pub struct TransmuteTypeEnv<'cx, 'tcx> {
+        infcx: &'cx InferCtxt<'cx, 'tcx>,
+    }
+
+    impl<'cx, 'tcx> TransmuteTypeEnv<'cx, 'tcx> {
+        pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>) -> Self {
+            Self { infcx }
+        }
+
+        #[allow(unused)]
+        pub fn is_transmutable(
+            &mut self,
+            cause: ObligationCause<'tcx>,
+            src_and_dst: Binder<'tcx, Types<'tcx>>,
+            scope: Ty<'tcx>,
+            assume: crate::Assume,
+        ) -> crate::Answer<crate::layout::rustc::Ref<'tcx>> {
+            let src = src_and_dst.map_bound(|types| types.src).skip_binder();
+            let dst = src_and_dst.map_bound(|types| types.dst).skip_binder();
+            crate::maybe_transmutable::MaybeTransmutableQuery::new(
+                src,
+                dst,
+                scope,
+                assume,
+                self.infcx.tcx,
+            )
+            .answer()
+        }
+    }
+}
+
+#[cfg(feature = "rustc")]
+pub use rustc::*;
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
new file mode 100644
index 00000000000..ef3852001a8
--- /dev/null
+++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
@@ -0,0 +1,320 @@
+use crate::Map;
+use crate::{Answer, Reason};
+
+#[cfg(test)]
+mod tests;
+
+mod query_context;
+use query_context::QueryContext;
+
+use crate::layout::{self, dfa, Byte, Dfa, Nfa, Tree, Uninhabited};
+pub(crate) struct MaybeTransmutableQuery<L, C>
+where
+    C: QueryContext,
+{
+    src: L,
+    dst: L,
+    scope: <C as QueryContext>::Scope,
+    assume: crate::Assume,
+    context: C,
+}
+
+impl<L, C> MaybeTransmutableQuery<L, C>
+where
+    C: QueryContext,
+{
+    pub(crate) fn new(
+        src: L,
+        dst: L,
+        scope: <C as QueryContext>::Scope,
+        assume: crate::Assume,
+        context: C,
+    ) -> Self {
+        Self { src, dst, scope, assume, context }
+    }
+
+    pub(crate) fn map_layouts<F, M>(
+        self,
+        f: F,
+    ) -> Result<MaybeTransmutableQuery<M, C>, Answer<<C as QueryContext>::Ref>>
+    where
+        F: FnOnce(
+            L,
+            L,
+            <C as QueryContext>::Scope,
+            &C,
+        ) -> Result<(M, M), Answer<<C as QueryContext>::Ref>>,
+    {
+        let Self { src, dst, scope, assume, context } = self;
+
+        let (src, dst) = f(src, dst, scope, &context)?;
+
+        Ok(MaybeTransmutableQuery { src, dst, scope, assume, context })
+    }
+}
+
+#[cfg(feature = "rustc")]
+mod rustc {
+    use super::*;
+    use crate::layout::tree::Err;
+
+    use rustc_middle::ty::Ty;
+    use rustc_middle::ty::TyCtxt;
+
+    impl<'tcx> MaybeTransmutableQuery<Ty<'tcx>, TyCtxt<'tcx>> {
+        /// This method begins by converting `src` and `dst` from `Ty`s to `Tree`s,
+        /// then computes an answer using those trees.
+        #[tracing::instrument(skip(self), fields(src = ?self.src, dst = ?self.dst))]
+        pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> {
+            let query_or_answer = self.map_layouts(|src, dst, scope, &context| {
+                // 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).map_err(|err| match err {
+                    // Answer `Yes` here, because "Unknown Type" will already be reported by
+                    // rustc. No need to spam the user with more errors.
+                    Err::Unknown => Answer::Yes,
+                    Err::Unspecified => Answer::No(Reason::SrcIsUnspecified),
+                })?;
+
+                let dst = Tree::from_ty(dst, context).map_err(|err| match err {
+                    Err::Unknown => Answer::Yes,
+                    Err::Unspecified => Answer::No(Reason::DstIsUnspecified),
+                })?;
+
+                Ok((src, dst))
+            });
+
+            match query_or_answer {
+                Ok(query) => query.answer(),
+                Err(answer) => answer,
+            }
+        }
+    }
+}
+
+impl<C> MaybeTransmutableQuery<Tree<<C as QueryContext>::Def, <C as QueryContext>::Ref>, C>
+where
+    C: QueryContext,
+{
+    /// Answers whether a `Tree` is transmutable into another `Tree`.
+    ///
+    /// This method begins by de-def'ing `src` and `dst`, and prunes private paths from `dst`,
+    /// then converts `src` and `dst` to `Nfa`s, and computes an answer using those NFAs.
+    #[inline(always)]
+    #[tracing::instrument(skip(self), fields(src = ?self.src, dst = ?self.dst))]
+    pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
+        let assume_visibility = self.assume.visibility;
+        let query_or_answer = self.map_layouts(|src, dst, scope, context| {
+            // Remove all `Def` nodes from `src`, without checking their visibility.
+            let src = src.prune(&|def| true);
+
+            tracing::trace!(src = ?src, "pruned src");
+
+            // Remove all `Def` nodes from `dst`, additionally...
+            let dst = if assume_visibility {
+                // ...if visibility is assumed, don't check their visibility.
+                dst.prune(&|def| true)
+            } else {
+                // ...otherwise, prune away all unreachable paths through the `Dst` layout.
+                dst.prune(&|def| context.is_accessible_from(def, scope))
+            };
+
+            tracing::trace!(dst = ?dst, "pruned dst");
+
+            // Convert `src` from a tree-based representation to an NFA-based representation.
+            // If the conversion fails because `src` is uninhabited, conclude that the transmutation
+            // is acceptable, because instances of the `src` type do not exist.
+            let src = Nfa::from_tree(src).map_err(|Uninhabited| Answer::Yes)?;
+
+            // Convert `dst` from a tree-based representation to an NFA-based representation.
+            // If the conversion fails because `src` is uninhabited, conclude that the transmutation
+            // is unacceptable, because instances of the `dst` type do not exist.
+            let dst =
+                Nfa::from_tree(dst).map_err(|Uninhabited| Answer::No(Reason::DstIsPrivate))?;
+
+            Ok((src, dst))
+        });
+
+        match query_or_answer {
+            Ok(query) => query.answer(),
+            Err(answer) => answer,
+        }
+    }
+}
+
+impl<C> MaybeTransmutableQuery<Nfa<<C as QueryContext>::Ref>, C>
+where
+    C: QueryContext,
+{
+    /// Answers whether a `Nfa` is transmutable into another `Nfa`.
+    ///
+    /// This method converts `src` and `dst` to DFAs, then computes an answer using those DFAs.
+    #[inline(always)]
+    #[tracing::instrument(skip(self), fields(src = ?self.src, dst = ?self.dst))]
+    pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
+        let query_or_answer = self
+            .map_layouts(|src, dst, scope, context| Ok((Dfa::from_nfa(src), Dfa::from_nfa(dst))));
+
+        match query_or_answer {
+            Ok(query) => query.answer(),
+            Err(answer) => answer,
+        }
+    }
+}
+
+impl<C> MaybeTransmutableQuery<Dfa<<C as QueryContext>::Ref>, C>
+where
+    C: QueryContext,
+{
+    /// Answers whether a `Nfa` is transmutable into another `Nfa`.
+    ///
+    /// This method converts `src` and `dst` to DFAs, then computes an answer using those DFAs.
+    pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
+        MaybeTransmutableQuery {
+            src: &self.src,
+            dst: &self.dst,
+            scope: self.scope,
+            assume: self.assume,
+            context: self.context,
+        }
+        .answer()
+    }
+}
+
+impl<'l, C> MaybeTransmutableQuery<&'l Dfa<<C as QueryContext>::Ref>, C>
+where
+    C: QueryContext,
+{
+    pub(crate) fn answer(&mut self) -> Answer<<C as QueryContext>::Ref> {
+        self.answer_memo(&mut Map::default(), self.src.start, self.dst.start)
+    }
+
+    #[inline(always)]
+    #[tracing::instrument(skip(self))]
+    fn answer_memo(
+        &self,
+        cache: &mut Map<(dfa::State, dfa::State), Answer<<C as QueryContext>::Ref>>,
+        src_state: dfa::State,
+        dst_state: dfa::State,
+    ) -> Answer<<C as QueryContext>::Ref> {
+        if let Some(answer) = cache.get(&(src_state, dst_state)) {
+            answer.clone()
+        } else {
+            let answer = if dst_state == self.dst.accepting {
+                // truncation: `size_of(Src) >= size_of(Dst)`
+                Answer::Yes
+            } else if src_state == self.src.accepting {
+                // extension: `size_of(Src) >= size_of(Dst)`
+                if let Some(dst_state_prime) = self.dst.byte_from(dst_state, Byte::Uninit) {
+                    self.answer_memo(cache, src_state, dst_state_prime)
+                } else {
+                    Answer::No(Reason::DstIsTooBig)
+                }
+            } else {
+                let src_quantification = 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
+                } 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
+                };
+
+                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)
+                        }
+                    },
+                )
+            };
+            cache.insert((src_state, dst_state), answer.clone());
+            answer
+        }
+    }
+}
+
+impl<R> Answer<R>
+where
+    R: layout::Ref,
+{
+    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::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]),
+        }
+    }
+
+    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),
+            (Self::IfAny(mut lhs), Self::IfAny(ref mut rhs)) => {
+                lhs.append(rhs);
+                Self::IfAny(lhs)
+            }
+            (constraint, Self::IfAny(mut constraints))
+            | (Self::IfAny(mut constraints), constraint) => {
+                constraints.push(constraint);
+                Self::IfAny(constraints)
+            }
+            (lhs, rhs) => Self::IfAny(vec![lhs, rhs]),
+        }
+    }
+}
+
+pub fn for_all<R, I, F>(iter: I, f: F) -> Answer<R>
+where
+    R: layout::Ref,
+    I: IntoIterator,
+    F: FnMut(<I as IntoIterator>::Item) -> Answer<R>,
+{
+    use std::ops::ControlFlow::{Break, Continue};
+    let (Continue(result) | Break(result)) =
+        iter.into_iter().map(f).try_fold(Answer::Yes, |constraints, constraint| {
+            match constraint.and(constraints) {
+                Answer::No(reason) => Break(Answer::No(reason)),
+                maybe => Continue(maybe),
+            }
+        });
+    result
+}
+
+pub fn there_exists<R, I, F>(iter: I, f: F) -> Answer<R>
+where
+    R: layout::Ref,
+    I: IntoIterator,
+    F: FnMut(<I as IntoIterator>::Item) -> Answer<R>,
+{
+    use std::ops::ControlFlow::{Break, Continue};
+    let (Continue(result) | Break(result)) = iter.into_iter().map(f).try_fold(
+        Answer::No(Reason::DstIsBitIncompatible),
+        |constraints, constraint| match constraint.or(constraints) {
+            Answer::Yes => Break(Answer::Yes),
+            maybe => Continue(maybe),
+        },
+    );
+    result
+}
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
new file mode 100644
index 00000000000..ab9bcd232f0
--- /dev/null
+++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
@@ -0,0 +1,93 @@
+use crate::layout;
+
+/// Context necessary to answer the question "Are these types transmutable?".
+pub(crate) trait QueryContext {
+    type Def: layout::Def;
+    type Ref: layout::Ref;
+    type Scope: Copy;
+
+    /// Is `def` accessible from the defining module of `scope`?
+    fn is_accessible_from(&self, def: Self::Def, scope: Self::Scope) -> bool;
+
+    fn min_align(&self, reference: Self::Ref) -> usize;
+}
+
+#[cfg(test)]
+pub(crate) mod test {
+    use super::QueryContext;
+
+    pub(crate) struct UltraMinimal;
+
+    #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
+    pub(crate) enum Def {
+        Visible,
+        Invisible,
+    }
+
+    impl crate::layout::Def for Def {}
+
+    impl QueryContext for UltraMinimal {
+        type Def = Def;
+        type Ref = !;
+        type Scope = ();
+
+        fn is_accessible_from(&self, def: Def, scope: ()) -> bool {
+            matches!(Def::Visible, def)
+        }
+
+        fn min_align(&self, reference: !) -> usize {
+            unimplemented!()
+        }
+    }
+}
+
+#[cfg(feature = "rustc")]
+mod rustc {
+    use super::*;
+    use rustc_middle::ty::{Ty, TyCtxt};
+
+    impl<'tcx> super::QueryContext for TyCtxt<'tcx> {
+        type Def = layout::rustc::Def<'tcx>;
+        type Ref = layout::rustc::Ref<'tcx>;
+
+        type Scope = Ty<'tcx>;
+
+        #[tracing::instrument(skip(self))]
+        fn is_accessible_from(&self, def: Self::Def, scope: Self::Scope) -> bool {
+            use layout::rustc::Def;
+            use rustc_middle::ty;
+
+            let parent = if let ty::Adt(adt_def, ..) = scope.kind() {
+                use rustc_middle::ty::DefIdTree;
+                let parent = self.parent(adt_def.did());
+                parent
+            } else {
+                // Is this always how we want to handle a non-ADT scope?
+                return false;
+            };
+
+            let def_id = match def {
+                Def::Adt(adt_def) => adt_def.did(),
+                Def::Variant(variant_def) => variant_def.def_id,
+                Def::Field(field_def) => field_def.did,
+                Def::Primitive => {
+                    // primitives do not have a def_id, but they're always accessible
+                    return true;
+                }
+            };
+
+            let ret = if self.visibility(def_id).is_accessible_from(parent, *self) {
+                true
+            } else {
+                false
+            };
+
+            tracing::trace!(ret = ?ret, "ret");
+            ret
+        }
+
+        fn min_align(&self, reference: Self::Ref) -> usize {
+            unimplemented!()
+        }
+    }
+}
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
new file mode 100644
index 00000000000..d9d125687f6
--- /dev/null
+++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
@@ -0,0 +1,115 @@
+use super::query_context::test::{Def, UltraMinimal};
+use crate::maybe_transmutable::MaybeTransmutableQuery;
+use crate::{layout, Answer, Reason, Set};
+use itertools::Itertools;
+
+mod bool {
+    use super::*;
+
+    #[test]
+    fn should_permit_identity_transmutation_tree() {
+        println!("{:?}", layout::Tree::<!, !>::bool());
+        let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new(
+            layout::Tree::<Def, !>::bool(),
+            layout::Tree::<Def, !>::bool(),
+            (),
+            crate::Assume { alignment: false, lifetimes: false, validity: true, visibility: false },
+            UltraMinimal,
+        )
+        .answer();
+        assert_eq!(answer, Answer::Yes);
+    }
+
+    #[test]
+    fn should_permit_identity_transmutation_dfa() {
+        let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new(
+            layout::Dfa::<!>::bool(),
+            layout::Dfa::<!>::bool(),
+            (),
+            crate::Assume { alignment: false, lifetimes: false, validity: true, visibility: false },
+            UltraMinimal,
+        )
+        .answer();
+        assert_eq!(answer, Answer::Yes);
+    }
+
+    #[test]
+    fn should_permit_validity_expansion_and_reject_contraction() {
+        let un = layout::Tree::<Def, !>::uninhabited();
+        let b0 = layout::Tree::<Def, !>::from_bits(0);
+        let b1 = layout::Tree::<Def, !>::from_bits(1);
+        let b2 = layout::Tree::<Def, !>::from_bits(2);
+
+        let alts = [b0, b1, b2];
+
+        let into_layout = |alts: Vec<_>| {
+            alts.into_iter().fold(layout::Tree::<Def, !>::uninhabited(), layout::Tree::<Def, !>::or)
+        };
+
+        let into_set = |alts: Vec<_>| {
+            #[cfg(feature = "rustc")]
+            let mut set = Set::default();
+            #[cfg(not(feature = "rustc"))]
+            let mut set = Set::new();
+            set.extend(alts);
+            set
+        };
+
+        for src_alts in alts.clone().into_iter().powerset() {
+            let src_layout = into_layout(src_alts.clone());
+            let src_set = into_set(src_alts.clone());
+
+            for dst_alts in alts.clone().into_iter().powerset().filter(|alts| !alts.is_empty()) {
+                let dst_layout = into_layout(dst_alts.clone());
+                let dst_set = into_set(dst_alts.clone());
+
+                if src_set.is_subset(&dst_set) {
+                    assert_eq!(
+                        Answer::Yes,
+                        MaybeTransmutableQuery::new(
+                            src_layout.clone(),
+                            dst_layout.clone(),
+                            (),
+                            crate::Assume { validity: false, ..crate::Assume::default() },
+                            UltraMinimal,
+                        )
+                        .answer(),
+                        "{:?} SHOULD be transmutable into {:?}",
+                        src_layout,
+                        dst_layout
+                    );
+                } else if !src_set.is_disjoint(&dst_set) {
+                    assert_eq!(
+                        Answer::Yes,
+                        MaybeTransmutableQuery::new(
+                            src_layout.clone(),
+                            dst_layout.clone(),
+                            (),
+                            crate::Assume { validity: true, ..crate::Assume::default() },
+                            UltraMinimal,
+                        )
+                        .answer(),
+                        "{:?} SHOULD be transmutable (assuming validity) into {:?}",
+                        src_layout,
+                        dst_layout
+                    );
+                } else {
+                    assert_eq!(
+                        Answer::No(Reason::DstIsBitIncompatible),
+                        MaybeTransmutableQuery::new(
+                            src_layout.clone(),
+                            dst_layout.clone(),
+                            (),
+                            crate::Assume { validity: false, ..crate::Assume::default() },
+                            UltraMinimal,
+                        )
+                        .answer(),
+                        "{:?} should NOT be transmutable into {:?}",
+                        src_layout,
+                        dst_layout
+                    );
+                }
+            }
+        }
+    }
+}
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 234fa213da8..add65a3be50 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -27,6 +27,10 @@ mod valid_align;
 // alignment as a parameter, such as `Layout::padding_needed_for`.
 pub(crate) use valid_align::ValidAlign;
 
+mod transmutability;
+#[unstable(feature = "transmutability", issue = "none")]
+pub use transmutability::{Assume, BikeshedIntrinsicFrom};
+
 #[stable(feature = "rust1", since = "1.0.0")]
 #[doc(inline)]
 pub use crate::intrinsics::transmute;
diff --git a/library/core/src/mem/transmutability.rs b/library/core/src/mem/transmutability.rs
new file mode 100644
index 00000000000..52342f8a0ec
--- /dev/null
+++ b/library/core/src/mem/transmutability.rs
@@ -0,0 +1,39 @@
+/// Are values of a type transmutable into values of another type?
+///
+/// This trait is implemented on-the-fly by the compiler for types `Src` and `Self` when the bits of
+/// any value of type `Self` are safely transmutable into a value of type `Dst`, in a given `Context`,
+/// notwithstanding whatever safety checks you have asked the compiler to [`Assume`] are satisfied.
+#[unstable(feature = "transmutability", issue = "none")]
+#[cfg_attr(not(bootstrap), lang = "transmute_trait")]
+pub unsafe trait BikeshedIntrinsicFrom<
+    Src,
+    Context,
+    const ASSUME_ALIGNMENT: bool,
+    const ASSUME_LIFETIMES: bool,
+    const ASSUME_VALIDITY: bool,
+    const ASSUME_VISIBILITY: bool,
+> where
+    Src: ?Sized,
+{
+}
+
+/// What transmutation safety conditions shall the compiler assume that *you* are checking?
+#[unstable(feature = "transmutability", issue = "none")]
+#[derive(PartialEq, Eq, Clone, Copy, Debug)]
+pub struct Assume {
+    /// When `true`, the compiler assumes that *you* are ensuring (either dynamically or statically) that
+    /// destination referents do not have stricter alignment requirements than source referents.
+    pub alignment: bool,
+
+    /// When `true`, the compiler assume that *you* are ensuring that lifetimes are not extended in a manner
+    /// that violates Rust's memory model.
+    pub lifetimes: bool,
+
+    /// When `true`, the compiler assumes that *you* are ensuring that the source type is actually a valid
+    /// instance of the destination type.
+    pub validity: bool,
+
+    /// When `true`, the compiler assumes that *you* have ensured that it is safe for you to violate the
+    /// type and field privacy of the destination type (and sometimes of the source type, too).
+    pub visibility: bool,
+}
diff --git a/src/test/ui/transmutability/abstraction/const_generic_fn.rs b/src/test/ui/transmutability/abstraction/const_generic_fn.rs
new file mode 100644
index 00000000000..94c38bb28f7
--- /dev/null
+++ b/src/test/ui/transmutability/abstraction/const_generic_fn.rs
@@ -0,0 +1,41 @@
+// check-pass
+//! An array must have the correct length.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn array_like<T, E, const N: usize>()
+    where
+        T: BikeshedIntrinsicFrom<[E; N], Context, false, false, false, true>,
+        [E; N]: BikeshedIntrinsicFrom<T, Context, false, false, false, true>
+    {}
+}
+
+fn len_0() {
+    type Array = [u8; 0];
+    #[repr(C)] struct Struct();
+    assert::array_like::<Struct, u8, 0>();
+}
+
+fn len_1() {
+    type Array = [u8; 1];
+    #[repr(C)] struct Struct(u8);
+    assert::array_like::<Struct, u8, 1>();
+}
+
+fn len_2() {
+    type Array = [u8; 2];
+    #[repr(C)] struct Struct(u8, u8);
+    assert::array_like::<Struct, u8, 2>();
+}
+
+fn len_3() {
+    type Array = [u8; 3];
+    #[repr(C)] struct Struct(u8, u8, u8);
+    assert::array_like::<Struct, u8, 3>();
+}
diff --git a/src/test/ui/transmutability/arrays/should_have_correct_length.rs b/src/test/ui/transmutability/arrays/should_have_correct_length.rs
new file mode 100644
index 00000000000..bfe6d830a1b
--- /dev/null
+++ b/src/test/ui/transmutability/arrays/should_have_correct_length.rs
@@ -0,0 +1,44 @@
+// check-pass
+//! An array must have the correct length.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+fn should_have_len_0() {
+    type Array = [u8; 0];
+    #[repr(C)] struct Struct();
+    assert::is_maybe_transmutable::<Array, Struct>();
+    assert::is_maybe_transmutable::<Struct, Array>();
+}
+
+fn should_have_len_1() {
+    type Array = [u8; 1];
+    #[repr(C)] struct Struct(u8);
+    assert::is_maybe_transmutable::<Array, Struct>();
+    assert::is_maybe_transmutable::<Struct, Array>();
+}
+
+fn should_have_len_2() {
+    type Array = [u8; 2];
+    #[repr(C)] struct Struct(u8, u8);
+    assert::is_maybe_transmutable::<Array, Struct>();
+    assert::is_maybe_transmutable::<Struct, Array>();
+}
+
+fn should_have_len_3() {
+    type Array = [u8; 3];
+    #[repr(C)] struct Struct(u8, u8, u8);
+    assert::is_maybe_transmutable::<Array, Struct>();
+    assert::is_maybe_transmutable::<Struct, Array>();
+}
diff --git a/src/test/ui/transmutability/arrays/should_inherit_alignment.rs b/src/test/ui/transmutability/arrays/should_inherit_alignment.rs
new file mode 100644
index 00000000000..fcb1765ea6b
--- /dev/null
+++ b/src/test/ui/transmutability/arrays/should_inherit_alignment.rs
@@ -0,0 +1,55 @@
+// check-pass
+//! An array must inherit the alignment of its inner type.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+#[derive(Clone, Copy)] #[repr(u8)] enum Ox00 { V = 0x00 }
+#[derive(Clone, Copy)] #[repr(u8)] enum Ox01 { V = 0x01 }
+#[derive(Clone, Copy)] #[repr(u8)] enum OxFF { V = 0xFF }
+
+#[repr(C)]
+union Uninit {
+    a: (),
+    b: OxFF,
+}
+
+#[repr(C, align(2))] struct align_2(Ox00);
+
+fn len_0() {
+    #[repr(C)] struct ImplicitlyPadded([align_2; 0], Ox01);
+    #[repr(C)] struct ExplicitlyPadded(Ox01, Uninit);
+
+    #[repr(C)] struct Struct();
+    assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>();
+    assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>();
+}
+
+fn len_1() {
+    #[repr(C)] struct ImplicitlyPadded([align_2; 1], Ox01);
+    #[repr(C)] struct ExplicitlyPadded(Ox00, Uninit, Ox01, Uninit);
+
+    #[repr(C)] struct Struct();
+    assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>();
+    assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>();
+}
+
+fn len_2() {
+    #[repr(C)] struct ImplicitlyPadded([align_2; 2], Ox01);
+    #[repr(C)] struct ExplicitlyPadded(Ox00, Uninit, Ox00, Uninit, Ox01, Uninit);
+
+    #[repr(C)] struct Struct();
+    assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>();
+    assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>();
+}
diff --git a/src/test/ui/transmutability/arrays/should_require_well_defined_layout.rs b/src/test/ui/transmutability/arrays/should_require_well_defined_layout.rs
new file mode 100644
index 00000000000..36f9ceb0da7
--- /dev/null
+++ b/src/test/ui/transmutability/arrays/should_require_well_defined_layout.rs
@@ -0,0 +1,61 @@
+//! An array must have a well-defined layout to participate in a transmutation.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+fn should_reject_repr_rust()
+{
+    fn unit() {
+        type repr_rust = [String; 0];
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+
+    fn singleton() {
+        type repr_rust = [String; 1];
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+
+    fn duplex() {
+        type repr_rust = [String; 2];
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+}
+
+fn should_accept_repr_C()
+{
+    fn unit() {
+        #[repr(C)] struct repr_c(u8, u16, u8);
+        type array = [repr_c; 0];
+        assert::is_maybe_transmutable::<array, ()>();
+        assert::is_maybe_transmutable::<i128, array>();
+    }
+
+    fn singleton() {
+        #[repr(C)] struct repr_c(u8, u16, u8);
+        type array = [repr_c; 1];
+        assert::is_maybe_transmutable::<array, repr_c>();
+        assert::is_maybe_transmutable::<repr_c, array>();
+    }
+
+    fn duplex() {
+        #[repr(C)] struct repr_c(u8, u16, u8);
+        #[repr(C)] struct duplex(repr_c, repr_c);
+        type array = [repr_c; 2];
+        assert::is_maybe_transmutable::<array, duplex>();
+        assert::is_maybe_transmutable::<duplex, array>();
+    }
+}
diff --git a/src/test/ui/transmutability/arrays/should_require_well_defined_layout.stderr b/src/test/ui/transmutability/arrays/should_require_well_defined_layout.stderr
new file mode 100644
index 00000000000..109e58c4093
--- /dev/null
+++ b/src/test/ui/transmutability/arrays/should_require_well_defined_layout.stderr
@@ -0,0 +1,93 @@
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<[String; 0], assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:21:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<[String; 0], assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `[String; 0]: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:22:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `[String; 0]`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<[String; 1], assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:27:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<[String; 1], assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `[String; 1]: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:28:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `[String; 1]`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<[String; 2], assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:33:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<[String; 2], assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `[String; 2]: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:34:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `[String; 2]`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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/src/test/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.rs b/src/test/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.rs
new file mode 100644
index 00000000000..cd411a4b838
--- /dev/null
+++ b/src/test/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.rs
@@ -0,0 +1,149 @@
+//! An enum with a primitive repr should have exactly the size of that primitive.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+#[repr(C)]
+struct Zst;
+
+#[derive(Clone, Copy)]
+#[repr(i8)] enum V0i8 { V }
+#[repr(u8)] enum V0u8 { V }
+#[repr(i16)] enum V0i16 { V }
+#[repr(u16)] enum V0u16 { V }
+#[repr(i32)] enum V0i32 { V }
+#[repr(u32)] enum V0u32 { V }
+#[repr(i64)] enum V0i64 { V }
+#[repr(u64)] enum V0u64 { V }
+#[repr(isize)] enum V0isize { V }
+#[repr(usize)] enum V0usize { V }
+
+fn n8() {
+    struct Context;
+
+    type Smaller = Zst;
+    type Analog = u8;
+    type Larger = u16;
+
+    fn i_should_have_correct_length() {
+        type Current = V0i8;
+
+        assert::is_transmutable::<Smaller, Current, Context>(); //~ ERROR not satisfied
+        assert::is_transmutable::<Current, Analog, Context>();
+        assert::is_transmutable::<Current, Larger, Context>(); //~ ERROR not satisfied
+    }
+
+    fn u_should_have_correct_length() {
+        type Current = V0u8;
+
+        assert::is_transmutable::<Smaller, Current, Context>(); //~ ERROR not satisfied
+        assert::is_transmutable::<Current, Analog, Context>();
+        assert::is_transmutable::<Current, Larger, Context>(); //~ ERROR not satisfied
+    }
+}
+
+fn n16() {
+    struct Context;
+
+    type Smaller = u8;
+    type Analog = u16;
+    type Larger = u32;
+
+    fn i_should_have_correct_length() {
+        type Current = V0i16;
+
+        assert::is_transmutable::<Smaller, Current, Context>(); //~ ERROR not satisfied
+        assert::is_transmutable::<Current, Analog, Context>();
+        assert::is_transmutable::<Current, Larger, Context>(); //~ ERROR not satisfied
+    }
+
+    fn u_should_have_correct_length() {
+        type Current = V0u16;
+
+        assert::is_transmutable::<Smaller, Current, Context>(); //~ ERROR not satisfied
+        assert::is_transmutable::<Current, Analog, Context>();
+        assert::is_transmutable::<Current, Larger, Context>(); //~ ERROR not satisfied
+    }
+}
+
+fn n32() {
+    struct Context;
+
+    type Smaller = u16;
+    type Analog = u32;
+    type Larger = u64;
+
+    fn i_should_have_correct_length() {
+        type Current = V0i32;
+
+        assert::is_transmutable::<Smaller, Current, Context>(); //~ ERROR not satisfied
+        assert::is_transmutable::<Current, Analog, Context>();
+        assert::is_transmutable::<Current, Larger, Context>(); //~ ERROR not satisfied
+    }
+
+    fn u_should_have_correct_length() {
+        type Current = V0u32;
+
+        assert::is_transmutable::<Smaller, Current, Context>(); //~ ERROR not satisfied
+        assert::is_transmutable::<Current, Analog, Context>();
+        assert::is_transmutable::<Current, Larger, Context>(); //~ ERROR not satisfied
+    }
+}
+
+fn n64() {
+    struct Context;
+
+    type Smaller = u32;
+    type Analog = u64;
+    type Larger = u128;
+
+    fn i_should_have_correct_length() {
+        type Current = V0i64;
+
+        assert::is_transmutable::<Smaller, Current, Context>(); //~ ERROR not satisfied
+        assert::is_transmutable::<Current, Analog, Context>();
+        assert::is_transmutable::<Current, Larger, Context>(); //~ ERROR not satisfied
+    }
+
+    fn u_should_have_correct_length() {
+        type Current = V0u64;
+
+        assert::is_transmutable::<Smaller, Current, Context>(); //~ ERROR not satisfied
+        assert::is_transmutable::<Current, Analog, Context>();
+        assert::is_transmutable::<Current, Larger, Context>(); //~ ERROR not satisfied
+    }
+}
+
+fn nsize() {
+    struct Context;
+
+    type Smaller = u8;
+    type Analog = usize;
+    type Larger = [usize; 2];
+
+    fn i_should_have_correct_length() {
+        type Current = V0isize;
+
+        assert::is_transmutable::<Smaller, Current, Context>(); //~ ERROR not satisfied
+        assert::is_transmutable::<Current, Analog, Context>();
+        assert::is_transmutable::<Current, Larger, Context>(); //~ ERROR not satisfied
+    }
+
+    fn u_should_have_correct_length() {
+        type Current = V0usize;
+
+        assert::is_transmutable::<Smaller, Current, Context>(); //~ ERROR not satisfied
+        assert::is_transmutable::<Current, Analog, Context>();
+        assert::is_transmutable::<Current, Larger, Context>(); //~ ERROR not satisfied
+    }
+}
diff --git a/src/test/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.stderr b/src/test/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.stderr
new file mode 100644
index 00000000000..da24c6a021e
--- /dev/null
+++ b/src/test/ui/transmutability/enums/repr/primitive_reprs_should_have_correct_length.stderr
@@ -0,0 +1,303 @@
+error[E0277]: the trait bound `V0i8: BikeshedIntrinsicFrom<Zst, n8::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:41:44
+   |
+LL |         assert::is_transmutable::<Smaller, Current, Context>();
+   |                                            ^^^^^^^ the trait `BikeshedIntrinsicFrom<Zst, n8::Context, true, true, true, true>` is not implemented for `V0i8`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u16: BikeshedIntrinsicFrom<V0i8, n8::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:43:44
+   |
+LL |         assert::is_transmutable::<Current, Larger, Context>();
+   |                                            ^^^^^^ the trait `BikeshedIntrinsicFrom<V0i8, n8::Context, true, true, true, true>` is not implemented for `u16`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `V0u8: BikeshedIntrinsicFrom<Zst, n8::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:49:44
+   |
+LL |         assert::is_transmutable::<Smaller, Current, Context>();
+   |                                            ^^^^^^^ the trait `BikeshedIntrinsicFrom<Zst, n8::Context, true, true, true, true>` is not implemented for `V0u8`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u16: BikeshedIntrinsicFrom<V0u8, n8::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:51:44
+   |
+LL |         assert::is_transmutable::<Current, Larger, Context>();
+   |                                            ^^^^^^ the trait `BikeshedIntrinsicFrom<V0u8, n8::Context, true, true, true, true>` is not implemented for `u16`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `V0i16: BikeshedIntrinsicFrom<u8, n16::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:65:44
+   |
+LL |         assert::is_transmutable::<Smaller, Current, Context>();
+   |                                            ^^^^^^^ the trait `BikeshedIntrinsicFrom<u8, n16::Context, true, true, true, true>` is not implemented for `V0i16`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u32: BikeshedIntrinsicFrom<V0i16, n16::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:67:44
+   |
+LL |         assert::is_transmutable::<Current, Larger, Context>();
+   |                                            ^^^^^^ the trait `BikeshedIntrinsicFrom<V0i16, n16::Context, true, true, true, true>` is not implemented for `u32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `V0u16: BikeshedIntrinsicFrom<u8, n16::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:73:44
+   |
+LL |         assert::is_transmutable::<Smaller, Current, Context>();
+   |                                            ^^^^^^^ the trait `BikeshedIntrinsicFrom<u8, n16::Context, true, true, true, true>` is not implemented for `V0u16`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u32: BikeshedIntrinsicFrom<V0u16, n16::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:75:44
+   |
+LL |         assert::is_transmutable::<Current, Larger, Context>();
+   |                                            ^^^^^^ the trait `BikeshedIntrinsicFrom<V0u16, n16::Context, true, true, true, true>` is not implemented for `u32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `V0i32: BikeshedIntrinsicFrom<u16, n32::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:89:44
+   |
+LL |         assert::is_transmutable::<Smaller, Current, Context>();
+   |                                            ^^^^^^^ the trait `BikeshedIntrinsicFrom<u16, n32::Context, true, true, true, true>` is not implemented for `V0i32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u64: BikeshedIntrinsicFrom<V0i32, n32::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:91:44
+   |
+LL |         assert::is_transmutable::<Current, Larger, Context>();
+   |                                            ^^^^^^ the trait `BikeshedIntrinsicFrom<V0i32, n32::Context, true, true, true, true>` is not implemented for `u64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `V0u32: BikeshedIntrinsicFrom<u16, n32::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:97:44
+   |
+LL |         assert::is_transmutable::<Smaller, Current, Context>();
+   |                                            ^^^^^^^ the trait `BikeshedIntrinsicFrom<u16, n32::Context, true, true, true, true>` is not implemented for `V0u32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u64: BikeshedIntrinsicFrom<V0u32, n32::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:99:44
+   |
+LL |         assert::is_transmutable::<Current, Larger, Context>();
+   |                                            ^^^^^^ the trait `BikeshedIntrinsicFrom<V0u32, n32::Context, true, true, true, true>` is not implemented for `u64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `V0i64: BikeshedIntrinsicFrom<u32, n64::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:113:44
+   |
+LL |         assert::is_transmutable::<Smaller, Current, Context>();
+   |                                            ^^^^^^^ the trait `BikeshedIntrinsicFrom<u32, n64::Context, true, true, true, true>` is not implemented for `V0i64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<V0i64, n64::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:115:44
+   |
+LL |         assert::is_transmutable::<Current, Larger, Context>();
+   |                                            ^^^^^^ the trait `BikeshedIntrinsicFrom<V0i64, n64::Context, true, true, true, true>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `V0u64: BikeshedIntrinsicFrom<u32, n64::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:121:44
+   |
+LL |         assert::is_transmutable::<Smaller, Current, Context>();
+   |                                            ^^^^^^^ the trait `BikeshedIntrinsicFrom<u32, n64::Context, true, true, true, true>` is not implemented for `V0u64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<V0u64, n64::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:123:44
+   |
+LL |         assert::is_transmutable::<Current, Larger, Context>();
+   |                                            ^^^^^^ the trait `BikeshedIntrinsicFrom<V0u64, n64::Context, true, true, true, true>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `V0isize: BikeshedIntrinsicFrom<u8, nsize::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:137:44
+   |
+LL |         assert::is_transmutable::<Smaller, Current, Context>();
+   |                                            ^^^^^^^ the trait `BikeshedIntrinsicFrom<u8, nsize::Context, true, true, true, true>` is not implemented for `V0isize`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `[usize; 2]: BikeshedIntrinsicFrom<V0isize, nsize::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:139:44
+   |
+LL |         assert::is_transmutable::<Current, Larger, Context>();
+   |                                            ^^^^^^ the trait `BikeshedIntrinsicFrom<V0isize, nsize::Context, true, true, true, true>` is not implemented for `[usize; 2]`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `V0usize: BikeshedIntrinsicFrom<u8, nsize::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:145:44
+   |
+LL |         assert::is_transmutable::<Smaller, Current, Context>();
+   |                                            ^^^^^^^ the trait `BikeshedIntrinsicFrom<u8, nsize::Context, true, true, true, true>` is not implemented for `V0usize`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `[usize; 2]: BikeshedIntrinsicFrom<V0usize, nsize::Context, true, true, true, true>` is not satisfied
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:147:44
+   |
+LL |         assert::is_transmutable::<Current, Larger, Context>();
+   |                                            ^^^^^^ the trait `BikeshedIntrinsicFrom<V0usize, nsize::Context, true, true, true, true>` is not implemented for `[usize; 2]`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/primitive_reprs_should_have_correct_length.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to 20 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/enums/repr/should_require_well_defined_layout.rs b/src/test/ui/transmutability/enums/repr/should_require_well_defined_layout.rs
new file mode 100644
index 00000000000..24a88d6ac95
--- /dev/null
+++ b/src/test/ui/transmutability/enums/repr/should_require_well_defined_layout.rs
@@ -0,0 +1,117 @@
+//! An enum must have a well-defined layout to participate in a transmutation.
+
+#![crate_type = "lib"]
+#![feature(repr128)]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+fn should_reject_repr_rust() {
+    fn void() {
+        enum repr_rust {}
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+
+    fn singleton() {
+        enum repr_rust { V }
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+
+    fn duplex() {
+        enum repr_rust { A, B }
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+}
+
+fn should_accept_primitive_reprs()
+{
+    fn should_accept_repr_i8() {
+        #[repr(i8)] enum repr_i8 { V }
+        assert::is_maybe_transmutable::<repr_i8, ()>();
+        assert::is_maybe_transmutable::<i8, repr_i8>();
+    }
+
+    fn should_accept_repr_u8() {
+        #[repr(u8)] enum repr_u8 { V }
+        assert::is_maybe_transmutable::<repr_u8, ()>();
+        assert::is_maybe_transmutable::<u8, repr_u8>();
+    }
+
+    fn should_accept_repr_i16() {
+        #[repr(i16)] enum repr_i16 { V }
+        assert::is_maybe_transmutable::<repr_i16, ()>();
+        assert::is_maybe_transmutable::<i16, repr_i16>();
+    }
+
+    fn should_accept_repr_u16() {
+        #[repr(u16)] enum repr_u16 { V }
+        assert::is_maybe_transmutable::<repr_u16, ()>();
+        assert::is_maybe_transmutable::<u16, repr_u16>();
+    }
+
+    fn should_accept_repr_i32() {
+        #[repr(i32)] enum repr_i32 { V }
+        assert::is_maybe_transmutable::<repr_i32, ()>();
+        assert::is_maybe_transmutable::<i32, repr_i32>();
+    }
+
+    fn should_accept_repr_u32() {
+        #[repr(u32)] enum repr_u32 { V }
+        assert::is_maybe_transmutable::<repr_u32, ()>();
+        assert::is_maybe_transmutable::<u32, repr_u32>();
+    }
+
+    fn should_accept_repr_i64() {
+        #[repr(i64)] enum repr_i64 { V }
+        assert::is_maybe_transmutable::<repr_i64, ()>();
+        assert::is_maybe_transmutable::<i64, repr_i64>();
+    }
+
+    fn should_accept_repr_u64() {
+        #[repr(u64)] enum repr_u64 { V }
+        assert::is_maybe_transmutable::<repr_u64, ()>();
+        assert::is_maybe_transmutable::<u64, repr_u64>();
+    }
+
+    fn should_accept_repr_i128() {
+        #[repr(i128)] enum repr_i128 { V }
+        assert::is_maybe_transmutable::<repr_i128, ()>();
+        assert::is_maybe_transmutable::<i128, repr_i128>();
+    }
+
+    fn should_accept_repr_u128() {
+        #[repr(u128)] enum repr_u128 { V }
+        assert::is_maybe_transmutable::<repr_u128, ()>();
+        assert::is_maybe_transmutable::<u128, repr_u128>();
+    }
+
+    fn should_accept_repr_isize() {
+        #[repr(isize)] enum repr_isize { V }
+        assert::is_maybe_transmutable::<repr_isize, ()>();
+        assert::is_maybe_transmutable::<isize, repr_isize>();
+    }
+
+    fn should_accept_repr_usize() {
+        #[repr(usize)] enum repr_usize { V }
+        assert::is_maybe_transmutable::<repr_usize, ()>();
+        assert::is_maybe_transmutable::<usize, repr_usize>();
+    }
+}
+
+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/src/test/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr b/src/test/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr
new file mode 100644
index 00000000000..1bfbff68f06
--- /dev/null
+++ b/src/test/ui/transmutability/enums/repr/should_require_well_defined_layout.stderr
@@ -0,0 +1,93 @@
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<void::repr_rust, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:21:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<void::repr_rust, assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/should_require_well_defined_layout.rs:14:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `void::repr_rust: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:22:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `void::repr_rust`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/should_require_well_defined_layout.rs:14:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<singleton::repr_rust, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:27:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<singleton::repr_rust, assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/should_require_well_defined_layout.rs:14:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `singleton::repr_rust: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:28:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `singleton::repr_rust`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/should_require_well_defined_layout.rs:14:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<duplex::repr_rust, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:33:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<duplex::repr_rust, assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/should_require_well_defined_layout.rs:14:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `duplex::repr_rust: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:34:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `duplex::repr_rust`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/should_require_well_defined_layout.rs:14:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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/src/test/ui/transmutability/enums/should_order_correctly.rs b/src/test/ui/transmutability/enums/should_order_correctly.rs
new file mode 100644
index 00000000000..6558d2658ee
--- /dev/null
+++ b/src/test/ui/transmutability/enums/should_order_correctly.rs
@@ -0,0 +1,32 @@
+// check-pass
+//! The payloads of an enum variant should be ordered after its tag.
+
+#![crate_type = "lib"]
+#![feature(arbitrary_enum_discriminant)]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+#[repr(u8)] enum V0 { V = 0 }
+#[repr(u8)] enum V1 { V = 1 }
+#[repr(u8)] enum V2 { V = 2 }
+
+#[repr(u8)] enum E01 { V0(V1) = 0u8 }
+#[repr(u8)] enum E012 { V0(V1, V2) = 0u8 }
+
+fn should_order_tag_and_fields_correctly() {
+    // An implementation that (incorrectly) arranges E01 as [0x01, 0x00] will,
+    // in principle, reject this transmutation.
+    assert::is_transmutable::<E01, V0>();
+    // Again, but with one more field.
+    assert::is_transmutable::<E012, E01>();
+}
diff --git a/src/test/ui/transmutability/enums/should_pad_variants.rs b/src/test/ui/transmutability/enums/should_pad_variants.rs
new file mode 100644
index 00000000000..87951586523
--- /dev/null
+++ b/src/test/ui/transmutability/enums/should_pad_variants.rs
@@ -0,0 +1,40 @@
+//! The variants of an enum must be padded with uninit bytes such that they have
+//! the same length (in bytes).
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)] struct Zst;
+
+#[derive(Clone, Copy)]
+#[repr(u8)] enum V0 { V = 0 }
+
+#[derive(Clone, Copy)]
+#[repr(u8)] enum V2 { V = 2 }
+
+#[repr(C, u8)]
+enum Lopsided {
+    Smol(Zst),
+    Lorg(V0),
+}
+
+#[repr(C)] struct Src(V0, Zst, V2);
+#[repr(C)] struct Dst(Lopsided, V2);
+
+fn should_pad_variants() {
+    struct Context;
+    // If the implementation (incorrectly) fails to pad `Lopsided::Smol` with
+    // an uninitialized byte, this transmutation might be (wrongly) accepted:
+    assert::is_transmutable::<Src, Dst, Context>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/enums/should_pad_variants.stderr b/src/test/ui/transmutability/enums/should_pad_variants.stderr
new file mode 100644
index 00000000000..b940ca077d4
--- /dev/null
+++ b/src/test/ui/transmutability/enums/should_pad_variants.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `Dst: BikeshedIntrinsicFrom<Src, should_pad_variants::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_pad_variants.rs:39:36
+   |
+LL |     assert::is_transmutable::<Src, Dst, Context>();
+   |                                    ^^^ the trait `BikeshedIntrinsicFrom<Src, should_pad_variants::Context, true, true, true, true>` is not implemented for `Dst`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/should_pad_variants.rs:13:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/enums/should_respect_endianness.rs b/src/test/ui/transmutability/enums/should_respect_endianness.rs
new file mode 100644
index 00000000000..9bab44e1d91
--- /dev/null
+++ b/src/test/ui/transmutability/enums/should_respect_endianness.rs
@@ -0,0 +1,33 @@
+//! The target endianness should be a consideration in computing the layout of
+//! an enum with a multi-byte tag.
+
+#![crate_type = "lib"]
+#![feature(arbitrary_enum_discriminant)]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+#[repr(u16)] enum Src { V = 0xCAFE }
+
+#[repr(u8)] enum OxCA { V = 0xCA }
+#[repr(u8)] enum OxFE { V = 0xFE }
+
+#[cfg(target_endian = "big")] #[repr(C)] struct Expected(OxCA, OxFE);
+#[cfg(target_endian = "big")] #[repr(C)] struct Unexpected(OxFE, OxCA);
+
+#[cfg(target_endian = "little")] #[repr(C)] struct Expected(OxFE, OxCA);
+#[cfg(target_endian = "little")] #[repr(C)] struct Unexpected(OxCA, OxFE);
+
+fn should_respect_endianness() {
+    assert::is_transmutable::<Src, Expected>();
+    assert::is_transmutable::<Src, Unexpected>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/enums/should_respect_endianness.stderr b/src/test/ui/transmutability/enums/should_respect_endianness.stderr
new file mode 100644
index 00000000000..3f3335d4a1b
--- /dev/null
+++ b/src/test/ui/transmutability/enums/should_respect_endianness.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `Unexpected: BikeshedIntrinsicFrom<Src, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_respect_endianness.rs:32:36
+   |
+LL |     assert::is_transmutable::<Src, Unexpected>();
+   |                                    ^^^^^^^^^^ the trait `BikeshedIntrinsicFrom<Src, assert::Context, true, true, true, true>` is not implemented for `Unexpected`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/should_respect_endianness.rs:15:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/malformed-program-gracefulness/unknown_dst.rs b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_dst.rs
new file mode 100644
index 00000000000..e13462d390b
--- /dev/null
+++ b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_dst.rs
@@ -0,0 +1,21 @@
+// An unknown destination type should be gracefully handled.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+    {}
+}
+
+fn should_gracefully_handle_unknown_dst() {
+    struct Context;
+    struct Src;
+    assert::is_transmutable::<Src, Dst, Context>(); //~ cannot find type
+}
diff --git a/src/test/ui/transmutability/malformed-program-gracefulness/unknown_dst.stderr b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_dst.stderr
new file mode 100644
index 00000000000..85087282d3a
--- /dev/null
+++ b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_dst.stderr
@@ -0,0 +1,12 @@
+error[E0412]: cannot find type `Dst` in this scope
+  --> $DIR/unknown_dst.rs:20:36
+   |
+LL | fn should_gracefully_handle_unknown_dst() {
+   |                                        - help: you might be missing a type parameter: `<Dst>`
+...
+LL |     assert::is_transmutable::<Src, Dst, Context>();
+   |                                    ^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src.rs b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src.rs
new file mode 100644
index 00000000000..dc51e2a8f4d
--- /dev/null
+++ b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src.rs
@@ -0,0 +1,21 @@
+// An unknown source type should be gracefully handled.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+    {}
+}
+
+fn should_gracefully_handle_unknown_src() {
+    struct Context;
+    #[repr(C)] struct Dst;
+    assert::is_transmutable::<Src, Dst, Context>(); //~ cannot find type
+}
diff --git a/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src.stderr b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src.stderr
new file mode 100644
index 00000000000..9bedbe87c3f
--- /dev/null
+++ b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src.stderr
@@ -0,0 +1,12 @@
+error[E0412]: cannot find type `Src` in this scope
+  --> $DIR/unknown_src.rs:20:31
+   |
+LL | fn should_gracefully_handle_unknown_src() {
+   |                                        - help: you might be missing a type parameter: `<Src>`
+...
+LL |     assert::is_transmutable::<Src, Dst, Context>();
+   |                               ^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src_field.rs b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src_field.rs
new file mode 100644
index 00000000000..86fc8bd6b28
--- /dev/null
+++ b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src_field.rs
@@ -0,0 +1,22 @@
+// An unknown destination type should be gracefully handled.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+    {}
+}
+
+fn should_gracefully_handle_unknown_dst_field() {
+    struct Context;
+    #[repr(C)] struct Src;
+    #[repr(C)] struct Dst(Missing); //~ cannot find type
+    assert::is_transmutable::<Src, Dst, Context>();
+}
diff --git a/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr
new file mode 100644
index 00000000000..475e6f429f3
--- /dev/null
+++ b/src/test/ui/transmutability/malformed-program-gracefulness/unknown_src_field.stderr
@@ -0,0 +1,9 @@
+error[E0412]: cannot find type `Missing` in this scope
+  --> $DIR/unknown_src_field.rs:20:27
+   |
+LL |     #[repr(C)] struct Dst(Missing);
+   |                           ^^^^^^^ not found in this scope
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0412`.
diff --git a/src/test/ui/transmutability/primitives/bool.rs b/src/test/ui/transmutability/primitives/bool.rs
new file mode 100644
index 00000000000..13cecd1d8b7
--- /dev/null
+++ b/src/test/ui/transmutability/primitives/bool.rs
@@ -0,0 +1,25 @@
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+#![allow(incomplete_features)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+    {}
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, true, true>
+    {}
+}
+
+fn contrast_with_u8() {
+    assert::is_transmutable::<u8, bool>(); //~ ERROR not satisfied
+    assert::is_maybe_transmutable::<u8, bool>();
+    assert::is_transmutable::<bool, u8>();
+}
diff --git a/src/test/ui/transmutability/primitives/bool.stderr b/src/test/ui/transmutability/primitives/bool.stderr
new file mode 100644
index 00000000000..f05bb433ec8
--- /dev/null
+++ b/src/test/ui/transmutability/primitives/bool.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `bool: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, true>` is not satisfied
+  --> $DIR/bool.rs:22:35
+   |
+LL |     assert::is_transmutable::<u8, bool>();
+   |                                   ^^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, true>` is not implemented for `bool`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/bool.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/primitives/numbers.rs b/src/test/ui/transmutability/primitives/numbers.rs
new file mode 100644
index 00000000000..3dbdfab9686
--- /dev/null
+++ b/src/test/ui/transmutability/primitives/numbers.rs
@@ -0,0 +1,128 @@
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+#![allow(incomplete_features)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    struct Context;
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+    {}
+}
+
+fn should_accept_identity() {
+    assert::is_transmutable::<   i8,    i8>();
+    assert::is_transmutable::<   u8,    u8>();
+    assert::is_transmutable::<  i16,   i16>();
+    assert::is_transmutable::<  u16,   u16>();
+    assert::is_transmutable::<  i32,   i32>();
+    assert::is_transmutable::<  f32,   f32>();
+    assert::is_transmutable::<  u32,   u32>();
+    assert::is_transmutable::<  i64,   i64>();
+    assert::is_transmutable::<  f64,   f64>();
+    assert::is_transmutable::<  u64,   u64>();
+    assert::is_transmutable::< i128,  i128>();
+    assert::is_transmutable::< u128,  u128>();
+    assert::is_transmutable::<isize, isize>();
+    assert::is_transmutable::<usize, usize>();
+}
+
+fn should_be_bitransmutable() {
+    assert::is_transmutable::<   i8,    u8>();
+    assert::is_transmutable::<   u8,    i8>();
+
+    assert::is_transmutable::<  i16,   u16>();
+    assert::is_transmutable::<  u16,   i16>();
+
+    assert::is_transmutable::<  i32,   f32>();
+    assert::is_transmutable::<  i32,   u32>();
+    assert::is_transmutable::<  f32,   i32>();
+    assert::is_transmutable::<  f32,   u32>();
+    assert::is_transmutable::<  u32,   i32>();
+    assert::is_transmutable::<  u32,   f32>();
+
+    assert::is_transmutable::<  u64,   i64>();
+    assert::is_transmutable::<  u64,   f64>();
+    assert::is_transmutable::<  i64,   u64>();
+    assert::is_transmutable::<  i64,   f64>();
+    assert::is_transmutable::<  f64,   u64>();
+    assert::is_transmutable::<  f64,   i64>();
+
+    assert::is_transmutable::< u128,  i128>();
+    assert::is_transmutable::< i128,  u128>();
+
+    assert::is_transmutable::<isize, usize>();
+    assert::is_transmutable::<usize, isize>();
+}
+
+fn should_reject_extension() {
+    assert::is_transmutable::<   i8,   i16>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   i8,   u16>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   i8,   i32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   i8,   f32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   i8,   u32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   i8,   u64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   i8,   i64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   i8,   f64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   i8,  u128>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   i8,  i128>(); //~ ERROR not satisfied
+
+    assert::is_transmutable::<   u8,   i16>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   u8,   u16>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   u8,   i32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   u8,   f32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   u8,   u32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   u8,   u64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   u8,   i64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   u8,   f64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   u8,  u128>(); //~ ERROR not satisfied
+    assert::is_transmutable::<   u8,  i128>(); //~ ERROR not satisfied
+
+    assert::is_transmutable::<  i16,   i32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i16,   f32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i16,   u32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i16,   u64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i16,   i64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i16,   f64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i16,  u128>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i16,  i128>(); //~ ERROR not satisfied
+
+    assert::is_transmutable::<  u16,   i32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u16,   f32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u16,   u32>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u16,   u64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u16,   i64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u16,   f64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u16,  u128>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u16,  i128>(); //~ ERROR not satisfied
+
+    assert::is_transmutable::<  i32,   u64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i32,   i64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i32,   f64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i32,  u128>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i32,  i128>(); //~ ERROR not satisfied
+
+    assert::is_transmutable::<  f32,   u64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  f32,   i64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  f32,   f64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  f32,  u128>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  f32,  i128>(); //~ ERROR not satisfied
+
+    assert::is_transmutable::<  u32,   u64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u32,   i64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u32,   f64>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u32,  u128>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u32,  i128>(); //~ ERROR not satisfied
+
+    assert::is_transmutable::<  u64,  u128>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  u64,  i128>(); //~ ERROR not satisfied
+
+    assert::is_transmutable::<  i64,  u128>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  i64,  i128>(); //~ ERROR not satisfied
+
+    assert::is_transmutable::<  f64,  u128>(); //~ ERROR not satisfied
+    assert::is_transmutable::<  f64,  i128>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/primitives/numbers.stderr b/src/test/ui/transmutability/primitives/numbers.stderr
new file mode 100644
index 00000000000..6bd3379dfd1
--- /dev/null
+++ b/src/test/ui/transmutability/primitives/numbers.stderr
@@ -0,0 +1,858 @@
+error[E0277]: the trait bound `i16: BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:62:40
+   |
+LL |     assert::is_transmutable::<   i8,   i16>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not implemented for `i16`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u16: BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:63:40
+   |
+LL |     assert::is_transmutable::<   i8,   u16>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not implemented for `u16`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i32: BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:64:40
+   |
+LL |     assert::is_transmutable::<   i8,   i32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not implemented for `i32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f32: BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:65:40
+   |
+LL |     assert::is_transmutable::<   i8,   f32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not implemented for `f32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u32: BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:66:40
+   |
+LL |     assert::is_transmutable::<   i8,   u32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not implemented for `u32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u64: BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:67:40
+   |
+LL |     assert::is_transmutable::<   i8,   u64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not implemented for `u64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i64: BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:68:40
+   |
+LL |     assert::is_transmutable::<   i8,   i64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not implemented for `i64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f64: BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:69:40
+   |
+LL |     assert::is_transmutable::<   i8,   f64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not implemented for `f64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:70:39
+   |
+LL |     assert::is_transmutable::<   i8,  u128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i128: BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:71:39
+   |
+LL |     assert::is_transmutable::<   i8,  i128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<i8, assert::Context, false, false, false, false>` is not implemented for `i128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i16: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:73:40
+   |
+LL |     assert::is_transmutable::<   u8,   i16>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not implemented for `i16`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u16: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:74:40
+   |
+LL |     assert::is_transmutable::<   u8,   u16>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not implemented for `u16`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i32: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:75:40
+   |
+LL |     assert::is_transmutable::<   u8,   i32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not implemented for `i32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f32: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:76:40
+   |
+LL |     assert::is_transmutable::<   u8,   f32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not implemented for `f32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u32: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:77:40
+   |
+LL |     assert::is_transmutable::<   u8,   u32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not implemented for `u32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u64: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:78:40
+   |
+LL |     assert::is_transmutable::<   u8,   u64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not implemented for `u64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i64: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:79:40
+   |
+LL |     assert::is_transmutable::<   u8,   i64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not implemented for `i64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f64: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:80:40
+   |
+LL |     assert::is_transmutable::<   u8,   f64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not implemented for `f64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:81:39
+   |
+LL |     assert::is_transmutable::<   u8,  u128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i128: BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:82:39
+   |
+LL |     assert::is_transmutable::<   u8,  i128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<u8, assert::Context, false, false, false, false>` is not implemented for `i128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i32: BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:84:40
+   |
+LL |     assert::is_transmutable::<  i16,   i32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not implemented for `i32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f32: BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:85:40
+   |
+LL |     assert::is_transmutable::<  i16,   f32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not implemented for `f32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u32: BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:86:40
+   |
+LL |     assert::is_transmutable::<  i16,   u32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not implemented for `u32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u64: BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:87:40
+   |
+LL |     assert::is_transmutable::<  i16,   u64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not implemented for `u64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i64: BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:88:40
+   |
+LL |     assert::is_transmutable::<  i16,   i64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not implemented for `i64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f64: BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:89:40
+   |
+LL |     assert::is_transmutable::<  i16,   f64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not implemented for `f64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:90:39
+   |
+LL |     assert::is_transmutable::<  i16,  u128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i128: BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:91:39
+   |
+LL |     assert::is_transmutable::<  i16,  i128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<i16, assert::Context, false, false, false, false>` is not implemented for `i128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i32: BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:93:40
+   |
+LL |     assert::is_transmutable::<  u16,   i32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not implemented for `i32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f32: BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:94:40
+   |
+LL |     assert::is_transmutable::<  u16,   f32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not implemented for `f32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u32: BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:95:40
+   |
+LL |     assert::is_transmutable::<  u16,   u32>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not implemented for `u32`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u64: BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:96:40
+   |
+LL |     assert::is_transmutable::<  u16,   u64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not implemented for `u64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i64: BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:97:40
+   |
+LL |     assert::is_transmutable::<  u16,   i64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not implemented for `i64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f64: BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:98:40
+   |
+LL |     assert::is_transmutable::<  u16,   f64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not implemented for `f64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:99:39
+   |
+LL |     assert::is_transmutable::<  u16,  u128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i128: BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:100:39
+   |
+LL |     assert::is_transmutable::<  u16,  i128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<u16, assert::Context, false, false, false, false>` is not implemented for `i128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u64: BikeshedIntrinsicFrom<i32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:102:40
+   |
+LL |     assert::is_transmutable::<  i32,   u64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i32, assert::Context, false, false, false, false>` is not implemented for `u64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i64: BikeshedIntrinsicFrom<i32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:103:40
+   |
+LL |     assert::is_transmutable::<  i32,   i64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i32, assert::Context, false, false, false, false>` is not implemented for `i64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f64: BikeshedIntrinsicFrom<i32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:104:40
+   |
+LL |     assert::is_transmutable::<  i32,   f64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<i32, assert::Context, false, false, false, false>` is not implemented for `f64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<i32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:105:39
+   |
+LL |     assert::is_transmutable::<  i32,  u128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<i32, assert::Context, false, false, false, false>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i128: BikeshedIntrinsicFrom<i32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:106:39
+   |
+LL |     assert::is_transmutable::<  i32,  i128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<i32, assert::Context, false, false, false, false>` is not implemented for `i128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u64: BikeshedIntrinsicFrom<f32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:108:40
+   |
+LL |     assert::is_transmutable::<  f32,   u64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<f32, assert::Context, false, false, false, false>` is not implemented for `u64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i64: BikeshedIntrinsicFrom<f32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:109:40
+   |
+LL |     assert::is_transmutable::<  f32,   i64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<f32, assert::Context, false, false, false, false>` is not implemented for `i64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f64: BikeshedIntrinsicFrom<f32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:110:40
+   |
+LL |     assert::is_transmutable::<  f32,   f64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<f32, assert::Context, false, false, false, false>` is not implemented for `f64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<f32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:111:39
+   |
+LL |     assert::is_transmutable::<  f32,  u128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<f32, assert::Context, false, false, false, false>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i128: BikeshedIntrinsicFrom<f32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:112:39
+   |
+LL |     assert::is_transmutable::<  f32,  i128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<f32, assert::Context, false, false, false, false>` is not implemented for `i128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u64: BikeshedIntrinsicFrom<u32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:114:40
+   |
+LL |     assert::is_transmutable::<  u32,   u64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u32, assert::Context, false, false, false, false>` is not implemented for `u64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i64: BikeshedIntrinsicFrom<u32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:115:40
+   |
+LL |     assert::is_transmutable::<  u32,   i64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u32, assert::Context, false, false, false, false>` is not implemented for `i64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `f64: BikeshedIntrinsicFrom<u32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:116:40
+   |
+LL |     assert::is_transmutable::<  u32,   f64>();
+   |                                        ^^^ the trait `BikeshedIntrinsicFrom<u32, assert::Context, false, false, false, false>` is not implemented for `f64`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<u32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:117:39
+   |
+LL |     assert::is_transmutable::<  u32,  u128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<u32, assert::Context, false, false, false, false>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i128: BikeshedIntrinsicFrom<u32, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:118:39
+   |
+LL |     assert::is_transmutable::<  u32,  i128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<u32, assert::Context, false, false, false, false>` is not implemented for `i128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<u64, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:120:39
+   |
+LL |     assert::is_transmutable::<  u64,  u128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<u64, assert::Context, false, false, false, false>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i128: BikeshedIntrinsicFrom<u64, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:121:39
+   |
+LL |     assert::is_transmutable::<  u64,  i128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<u64, assert::Context, false, false, false, false>` is not implemented for `i128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<i64, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:123:39
+   |
+LL |     assert::is_transmutable::<  i64,  u128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<i64, assert::Context, false, false, false, false>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i128: BikeshedIntrinsicFrom<i64, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:124:39
+   |
+LL |     assert::is_transmutable::<  i64,  i128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<i64, assert::Context, false, false, false, false>` is not implemented for `i128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `u128: BikeshedIntrinsicFrom<f64, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:126:39
+   |
+LL |     assert::is_transmutable::<  f64,  u128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<f64, assert::Context, false, false, false, false>` is not implemented for `u128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `i128: BikeshedIntrinsicFrom<f64, assert::Context, false, false, false, false>` is not satisfied
+  --> $DIR/numbers.rs:127:39
+   |
+LL |     assert::is_transmutable::<  f64,  i128>();
+   |                                       ^^^^ the trait `BikeshedIntrinsicFrom<f64, assert::Context, false, false, false, false>` is not implemented for `i128`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/numbers.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to 57 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/primitives/unit.rs b/src/test/ui/transmutability/primitives/unit.rs
new file mode 100644
index 00000000000..a9c618188bf
--- /dev/null
+++ b/src/test/ui/transmutability/primitives/unit.rs
@@ -0,0 +1,24 @@
+//! The unit type, `()`, should be one byte.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+#[repr(C)]
+struct Zst;
+
+fn should_have_correct_size() {
+    struct Context;
+    assert::is_transmutable::<(), Zst, Context>();
+    assert::is_transmutable::<Zst, (), Context>();
+    assert::is_transmutable::<(), u8, Context>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/primitives/unit.stderr b/src/test/ui/transmutability/primitives/unit.stderr
new file mode 100644
index 00000000000..f602612feea
--- /dev/null
+++ b/src/test/ui/transmutability/primitives/unit.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `u8: BikeshedIntrinsicFrom<(), should_have_correct_size::Context, true, true, true, true>` is not satisfied
+  --> $DIR/unit.rs:23:35
+   |
+LL |     assert::is_transmutable::<(), u8, Context>();
+   |                                   ^^ the trait `BikeshedIntrinsicFrom<(), should_have_correct_size::Context, true, true, true, true>` is not implemented for `u8`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/unit.rs:12:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/references.rs b/src/test/ui/transmutability/references.rs
new file mode 100644
index 00000000000..c7d24aaf1ca
--- /dev/null
+++ b/src/test/ui/transmutability/references.rs
@@ -0,0 +1,20 @@
+//! Transmutations involving references are not yet supported.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+fn not_yet_implemented() {
+    #[repr(C)] struct Unit;
+    assert::is_maybe_transmutable::<&'static Unit, &'static Unit>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/references.stderr b/src/test/ui/transmutability/references.stderr
new file mode 100644
index 00000000000..7199e169e29
--- /dev/null
+++ b/src/test/ui/transmutability/references.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `&'static Unit: BikeshedIntrinsicFrom<&'static Unit, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/references.rs:19:52
+   |
+LL |     assert::is_maybe_transmutable::<&'static Unit, &'static Unit>();
+   |                                                    ^^^^^^^^^^^^^ the trait `BikeshedIntrinsicFrom<&'static Unit, assert::Context, true, true, true, true>` is not implemented for `&'static Unit`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/references.rs:13:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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/src/test/ui/transmutability/structs/repr/should_handle_align.rs b/src/test/ui/transmutability/structs/repr/should_handle_align.rs
new file mode 100644
index 00000000000..71720165ab0
--- /dev/null
+++ b/src/test/ui/transmutability/structs/repr/should_handle_align.rs
@@ -0,0 +1,36 @@
+// check-pass
+//! The presence of an `align(X)` annotation must be accounted for.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+fn should_pad_explicitly_aligned_field() {
+    #[derive(Clone, Copy)] #[repr(u8)] enum V0u8 { V = 0 }
+
+    #[repr(C)]
+    pub union Uninit {
+        a: (),
+        b: V0u8,
+    }
+
+    #[repr(C, align(2))] struct align_2(V0u8);
+
+    #[repr(C)] struct ImplicitlyPadded(align_2, V0u8);
+    #[repr(C)] struct ExplicitlyPadded(V0u8, Uninit, V0u8);
+
+    // An implementation that (incorrectly) does not place a padding byte after
+    // `align_2` will, incorrectly, reject the following transmutations.
+    assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>();
+    assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>();
+}
diff --git a/src/test/ui/transmutability/structs/repr/should_handle_packed.rs b/src/test/ui/transmutability/structs/repr/should_handle_packed.rs
new file mode 100644
index 00000000000..ae8acf50418
--- /dev/null
+++ b/src/test/ui/transmutability/structs/repr/should_handle_packed.rs
@@ -0,0 +1,35 @@
+// check-pass
+//! The presence of an `align(X)` annotation must be accounted for.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+fn should_pad_explicitly_packed_field() {
+    #[derive(Clone, Copy)] #[repr(u8)] enum V0u8 { V = 0 }
+    #[derive(Clone, Copy)] #[repr(u32)] enum V0u32 { V = 0 }
+
+    #[repr(C)]
+    pub union Uninit {
+        a: (),
+        b: V0u8,
+    }
+
+    #[repr(C, packed(2))] struct ImplicitlyPadded(V0u8, V0u32);
+    #[repr(C)] struct ExplicitlyPadded(V0u8, Uninit, V0u8, V0u8, V0u8, V0u8);
+
+    // An implementation that (incorrectly) does not place a padding byte after
+    // `align_2` will, incorrectly, reject the following transmutations.
+    assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>();
+    assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>();
+}
diff --git a/src/test/ui/transmutability/structs/repr/should_require_well_defined_layout.rs b/src/test/ui/transmutability/structs/repr/should_require_well_defined_layout.rs
new file mode 100644
index 00000000000..63419aceb6c
--- /dev/null
+++ b/src/test/ui/transmutability/structs/repr/should_require_well_defined_layout.rs
@@ -0,0 +1,76 @@
+//! A struct must have a well-defined layout to participate in a transmutation.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+fn should_reject_repr_rust()
+{
+    fn unit() {
+        struct repr_rust;
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+
+    fn tuple() {
+        struct repr_rust();
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+
+    fn braces() {
+        struct repr_rust{}
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+
+    fn aligned() {
+        #[repr(align(1))] struct repr_rust{}
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+
+    fn packed() {
+        #[repr(packed)] struct repr_rust{}
+        assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+    }
+
+    fn nested() {
+        struct repr_rust;
+        #[repr(C)] struct repr_c(repr_rust);
+        assert::is_maybe_transmutable::<repr_c, ()>(); //~ ERROR not satisfied
+        assert::is_maybe_transmutable::<u128, repr_c>(); //~ ERROR not satisfied
+    }
+}
+
+fn should_accept_repr_C()
+{
+    fn unit() {
+        #[repr(C)] struct repr_c;
+        assert::is_maybe_transmutable::<repr_c, ()>();
+        assert::is_maybe_transmutable::<i128, repr_c>();
+    }
+
+    fn tuple() {
+        #[repr(C)] struct repr_c();
+        assert::is_maybe_transmutable::<repr_c, ()>();
+        assert::is_maybe_transmutable::<i128, repr_c>();
+    }
+
+    fn braces() {
+        #[repr(C)] struct repr_c{}
+        assert::is_maybe_transmutable::<repr_c, ()>();
+        assert::is_maybe_transmutable::<i128, repr_c>();
+    }
+}
diff --git a/src/test/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr b/src/test/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr
new file mode 100644
index 00000000000..ab582dd6688
--- /dev/null
+++ b/src/test/ui/transmutability/structs/repr/should_require_well_defined_layout.stderr
@@ -0,0 +1,183 @@
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<should_reject_repr_rust::unit::repr_rust, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:21:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<should_reject_repr_rust::unit::repr_rust, assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `should_reject_repr_rust::unit::repr_rust: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:22:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `should_reject_repr_rust::unit::repr_rust`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<should_reject_repr_rust::tuple::repr_rust, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:27:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<should_reject_repr_rust::tuple::repr_rust, assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `should_reject_repr_rust::tuple::repr_rust: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:28:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `should_reject_repr_rust::tuple::repr_rust`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<should_reject_repr_rust::braces::repr_rust, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:33:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<should_reject_repr_rust::braces::repr_rust, assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `should_reject_repr_rust::braces::repr_rust: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:34:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `should_reject_repr_rust::braces::repr_rust`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<aligned::repr_rust, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:39:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<aligned::repr_rust, assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `aligned::repr_rust: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:40:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `aligned::repr_rust`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<packed::repr_rust, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:45:52
+   |
+LL |         assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                    ^^ the trait `BikeshedIntrinsicFrom<packed::repr_rust, assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `packed::repr_rust: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:46:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                               ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `packed::repr_rust`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<nested::repr_c, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:52:49
+   |
+LL |         assert::is_maybe_transmutable::<repr_c, ()>();
+   |                                                 ^^ the trait `BikeshedIntrinsicFrom<nested::repr_c, assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `nested::repr_c: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:53:47
+   |
+LL |         assert::is_maybe_transmutable::<u128, repr_c>();
+   |                                               ^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `nested::repr_c`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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/src/test/ui/transmutability/structs/should_order_fields_correctly.rs b/src/test/ui/transmutability/structs/should_order_fields_correctly.rs
new file mode 100644
index 00000000000..db49b914fe0
--- /dev/null
+++ b/src/test/ui/transmutability/structs/should_order_fields_correctly.rs
@@ -0,0 +1,31 @@
+// check-pass
+//! The fields of a struct should be laid out in lexical order.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+#[repr(u8)] enum V0 { V = 0 }
+#[repr(u8)] enum V1 { V = 1 }
+#[repr(u8)] enum V2 { V = 2 }
+
+#[repr(C)] struct S01(V0, V1);
+#[repr(C)] struct S012(V0, V1, V2);
+
+fn should_order_tag_and_fields_correctly() {
+    // An implementation that (incorrectly) arranges S01 as [0x01, 0x00] will,
+    // in principle, reject this transmutation.
+    assert::is_transmutable::<S01, V0>();
+    // Again, but with one more field.
+    assert::is_transmutable::<S012, S01>();
+}
diff --git a/src/test/ui/transmutability/unions/boolish.rs b/src/test/ui/transmutability/unions/boolish.rs
new file mode 100644
index 00000000000..975118b99b7
--- /dev/null
+++ b/src/test/ui/transmutability/unions/boolish.rs
@@ -0,0 +1,31 @@
+// check-pass
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![feature(marker_trait_attr)]
+#![allow(dead_code)]
+#![allow(incomplete_features)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+    {}
+}
+
+fn should_match_bool() {
+    #[derive(Copy, Clone)] #[repr(u8)] pub enum False { V = 0 }
+    #[derive(Copy, Clone)] #[repr(u8)] pub enum True { V = 1 }
+
+    #[repr(C)]
+    pub union Bool {
+        pub f: False,
+        pub t: True,
+    }
+
+    assert::is_transmutable::<Bool, bool>();
+    assert::is_transmutable::<bool, Bool>();
+}
diff --git a/src/test/ui/transmutability/unions/repr/should_handle_align.rs b/src/test/ui/transmutability/unions/repr/should_handle_align.rs
new file mode 100644
index 00000000000..e215799a232
--- /dev/null
+++ b/src/test/ui/transmutability/unions/repr/should_handle_align.rs
@@ -0,0 +1,40 @@
+// check-pass
+//! The presence of an `align(X)` annotation must be accounted for.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+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)]
+    pub union Uninit {
+        a: (),
+        b: V1u8,
+    }
+
+    #[repr(C, align(2))]
+    pub union align_2 {
+        a: V0u8,
+    }
+
+    #[repr(C)] struct ImplicitlyPadded(align_2, V0u8);
+    #[repr(C)] struct ExplicitlyPadded(V0u8, Uninit, V0u8);
+
+    // An implementation that (incorrectly) does not place a padding byte after
+    // `align_2` will, incorrectly, reject the following transmutations.
+    assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>();
+    assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>();
+}
diff --git a/src/test/ui/transmutability/unions/repr/should_handle_packed.rs b/src/test/ui/transmutability/unions/repr/should_handle_packed.rs
new file mode 100644
index 00000000000..34a53c7a80c
--- /dev/null
+++ b/src/test/ui/transmutability/unions/repr/should_handle_packed.rs
@@ -0,0 +1,41 @@
+// check-pass
+//! The presence of an `align(X)` annotation must be accounted for.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+fn should_pad_explicitly_packed_field() {
+    #[derive(Clone, Copy)] #[repr(u8)] enum V0u8 { V = 0 }
+    #[derive(Clone, Copy)] #[repr(u8)] enum V1u8 { V = 1 }
+    #[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,
+    }
+
+    #[repr(C, packed(2))]
+    pub union Packed {
+        a: [V3u32; 0],
+        b: V0u8,
+    }
+
+    #[repr(C)] struct ImplicitlyPadded(Packed, V2u8);
+    #[repr(C)] struct ExplicitlyPadded(V0u8, Uninit, V2u8);
+
+    assert::is_maybe_transmutable::<ImplicitlyPadded, ExplicitlyPadded>();
+    assert::is_maybe_transmutable::<ExplicitlyPadded, ImplicitlyPadded>();
+}
diff --git a/src/test/ui/transmutability/unions/repr/should_require_well_defined_layout.rs b/src/test/ui/transmutability/unions/repr/should_require_well_defined_layout.rs
new file mode 100644
index 00000000000..d6e28d7f0db
--- /dev/null
+++ b/src/test/ui/transmutability/unions/repr/should_require_well_defined_layout.rs
@@ -0,0 +1,37 @@
+//! A struct must have a well-defined layout to participate in a transmutation.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+fn should_reject_repr_rust()
+{
+    union repr_rust {
+        a: u8
+    }
+
+    assert::is_maybe_transmutable::<repr_rust, ()>(); //~ ERROR not satisfied
+    assert::is_maybe_transmutable::<u128, repr_rust>(); //~ ERROR not satisfied
+}
+
+fn should_accept_repr_C()
+{
+    #[repr(C)]
+    union repr_c {
+        a: u8
+    }
+
+    struct repr_rust;
+    assert::is_maybe_transmutable::<repr_c, ()>();
+    assert::is_maybe_transmutable::<u128, repr_c>();
+}
diff --git a/src/test/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr b/src/test/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr
new file mode 100644
index 00000000000..c24193f9a6d
--- /dev/null
+++ b/src/test/ui/transmutability/unions/repr/should_require_well_defined_layout.stderr
@@ -0,0 +1,33 @@
+error[E0277]: the trait bound `(): BikeshedIntrinsicFrom<should_reject_repr_rust::repr_rust, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:23:48
+   |
+LL |     assert::is_maybe_transmutable::<repr_rust, ()>();
+   |                                                ^^ the trait `BikeshedIntrinsicFrom<should_reject_repr_rust::repr_rust, assert::Context, true, true, true, true>` is not implemented for `()`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `should_reject_repr_rust::repr_rust: BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_require_well_defined_layout.rs:24:43
+   |
+LL |     assert::is_maybe_transmutable::<u128, repr_rust>();
+   |                                           ^^^^^^^^^ the trait `BikeshedIntrinsicFrom<u128, assert::Context, true, true, true, true>` is not implemented for `should_reject_repr_rust::repr_rust`
+   |
+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
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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/src/test/ui/transmutability/unions/should_pad_variants.rs b/src/test/ui/transmutability/unions/should_pad_variants.rs
new file mode 100644
index 00000000000..d4126693f92
--- /dev/null
+++ b/src/test/ui/transmutability/unions/should_pad_variants.rs
@@ -0,0 +1,40 @@
+//! The variants of a union must be padded with uninit bytes such that they have
+//! the same length (in bytes).
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+    {}
+}
+
+#[derive(Clone, Copy)]
+#[repr(C)] struct Zst;
+
+#[derive(Clone, Copy)]
+#[repr(u8)] enum V0 { V = 0 }
+
+#[derive(Clone, Copy)]
+#[repr(u8)] enum V2 { V = 2 }
+
+#[repr(C)]
+union Lopsided {
+    smol: Zst,
+    lorg: V0,
+}
+
+#[repr(C)] struct Src(V0, Zst, V2);
+#[repr(C)] struct Dst(V0, Lopsided, V2);
+
+fn should_pad_variants() {
+    struct Context;
+    // If the implementation (incorrectly) fails to pad `Lopsided::smol` with
+    // an uninitialized byte, this transmutation might be (wrongly) accepted:
+    assert::is_transmutable::<Src, Dst, Context>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/unions/should_pad_variants.stderr b/src/test/ui/transmutability/unions/should_pad_variants.stderr
new file mode 100644
index 00000000000..b940ca077d4
--- /dev/null
+++ b/src/test/ui/transmutability/unions/should_pad_variants.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `Dst: BikeshedIntrinsicFrom<Src, should_pad_variants::Context, true, true, true, true>` is not satisfied
+  --> $DIR/should_pad_variants.rs:39:36
+   |
+LL |     assert::is_transmutable::<Src, Dst, Context>();
+   |                                    ^^^ the trait `BikeshedIntrinsicFrom<Src, should_pad_variants::Context, true, true, true, true>` is not implemented for `Dst`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/should_pad_variants.rs:13:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, true, true, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/unions/should_permit_intersecting_if_validity_is_assumed.rs b/src/test/ui/transmutability/unions/should_permit_intersecting_if_validity_is_assumed.rs
new file mode 100644
index 00000000000..2493d71554a
--- /dev/null
+++ b/src/test/ui/transmutability/unions/should_permit_intersecting_if_validity_is_assumed.rs
@@ -0,0 +1,39 @@
+// check-pass
+//! If validity is assumed, there need only be one matching bit-pattern between
+//! the source and destination types.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, true, true>
+        // validity IS assumed --------------------------------^^^^
+    {}
+}
+
+#[derive(Clone, Copy)] #[repr(u8)] enum Ox00 { V = 0x00 }
+#[derive(Clone, Copy)] #[repr(u8)] enum Ox7F { V = 0x7F }
+#[derive(Clone, Copy)] #[repr(u8)] enum OxFF { V = 0xFF }
+
+fn test() {
+    #[repr(C)]
+    union A {
+        a: Ox00,
+        b: Ox7F,
+    }
+
+    #[repr(C)]
+    union B {
+        a: Ox7F,
+        b: OxFF,
+    }
+
+    assert::is_maybe_transmutable::<A, B>();
+    assert::is_maybe_transmutable::<B, A>();
+}
diff --git a/src/test/ui/transmutability/unions/should_reject_contraction.rs b/src/test/ui/transmutability/unions/should_reject_contraction.rs
new file mode 100644
index 00000000000..34b31595193
--- /dev/null
+++ b/src/test/ui/transmutability/unions/should_reject_contraction.rs
@@ -0,0 +1,36 @@
+//! Validity may not be contracted, unless validity is assumed.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+    {}
+}
+
+#[derive(Clone, Copy)] #[repr(u8)] enum Ox00 { V = 0x00 }
+#[derive(Clone, Copy)] #[repr(u8)] enum Ox01 { V = 0x01 }
+#[derive(Clone, Copy)] #[repr(u8)] enum OxFF { V = 0xFF }
+
+fn test() {
+    #[repr(C)]
+    union Subset {
+        a: Ox00,
+        b: OxFF,
+    }
+
+    #[repr(C)]
+    union Superset {
+        a: Ox00,
+        b: OxFF,
+        c: Ox01,
+    }
+
+    assert::is_transmutable::<Superset, Subset>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/unions/should_reject_contraction.stderr b/src/test/ui/transmutability/unions/should_reject_contraction.stderr
new file mode 100644
index 00000000000..1465c3df228
--- /dev/null
+++ b/src/test/ui/transmutability/unions/should_reject_contraction.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `Subset: BikeshedIntrinsicFrom<Superset, assert::Context, false, false, false, true>` is not satisfied
+  --> $DIR/should_reject_contraction.rs:35:41
+   |
+LL |     assert::is_transmutable::<Superset, Subset>();
+   |                                         ^^^^^^ the trait `BikeshedIntrinsicFrom<Superset, assert::Context, false, false, false, true>` is not implemented for `Subset`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/should_reject_contraction.rs:13:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/unions/should_reject_disjoint.rs b/src/test/ui/transmutability/unions/should_reject_disjoint.rs
new file mode 100644
index 00000000000..b4b06c57131
--- /dev/null
+++ b/src/test/ui/transmutability/unions/should_reject_disjoint.rs
@@ -0,0 +1,36 @@
+//! Validity must be satisfiable, even if validity is assumed.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_maybe_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, true, true>
+        // validity IS assumed --------------------------------^^^^
+    {}
+}
+
+#[derive(Clone, Copy)] #[repr(u8)] enum Ox00 { V = 0x00 }
+#[derive(Clone, Copy)] #[repr(u8)] enum Ox01 { V = 0x01 }
+#[derive(Clone, Copy)] #[repr(u8)] enum OxFF { V = 0xFF }
+
+fn test() {
+    #[repr(C)]
+    union A {
+        a: Ox00,
+        b: OxFF,
+    }
+
+    #[repr(C)]
+    union B {
+        c: Ox01,
+    }
+
+    assert::is_maybe_transmutable::<A, B>(); //~ ERROR not satisfied
+    assert::is_maybe_transmutable::<B, A>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/unions/should_reject_disjoint.stderr b/src/test/ui/transmutability/unions/should_reject_disjoint.stderr
new file mode 100644
index 00000000000..a140f0c506b
--- /dev/null
+++ b/src/test/ui/transmutability/unions/should_reject_disjoint.stderr
@@ -0,0 +1,33 @@
+error[E0277]: the trait bound `B: BikeshedIntrinsicFrom<A, assert::Context, false, false, true, true>` is not satisfied
+  --> $DIR/should_reject_disjoint.rs:34:40
+   |
+LL |     assert::is_maybe_transmutable::<A, B>();
+   |                                        ^ the trait `BikeshedIntrinsicFrom<A, assert::Context, false, false, true, true>` is not implemented for `B`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/should_reject_disjoint.rs:13:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_maybe_transmutable`
+
+error[E0277]: the trait bound `A: BikeshedIntrinsicFrom<B, assert::Context, false, false, true, true>` is not satisfied
+  --> $DIR/should_reject_disjoint.rs:35:40
+   |
+LL |     assert::is_maybe_transmutable::<B, A>();
+   |                                        ^ the trait `BikeshedIntrinsicFrom<B, assert::Context, false, false, true, true>` is not implemented for `A`
+   |
+note: required by a bound in `is_maybe_transmutable`
+  --> $DIR/should_reject_disjoint.rs:13:14
+   |
+LL |     pub fn is_maybe_transmutable<Src, Dst>()
+   |            --------------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, true, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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/src/test/ui/transmutability/unions/should_reject_intersecting.rs b/src/test/ui/transmutability/unions/should_reject_intersecting.rs
new file mode 100644
index 00000000000..1ed7d2a0bd9
--- /dev/null
+++ b/src/test/ui/transmutability/unions/should_reject_intersecting.rs
@@ -0,0 +1,38 @@
+//! ALL valid bit patterns of the source must be valid bit patterns of the
+//! destination type, unless validity is assumed.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code, incomplete_features, non_camel_case_types)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+    pub struct Context;
+
+    pub fn is_transmutable<Src, Dst>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+        // validity is NOT assumed ----------------------------^^^^^
+    {}
+}
+
+#[derive(Clone, Copy)] #[repr(u8)] enum Ox00 { V = 0x00 }
+#[derive(Clone, Copy)] #[repr(u8)] enum Ox7F { V = 0x7F }
+#[derive(Clone, Copy)] #[repr(u8)] enum OxFF { V = 0xFF }
+
+fn test() {
+    #[repr(C)]
+    union A {
+        a: Ox00,
+        b: Ox7F,
+    }
+
+    #[repr(C)]
+    union B {
+        a: Ox7F,
+        b: OxFF,
+    }
+
+    assert::is_transmutable::<A, B>(); //~ ERROR not satisfied
+    assert::is_transmutable::<B, A>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/unions/should_reject_intersecting.stderr b/src/test/ui/transmutability/unions/should_reject_intersecting.stderr
new file mode 100644
index 00000000000..43e642b5691
--- /dev/null
+++ b/src/test/ui/transmutability/unions/should_reject_intersecting.stderr
@@ -0,0 +1,33 @@
+error[E0277]: the trait bound `B: BikeshedIntrinsicFrom<A, assert::Context, false, false, false, true>` is not satisfied
+  --> $DIR/should_reject_intersecting.rs:36:34
+   |
+LL |     assert::is_transmutable::<A, B>();
+   |                                  ^ the trait `BikeshedIntrinsicFrom<A, assert::Context, false, false, false, true>` is not implemented for `B`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/should_reject_intersecting.rs:14:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error[E0277]: the trait bound `A: BikeshedIntrinsicFrom<B, assert::Context, false, false, false, true>` is not satisfied
+  --> $DIR/should_reject_intersecting.rs:37:34
+   |
+LL |     assert::is_transmutable::<B, A>();
+   |                                  ^ the trait `BikeshedIntrinsicFrom<B, assert::Context, false, false, false, true>` is not implemented for `A`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/should_reject_intersecting.rs:14:14
+   |
+LL |     pub fn is_transmutable<Src, Dst>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_private_field.rs b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_private_field.rs
new file mode 100644
index 00000000000..5a8c810494c
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_private_field.rs
@@ -0,0 +1,38 @@
+// check-pass
+//! If visibility is assumed, a transmutation should be accepted even if the
+//! destination type contains a private field.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+        // visibility IS assumed -------------------------------------^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(self) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(self) field: Zst,
+    }
+}
+
+mod dst {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Dst {
+        pub(self) field: Zst, // <- private field
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>();
+}
diff --git a/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_private_variant.rs b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_private_variant.rs
new file mode 100644
index 00000000000..77ab4fa6bff
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_private_variant.rs
@@ -0,0 +1,39 @@
+// check-pass
+//! If visibility is assumed, a transmutation should be accepted even if the
+//! destination type contains a private variant.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+        // visibility IS assumed -------------------------------------^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(self) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(self) field: Zst,
+    }
+}
+
+mod dst {
+    #[derive(Copy, Clone)]
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) union Dst {
+        pub(self) field: Zst, // <- private variant
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>();
+}
diff --git a/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_tricky_unreachable_field.rs b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_tricky_unreachable_field.rs
new file mode 100644
index 00000000000..2421b24cbf0
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_tricky_unreachable_field.rs
@@ -0,0 +1,46 @@
+// check-pass
+//! Unless visibility is assumed, a transmutation should be rejected if the
+//! destination type contains an unreachable field (e.g., a public field with a
+//! private type). (This rule is distinct from type privacy, which still may
+//! forbid naming such types.)
+//!
+//! This test exercises a tricky-to-implement instance of this principle: the
+//! "pub-in-priv trick". In the below example, the type `dst::private::Zst` is
+//! unreachable from `Context`.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+        // visibility IS assumed -------------------------------------^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(in super) field: Zst,
+    }
+}
+
+mod dst {
+    mod private {
+        #[repr(C)] pub struct Zst; // <- unreachable type
+    }
+
+    #[repr(C)] pub(in super) struct Dst {
+        pub(in super) field: private::Zst,
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>();
+}
diff --git a/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_field.rs b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_field.rs
new file mode 100644
index 00000000000..80b454fda56
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_field.rs
@@ -0,0 +1,39 @@
+//! If visibility is assumed, a transmutation should be accepted even if the
+//! destination type contains an unreachable field (e.g., a public field with a
+//! private type). (This rule is distinct from type privacy, which still may
+//! forbid naming such types.)
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+        // visibility IS assumed -------------------------------------^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(self) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(self) field: Zst,
+    }
+}
+
+mod dst {
+    #[repr(C)] pub(self) struct Zst; // <- unreachable type
+
+    #[repr(C)] pub(in super) struct Dst {
+        pub(in super) field: Zst, //~ ERROR private type
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>();
+}
diff --git a/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_field.stderr b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_field.stderr
new file mode 100644
index 00000000000..be83b7ce33f
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_field.stderr
@@ -0,0 +1,12 @@
+error[E0446]: private type `dst::Zst` in public interface
+  --> $DIR/should_accept_if_dst_has_unreachable_field.rs:32:9
+   |
+LL |     #[repr(C)] pub(self) struct Zst; // <- unreachable type
+   |                -------------------- `dst::Zst` declared as private
+...
+LL |         pub(in super) field: Zst,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0446`.
diff --git a/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_ty.rs b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_ty.rs
new file mode 100644
index 00000000000..7c53c91e4ed
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_ty.rs
@@ -0,0 +1,40 @@
+//! If visibility is assumed, a transmutation should be accepted even if the
+//! destination type contains an unreachable field (e.g., a public field with a
+//! private type). (This rule is distinct from type privacy, which still may
+//! forbid naming such types.)
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, true>
+        // visibility IS assumed -------------------------------------^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(self) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(self) field: Zst,
+    }
+}
+
+mod dst {
+    #[repr(C)] pub(in super) struct Zst;
+
+    // unreachable type
+    #[repr(C)] pub(self) struct Dst {
+        pub(in super) field: Zst,
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>(); //~ ERROR `Dst` is private
+}
diff --git a/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_ty.stderr b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_ty.stderr
new file mode 100644
index 00000000000..827df05decb
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/assume/should_accept_if_dst_has_unreachable_ty.stderr
@@ -0,0 +1,15 @@
+error[E0603]: struct `Dst` is private
+  --> $DIR/should_accept_if_dst_has_unreachable_ty.rs:39:46
+   |
+LL |     assert::is_transmutable::<src::Src, dst::Dst, Context>();
+   |                                              ^^^ private struct
+   |
+note: the struct `Dst` is defined here
+  --> $DIR/should_accept_if_dst_has_unreachable_ty.rs:32:16
+   |
+LL |     #[repr(C)] pub(self) struct Dst {
+   |                ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0603`.
diff --git a/src/test/ui/transmutability/visibility/should_accept_if_src_has_private_field.rs b/src/test/ui/transmutability/visibility/should_accept_if_src_has_private_field.rs
new file mode 100644
index 00000000000..c3f298f0163
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_accept_if_src_has_private_field.rs
@@ -0,0 +1,38 @@
+// check-pass
+//! The presence of a private field in the source type does not affect
+//! transmutability.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+        // visibility is NOT assumed ---------------------------------^^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(self) field: Zst, // <- private field
+    }
+}
+
+mod dst {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Dst {
+        pub(in super) field: Zst,
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>();
+}
diff --git a/src/test/ui/transmutability/visibility/should_accept_if_src_has_private_variant.rs b/src/test/ui/transmutability/visibility/should_accept_if_src_has_private_variant.rs
new file mode 100644
index 00000000000..73f6aece51e
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_accept_if_src_has_private_variant.rs
@@ -0,0 +1,39 @@
+// check-pass
+//! The presence of a private variant in the source type does not affect
+//! transmutability.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+        // visibility is NOT assumed ---------------------------------^^^^^
+    {}
+}
+
+mod src {
+    #[derive(Copy, Clone)]
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) union Src {
+        pub(self) field: Zst, // <- private variant
+    }
+}
+
+mod dst {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Dst {
+        pub(in super) field: Zst,
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>();
+}
diff --git a/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_field.rs b/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_field.rs
new file mode 100644
index 00000000000..6d602601e96
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_field.rs
@@ -0,0 +1,38 @@
+//! The presence of an unreachable field in the source type (e.g., a public
+//! field with a private type does not affect transmutability. (This rule is
+//! distinct from type privacy, which still may forbid naming such types.)
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+        // visibility is NOT assumed ---------------------------------^^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(self) struct Zst; // <- unreachable type
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(in super) field: Zst, //~ ERROR private type
+    }
+}
+
+mod dst {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Dst {
+        pub(in super) field: Zst,
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>();
+}
diff --git a/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_field.stderr b/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_field.stderr
new file mode 100644
index 00000000000..3f7d08d0ae2
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_field.stderr
@@ -0,0 +1,12 @@
+error[E0446]: private type `src::Zst` in public interface
+  --> $DIR/should_accept_if_src_has_unreachable_field.rs:23:9
+   |
+LL |     #[repr(C)] pub(self) struct Zst; // <- unreachable type
+   |                -------------------- `src::Zst` declared as private
+...
+LL |         pub(in super) field: Zst,
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^ can't leak private type
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0446`.
diff --git a/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_ty.rs b/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_ty.rs
new file mode 100644
index 00000000000..1943fb8716a
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_ty.rs
@@ -0,0 +1,39 @@
+//! The presence of an unreachable source type (i.e., the source type is
+//! private) does not affect transmutability. (This rule is distinct from type
+//! privacy, which still may forbid naming such types.)
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+        // visibility is NOT assumed ---------------------------------^^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(in super) struct Zst;
+
+    // unreachable type
+    #[repr(C)] pub(self) struct Src {
+        pub(in super) field: Zst,
+    }
+}
+
+mod dst {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Dst {
+        pub(in super) field: Zst,
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>(); //~ ERROR `Src` is private
+}
diff --git a/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_ty.stderr b/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_ty.stderr
new file mode 100644
index 00000000000..e961984e189
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_accept_if_src_has_unreachable_ty.stderr
@@ -0,0 +1,15 @@
+error[E0603]: struct `Src` is private
+  --> $DIR/should_accept_if_src_has_unreachable_ty.rs:38:36
+   |
+LL |     assert::is_transmutable::<src::Src, dst::Dst, Context>();
+   |                                    ^^^ private struct
+   |
+note: the struct `Src` is defined here
+  --> $DIR/should_accept_if_src_has_unreachable_ty.rs:23:16
+   |
+LL |     #[repr(C)] pub(self) struct Src {
+   |                ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0603`.
diff --git a/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_field.rs b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_field.rs
new file mode 100644
index 00000000000..04cb6885887
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_field.rs
@@ -0,0 +1,37 @@
+//! Unless visibility is assumed, a transmutation should be rejected if the
+//! destination type contains a private field.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+        // visibility is NOT assumed ---------------------------------^^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(in super) field: Zst,
+    }
+}
+
+mod dst {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Dst {
+        pub(self) field: Zst, // <- private field
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_field.stderr b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_field.stderr
new file mode 100644
index 00000000000..4dfbfaeafb6
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_field.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `Dst: BikeshedIntrinsicFrom<Src, test::Context, false, false, false, false>` is not satisfied
+  --> $DIR/should_reject_if_dst_has_private_field.rs:36:41
+   |
+LL |     assert::is_transmutable::<src::Src, dst::Dst, Context>();
+   |                                         ^^^^^^^^ the trait `BikeshedIntrinsicFrom<Src, test::Context, false, false, false, false>` is not implemented for `Dst`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/should_reject_if_dst_has_private_field.rs:13:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_variant.rs b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_variant.rs
new file mode 100644
index 00000000000..768e7bc559e
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_variant.rs
@@ -0,0 +1,38 @@
+//! Unless visibility is assumed, a transmutation should be rejected if the
+//! destination type contains a private variant.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+        // visibility is NOT assumed ---------------------------------^^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(in super) field: Zst,
+    }
+}
+
+mod dst {
+    #[derive(Copy, Clone)]
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) union Dst {
+        pub(self) field: Zst, // <- private variant
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_variant.stderr b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_variant.stderr
new file mode 100644
index 00000000000..ed834a1bd25
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_private_variant.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `Dst: BikeshedIntrinsicFrom<Src, test::Context, false, false, false, false>` is not satisfied
+  --> $DIR/should_reject_if_dst_has_private_variant.rs:37:41
+   |
+LL |     assert::is_transmutable::<src::Src, dst::Dst, Context>();
+   |                                         ^^^^^^^^ the trait `BikeshedIntrinsicFrom<Src, test::Context, false, false, false, false>` is not implemented for `Dst`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/should_reject_if_dst_has_private_variant.rs:13:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/visibility/should_reject_if_dst_has_tricky_unreachable_field.rs b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_tricky_unreachable_field.rs
new file mode 100644
index 00000000000..c44fed4cce3
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_tricky_unreachable_field.rs
@@ -0,0 +1,52 @@
+// check-pass
+//! NOTE: This test documents a known-bug in the implementation of the
+//! transmutability trait. Once fixed, the above "check-pass" header should be
+//! removed, and an "ERROR not satisfied" annotation should be added at the end
+//! of the line starting with `assert::is_transmutable`.
+//!
+//! Unless visibility is assumed, a transmutation should be rejected if the
+//! destination type contains an unreachable field (e.g., a public field with a
+//! private type). (This rule is distinct from type privacy, which still may
+//! forbid naming such types.)
+//!
+//! This test exercises a tricky-to-implement instance of this principle: the
+//! "pub-in-priv trick". In the below example, the type `dst::private::Zst` is
+//! unreachable from `Context`. Consequently, the transmute from `Src` to `Dst`
+//! SHOULD be rejected.
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+        // visibility is NOT assumed ---------------------------------^^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(in super) field: Zst,
+    }
+}
+
+mod dst {
+    mod private {
+        #[repr(C)] pub struct Zst; // <- unreachable type
+    }
+
+    #[repr(C)] pub(in super) struct Dst {
+        pub(in super) field: private::Zst,
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>();
+}
diff --git a/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_field.rs b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_field.rs
new file mode 100644
index 00000000000..dbef149bacc
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_field.rs
@@ -0,0 +1,39 @@
+//! Unless visibility is assumed, a transmutation should be rejected if the
+//! destination type contains an unreachable field (e.g., a public field with a
+//! private type). (This rule is distinct from type privacy, which still may
+//! forbid naming such types.)
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+        // visibility is NOT assumed ---------------------------------^^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(in super) field: Zst,
+    }
+}
+
+mod dst {
+    #[repr(C)] pub(self) struct Zst; // <- unreachable type
+
+    #[repr(C)] pub(in super) struct Dst {
+        pub(in super) field: Zst,
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>(); //~ ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_field.stderr b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_field.stderr
new file mode 100644
index 00000000000..3029d6ab8ee
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_field.stderr
@@ -0,0 +1,18 @@
+error[E0277]: the trait bound `Dst: BikeshedIntrinsicFrom<Src, test::Context, false, false, false, false>` is not satisfied
+  --> $DIR/should_reject_if_dst_has_unreachable_field.rs:38:41
+   |
+LL |     assert::is_transmutable::<src::Src, dst::Dst, Context>();
+   |                                         ^^^^^^^^ the trait `BikeshedIntrinsicFrom<Src, test::Context, false, false, false, false>` is not implemented for `Dst`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/should_reject_if_dst_has_unreachable_field.rs:15:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_ty.rs b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_ty.rs
new file mode 100644
index 00000000000..c5947eceb65
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_ty.rs
@@ -0,0 +1,42 @@
+//! Unless visibility is assumed, a transmutation should be rejected if the
+//! destination type contains an unreachable field (e.g., a public field with a
+//! private type). (This rule is distinct from type privacy, which still may
+//! forbid naming such types.)
+
+#![crate_type = "lib"]
+#![feature(transmutability)]
+#![allow(dead_code)]
+
+mod assert {
+    use std::mem::BikeshedIntrinsicFrom;
+
+    pub fn is_transmutable<Src, Dst, Context>()
+    where
+        Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+        // visibility is NOT assumed ---------------------------------^^^^^
+    {}
+}
+
+mod src {
+    #[repr(C)] pub(in super) struct Zst;
+
+    #[repr(C)] pub(in super) struct Src {
+        pub(in super) field: Zst,
+    }
+}
+
+mod dst {
+    #[repr(C)] pub(in super) struct Zst;
+
+    // unreachable type
+    #[repr(C)] pub(self) struct Dst {
+        pub(in super) field: Zst,
+    }
+}
+
+fn test() {
+    struct Context;
+    assert::is_transmutable::<src::Src, dst::Dst, Context>();
+    //~^ ERROR `Dst` is private
+    //~| ERROR not satisfied
+}
diff --git a/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_ty.stderr b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_ty.stderr
new file mode 100644
index 00000000000..2fd38890321
--- /dev/null
+++ b/src/test/ui/transmutability/visibility/should_reject_if_dst_has_unreachable_ty.stderr
@@ -0,0 +1,31 @@
+error[E0603]: struct `Dst` is private
+  --> $DIR/should_reject_if_dst_has_unreachable_ty.rs:39:46
+   |
+LL |     assert::is_transmutable::<src::Src, dst::Dst, Context>();
+   |                                              ^^^ private struct
+   |
+note: the struct `Dst` is defined here
+  --> $DIR/should_reject_if_dst_has_unreachable_ty.rs:32:16
+   |
+LL |     #[repr(C)] pub(self) struct Dst {
+   |                ^^^^^^^^^^^^^^^^^^^^
+
+error[E0277]: the trait bound `Dst: BikeshedIntrinsicFrom<Src, test::Context, false, false, false, false>` is not satisfied
+  --> $DIR/should_reject_if_dst_has_unreachable_ty.rs:39:41
+   |
+LL |     assert::is_transmutable::<src::Src, dst::Dst, Context>();
+   |                                         ^^^^^^^^ the trait `BikeshedIntrinsicFrom<Src, test::Context, false, false, false, false>` is not implemented for `Dst`
+   |
+note: required by a bound in `is_transmutable`
+  --> $DIR/should_reject_if_dst_has_unreachable_ty.rs:15:14
+   |
+LL |     pub fn is_transmutable<Src, Dst, Context>()
+   |            --------------- required by a bound in this
+LL |     where
+LL |         Dst: BikeshedIntrinsicFrom<Src, Context, false, false, false, false>
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0277, E0603.
+For more information about an error, try `rustc --explain E0277`.