about summary refs log tree commit diff
path: root/compiler/rustc_transmute/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_transmute/src')
-rw-r--r--compiler/rustc_transmute/src/layout/dfa.rs45
-rw-r--r--compiler/rustc_transmute/src/layout/mod.rs111
-rw-r--r--compiler/rustc_transmute/src/layout/tree.rs52
-rw-r--r--compiler/rustc_transmute/src/layout/tree/tests.rs16
-rw-r--r--compiler/rustc_transmute/src/lib.rs30
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/mod.rs208
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/query_context.rs21
-rw-r--r--compiler/rustc_transmute/src/maybe_transmutable/tests.rs57
8 files changed, 296 insertions, 244 deletions
diff --git a/compiler/rustc_transmute/src/layout/dfa.rs b/compiler/rustc_transmute/src/layout/dfa.rs
index 6d072c336af..6fc40ce42d8 100644
--- a/compiler/rustc_transmute/src/layout/dfa.rs
+++ b/compiler/rustc_transmute/src/layout/dfa.rs
@@ -2,32 +2,35 @@ use std::fmt;
 use std::iter::Peekable;
 use std::sync::atomic::{AtomicU32, Ordering};
 
-use super::{Byte, Ref, Tree, Uninhabited};
+use super::{Byte, Reference, Region, Tree, Type, Uninhabited};
 use crate::{Map, Set};
 
 #[derive(PartialEq)]
 #[cfg_attr(test, derive(Clone))]
-pub(crate) struct Dfa<R>
+pub(crate) struct Dfa<R, T>
 where
-    R: Ref,
+    R: Region,
+    T: Type,
 {
-    pub(crate) transitions: Map<State, Transitions<R>>,
+    pub(crate) transitions: Map<State, Transitions<R, T>>,
     pub(crate) start: State,
     pub(crate) accept: State,
 }
 
 #[derive(PartialEq, Clone, Debug)]
-pub(crate) struct Transitions<R>
+pub(crate) struct Transitions<R, T>
 where
-    R: Ref,
+    R: Region,
+    T: Type,
 {
     byte_transitions: EdgeSet<State>,
-    ref_transitions: Map<R, State>,
+    ref_transitions: Map<Reference<R, T>, State>,
 }
 
-impl<R> Default for Transitions<R>
+impl<R, T> Default for Transitions<R, T>
 where
-    R: Ref,
+    R: Region,
+    T: Type,
 {
     fn default() -> Self {
         Self { byte_transitions: EdgeSet::empty(), ref_transitions: Map::default() }
@@ -51,9 +54,10 @@ impl fmt::Debug for State {
     }
 }
 
-impl<R> Dfa<R>
+impl<R, T> Dfa<R, T>
 where
-    R: Ref,
+    R: Region,
+    T: Type,
 {
     #[cfg(test)]
     pub(crate) fn bool() -> Self {
@@ -64,7 +68,7 @@ where
     }
 
     pub(crate) fn unit() -> Self {
-        let transitions: Map<State, Transitions<R>> = Map::default();
+        let transitions: Map<State, Transitions<R, T>> = Map::default();
         let start = State::new();
         let accept = start;
 
@@ -78,21 +82,21 @@ where
         })
     }
 
-    pub(crate) fn from_ref(r: R) -> Self {
+    pub(crate) fn from_ref(r: Reference<R, T>) -> Self {
         Self::from_transitions(|accept| Transitions {
             byte_transitions: EdgeSet::empty(),
             ref_transitions: [(r, accept)].into_iter().collect(),
         })
     }
 
-    fn from_transitions(f: impl FnOnce(State) -> Transitions<R>) -> Self {
+    fn from_transitions(f: impl FnOnce(State) -> Transitions<R, T>) -> Self {
         let start = State::new();
         let accept = State::new();
 
         Self { transitions: [(start, f(accept))].into_iter().collect(), start, accept }
     }
 
-    pub(crate) fn from_tree(tree: Tree<!, R>) -> Result<Self, Uninhabited> {
+    pub(crate) fn from_tree(tree: Tree<!, R, T>) -> Result<Self, Uninhabited> {
         Ok(match tree {
             Tree::Byte(b) => Self::from_byte(b),
             Tree::Ref(r) => Self::from_ref(r),
@@ -125,7 +129,7 @@ where
         let start = self.start;
         let accept = other.accept;
 
-        let mut transitions: Map<State, Transitions<R>> = self.transitions;
+        let mut transitions: Map<State, Transitions<R, T>> = self.transitions;
 
         for (source, transition) in other.transitions {
             let fix_state = |state| if state == other.start { self.accept } else { state };
@@ -169,7 +173,7 @@ where
         };
 
         let start = mapped((Some(a.start), Some(b.start)));
-        let mut transitions: Map<State, Transitions<R>> = Map::default();
+        let mut transitions: Map<State, Transitions<R, T>> = Map::default();
         let empty_transitions = Transitions::default();
 
         struct WorkQueue {
@@ -257,7 +261,7 @@ where
             .flat_map(|transitions| transitions.byte_transitions.iter())
     }
 
-    pub(crate) fn refs_from(&self, start: State) -> impl Iterator<Item = (R, State)> {
+    pub(crate) fn refs_from(&self, start: State) -> impl Iterator<Item = (Reference<R, T>, State)> {
         self.transitions
             .get(&start)
             .into_iter()
@@ -297,9 +301,10 @@ where
 }
 
 /// Serialize the DFA using the Graphviz DOT format.
-impl<R> fmt::Debug for Dfa<R>
+impl<R, T> fmt::Debug for Dfa<R, T>
 where
-    R: Ref,
+    R: Region,
+    T: Type,
 {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         writeln!(f, "digraph {{")?;
diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs
index c08bf440734..acbce258f39 100644
--- a/compiler/rustc_transmute/src/layout/mod.rs
+++ b/compiler/rustc_transmute/src/layout/mod.rs
@@ -78,16 +78,41 @@ impl From<u8> for Byte {
     }
 }
 
+/// A reference, i.e., `&'region T` or `&'region mut T`.
+#[derive(Debug, Hash, Eq, PartialEq, Ord, PartialOrd, Clone, Copy)]
+pub(crate) struct Reference<R, T>
+where
+    R: Region,
+    T: Type,
+{
+    pub(crate) region: R,
+    pub(crate) is_mut: bool,
+    pub(crate) referent: T,
+    pub(crate) referent_size: usize,
+    pub(crate) referent_align: usize,
+}
+
+impl<R, T> fmt::Display for Reference<R, T>
+where
+    R: Region,
+    T: Type,
+{
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str("&")?;
+        if self.is_mut {
+            f.write_str("mut ")?;
+        }
+        self.referent.fmt(f)
+    }
+}
+
 pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {
     fn has_safety_invariants(&self) -> bool;
 }
-pub trait Ref: Debug + Hash + Eq + PartialEq + Copy + Clone {
-    fn min_align(&self) -> usize;
 
-    fn size(&self) -> usize;
+pub(crate) trait Region: Debug + Hash + Eq + PartialEq + Copy + Clone {}
 
-    fn is_mutable(&self) -> bool;
-}
+pub(crate) trait Type: Debug + Hash + Eq + PartialEq + Copy + Clone {}
 
 impl Def for ! {
     fn has_safety_invariants(&self) -> bool {
@@ -95,79 +120,21 @@ impl Def for ! {
     }
 }
 
-impl Ref for ! {
-    fn min_align(&self) -> usize {
-        unreachable!()
-    }
-    fn size(&self) -> usize {
-        unreachable!()
-    }
-    fn is_mutable(&self) -> bool {
-        unreachable!()
-    }
-}
+impl Region for ! {}
 
-#[cfg(test)]
-impl<const N: usize> Ref for [(); N] {
-    fn min_align(&self) -> usize {
-        N
-    }
+impl Type for ! {}
 
-    fn size(&self) -> usize {
-        N
-    }
+#[cfg(test)]
+impl Region for usize {}
 
-    fn is_mutable(&self) -> bool {
-        false
-    }
-}
+#[cfg(test)]
+impl Type for () {}
 
 #[cfg(feature = "rustc")]
 pub mod rustc {
-    use std::fmt::{self, Write};
-
     use rustc_abi::Layout;
-    use rustc_middle::mir::Mutability;
     use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError};
-    use rustc_middle::ty::{self, Ty};
-
-    /// A reference in the layout.
-    #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
-    pub struct Ref<'tcx> {
-        pub lifetime: ty::Region<'tcx>,
-        pub ty: Ty<'tcx>,
-        pub mutability: Mutability,
-        pub align: usize,
-        pub size: usize,
-    }
-
-    impl<'tcx> super::Ref for Ref<'tcx> {
-        fn min_align(&self) -> usize {
-            self.align
-        }
-
-        fn size(&self) -> usize {
-            self.size
-        }
-
-        fn is_mutable(&self) -> bool {
-            match self.mutability {
-                Mutability::Mut => true,
-                Mutability::Not => false,
-            }
-        }
-    }
-    impl<'tcx> Ref<'tcx> {}
-
-    impl<'tcx> fmt::Display for Ref<'tcx> {
-        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            f.write_char('&')?;
-            if self.mutability == Mutability::Mut {
-                f.write_str("mut ")?;
-            }
-            self.ty.fmt(f)
-        }
-    }
+    use rustc_middle::ty::{self, Region, Ty};
 
     /// A visibility node in the layout.
     #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)]
@@ -187,6 +154,10 @@ pub mod rustc {
         }
     }
 
+    impl<'tcx> super::Region for Region<'tcx> {}
+
+    impl<'tcx> super::Type for Ty<'tcx> {}
+
     pub(crate) fn layout_of<'tcx>(
         cx: LayoutCx<'tcx>,
         ty: Ty<'tcx>,
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index ff665695b5a..150f5d118e0 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -1,6 +1,6 @@
 use std::ops::{ControlFlow, RangeInclusive};
 
-use super::{Byte, Def, Ref};
+use super::{Byte, Def, Reference, Region, Type};
 
 #[cfg(test)]
 mod tests;
@@ -15,10 +15,11 @@ mod tests;
 /// 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>
+pub(crate) enum Tree<D, R, T>
 where
     D: Def,
-    R: Ref,
+    R: Region,
+    T: Type,
 {
     /// A sequence of successive layouts.
     Seq(Vec<Self>),
@@ -27,7 +28,7 @@ where
     /// A definition node.
     Def(D),
     /// A reference node.
-    Ref(R),
+    Ref(Reference<R, T>),
     /// A byte node.
     Byte(Byte),
 }
@@ -48,10 +49,11 @@ impl From<rustc_abi::Endian> for Endian {
     }
 }
 
-impl<D, R> Tree<D, R>
+impl<D, R, T> Tree<D, R, T>
 where
     D: Def,
-    R: Ref,
+    R: Region,
+    T: Type,
 {
     /// A `Tree` consisting only of a definition node.
     pub(crate) fn def(def: D) -> Self {
@@ -138,7 +140,7 @@ where
 
     /// Remove all `Def` nodes, and all branches of the layout for which `f`
     /// produces `true`.
-    pub(crate) fn prune<F>(self, f: &F) -> Tree<!, R>
+    pub(crate) fn prune<F>(self, f: &F) -> Tree<!, R, T>
     where
         F: Fn(D) -> bool,
     {
@@ -198,13 +200,13 @@ where
 
     /// Produces a `Tree` where each of the trees in `trees` are sequenced one
     /// after another.
-    pub(crate) fn seq<const N: usize>(trees: [Tree<D, R>; N]) -> Self {
+    pub(crate) fn seq<const N: usize>(trees: [Tree<D, R, T>; N]) -> Self {
         trees.into_iter().fold(Tree::unit(), Self::then)
     }
 
     /// Produces a `Tree` where each of the trees in `trees` are accepted as
     /// alternative layouts.
-    pub(crate) fn alt<const N: usize>(trees: [Tree<D, R>; N]) -> Self {
+    pub(crate) fn alt<const N: usize>(trees: [Tree<D, R, T>; N]) -> Self {
         trees.into_iter().fold(Tree::uninhabited(), Self::or)
     }
 
@@ -251,11 +253,14 @@ pub(crate) mod rustc {
         FieldIdx, FieldsShape, Layout, Size, TagEncoding, TyAndLayout, VariantIdx, Variants,
     };
     use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError};
-    use rustc_middle::ty::{self, AdtDef, AdtKind, List, ScalarInt, Ty, TyCtxt, TypeVisitableExt};
+    use rustc_middle::ty::{
+        self, AdtDef, AdtKind, List, Region, ScalarInt, Ty, TyCtxt, TypeVisitableExt,
+    };
     use rustc_span::ErrorGuaranteed;
 
     use super::Tree;
-    use crate::layout::rustc::{Def, Ref, layout_of};
+    use crate::layout::Reference;
+    use crate::layout::rustc::{Def, layout_of};
 
     #[derive(Debug, Copy, Clone)]
     pub(crate) enum Err {
@@ -281,7 +286,7 @@ pub(crate) mod rustc {
         }
     }
 
-    impl<'tcx> Tree<Def<'tcx>, Ref<'tcx>> {
+    impl<'tcx> Tree<Def<'tcx>, Region<'tcx>, Ty<'tcx>> {
         pub(crate) fn from_ty(ty: Ty<'tcx>, cx: LayoutCx<'tcx>) -> Result<Self, Err> {
             use rustc_abi::HasDataLayout;
             let layout = layout_of(cx, ty)?;
@@ -353,16 +358,17 @@ pub(crate) mod rustc {
                     }
                 }
 
-                ty::Ref(lifetime, ty, mutability) => {
+                ty::Ref(region, ty, mutability) => {
                     let layout = layout_of(cx, *ty)?;
-                    let align = layout.align.abi.bytes_usize();
-                    let size = layout.size.bytes_usize();
-                    Ok(Tree::Ref(Ref {
-                        lifetime: *lifetime,
-                        ty: *ty,
-                        mutability: *mutability,
-                        align,
-                        size,
+                    let referent_align = layout.align.abi.bytes_usize();
+                    let referent_size = layout.size.bytes_usize();
+
+                    Ok(Tree::Ref(Reference {
+                        region: *region,
+                        is_mut: mutability.is_mut(),
+                        referent: *ty,
+                        referent_align,
+                        referent_size,
                     }))
                 }
 
@@ -426,7 +432,9 @@ pub(crate) mod rustc {
                     if variant_layout.is_uninhabited() {
                         return Ok(Self::uninhabited());
                     }
-                    let tag = cx.tcx().tag_for_variant((cx.tcx().erase_regions(ty), index));
+                    let tag = cx.tcx().tag_for_variant(
+                        cx.typing_env.as_query_input((cx.tcx().erase_regions(ty), index)),
+                    );
                     let variant_def = Def::Variant(def.variant(index));
                     Self::from_variant(
                         variant_def,
diff --git a/compiler/rustc_transmute/src/layout/tree/tests.rs b/compiler/rustc_transmute/src/layout/tree/tests.rs
index 8c3dbbe37ab..bc47b19c681 100644
--- a/compiler/rustc_transmute/src/layout/tree/tests.rs
+++ b/compiler/rustc_transmute/src/layout/tree/tests.rs
@@ -20,13 +20,13 @@ mod prune {
 
         #[test]
         fn seq_1() {
-            let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants).then(Tree::byte(0x00));
+            let layout: Tree<Def, !, !> = Tree::def(Def::NoSafetyInvariants).then(Tree::byte(0x00));
             assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00));
         }
 
         #[test]
         fn seq_2() {
-            let layout: Tree<Def, !> =
+            let layout: Tree<Def, !, !> =
                 Tree::byte(0x00).then(Tree::def(Def::NoSafetyInvariants)).then(Tree::byte(0x01));
 
             assert_eq!(
@@ -41,7 +41,7 @@ mod prune {
 
         #[test]
         fn invisible_def() {
-            let layout: Tree<Def, !> = Tree::def(Def::HasSafetyInvariants);
+            let layout: Tree<Def, !, !> = Tree::def(Def::HasSafetyInvariants);
             assert_eq!(
                 layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
                 Tree::uninhabited()
@@ -50,7 +50,7 @@ mod prune {
 
         #[test]
         fn invisible_def_in_seq_len_2() {
-            let layout: Tree<Def, !> =
+            let layout: Tree<Def, !, !> =
                 Tree::def(Def::NoSafetyInvariants).then(Tree::def(Def::HasSafetyInvariants));
             assert_eq!(
                 layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)),
@@ -60,7 +60,7 @@ mod prune {
 
         #[test]
         fn invisible_def_in_seq_len_3() {
-            let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants)
+            let layout: Tree<Def, !, !> = Tree::def(Def::NoSafetyInvariants)
                 .then(Tree::byte(0x00))
                 .then(Tree::def(Def::HasSafetyInvariants));
             assert_eq!(
@@ -75,20 +75,20 @@ mod prune {
 
         #[test]
         fn visible_def() {
-            let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants);
+            let layout: Tree<Def, !, !> = Tree::def(Def::NoSafetyInvariants);
             assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::unit());
         }
 
         #[test]
         fn visible_def_in_seq_len_2() {
-            let layout: Tree<Def, !> =
+            let layout: Tree<Def, !, !> =
                 Tree::def(Def::NoSafetyInvariants).then(Tree::def(Def::NoSafetyInvariants));
             assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::unit());
         }
 
         #[test]
         fn visible_def_in_seq_len_3() {
-            let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants)
+            let layout: Tree<Def, !, !> = Tree::def(Def::NoSafetyInvariants)
                 .then(Tree::byte(0x00))
                 .then(Tree::def(Def::NoSafetyInvariants));
             assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00));
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index ce18dad5517..36281ff16bc 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -19,23 +19,29 @@ pub struct Assume {
 /// Either transmutation is allowed, we have an error, or we have an optional
 /// Condition that must hold.
 #[derive(Debug, Hash, Eq, PartialEq, Clone)]
-pub enum Answer<R> {
+pub enum Answer<R, T> {
     Yes,
-    No(Reason<R>),
-    If(Condition<R>),
+    No(Reason<T>),
+    If(Condition<R, T>),
 }
 
 /// A condition which must hold for safe transmutation to be possible.
 #[derive(Debug, Hash, Eq, PartialEq, Clone)]
-pub enum Condition<R> {
+pub enum Condition<R, T> {
     /// `Src` is transmutable into `Dst`, if `src` is transmutable into `dst`.
-    IfTransmutable { src: R, dst: R },
+    Transmutable { src: T, dst: T },
+
+    /// The region `long` must outlive `short`.
+    Outlives { long: R, short: R },
+
+    /// The `ty` is immutable.
+    Immutable { ty: T },
 
     /// `Src` is transmutable into `Dst`, if all of the enclosed requirements are met.
-    IfAll(Vec<Condition<R>>),
+    IfAll(Vec<Condition<R, T>>),
 
     /// `Src` is transmutable into `Dst` if any of the enclosed requirements are met.
-    IfAny(Vec<Condition<R>>),
+    IfAny(Vec<Condition<R, T>>),
 }
 
 /// Answers "why wasn't the source type transmutable into the destination type?"
@@ -53,12 +59,16 @@ pub enum Reason<T> {
     DstMayHaveSafetyInvariants,
     /// `Dst` is larger than `Src`, and the excess bytes were not exclusively uninitialized.
     DstIsTooBig,
-    /// A referent of `Dst` is larger than a referent in `Src`.
+    /// `Dst` is larger `Src`.
     DstRefIsTooBig {
         /// The referent of the source type.
         src: T,
+        /// The size of the source type's referent.
+        src_size: usize,
         /// The too-large referent of the destination type.
         dst: T,
+        /// The size of the destination type's referent.
+        dst_size: usize,
     },
     /// Src should have a stricter alignment than Dst, but it does not.
     DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize },
@@ -79,7 +89,7 @@ pub enum Reason<T> {
 #[cfg(feature = "rustc")]
 mod rustc {
     use rustc_hir::lang_items::LangItem;
-    use rustc_middle::ty::{Const, Ty, TyCtxt};
+    use rustc_middle::ty::{Const, Region, Ty, TyCtxt};
 
     use super::*;
 
@@ -105,7 +115,7 @@ mod rustc {
             &mut self,
             types: Types<'tcx>,
             assume: crate::Assume,
-        ) -> crate::Answer<crate::layout::rustc::Ref<'tcx>> {
+        ) -> crate::Answer<Region<'tcx>, Ty<'tcx>> {
             crate::maybe_transmutable::MaybeTransmutableQuery::new(
                 types.src, types.dst, assume, self.tcx,
             )
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
index f76abe50ed3..062de4b5d08 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs
@@ -5,7 +5,7 @@ pub(crate) mod query_context;
 #[cfg(test)]
 mod tests;
 
-use crate::layout::{self, Def, Dfa, Ref, Tree, dfa, union};
+use crate::layout::{self, Def, Dfa, Reference, Tree, dfa, union};
 use crate::maybe_transmutable::query_context::QueryContext;
 use crate::{Answer, Condition, Map, Reason};
 
@@ -41,7 +41,10 @@ mod rustc {
         /// This method begins by converting `src` and `dst` from `Ty`s to `Tree`s,
         /// then computes an answer using those trees.
         #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
-        pub(crate) fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> {
+        pub(crate) fn answer(
+            self,
+        ) -> Answer<<TyCtxt<'tcx> as QueryContext>::Region, <TyCtxt<'tcx> as QueryContext>::Type>
+        {
             let Self { src, dst, assume, context } = self;
 
             let layout_cx = LayoutCx::new(context, TypingEnv::fully_monomorphized());
@@ -67,7 +70,11 @@ mod rustc {
     }
 }
 
-impl<C> MaybeTransmutableQuery<Tree<<C as QueryContext>::Def, <C as QueryContext>::Ref>, C>
+impl<C>
+    MaybeTransmutableQuery<
+        Tree<<C as QueryContext>::Def, <C as QueryContext>::Region, <C as QueryContext>::Type>,
+        C,
+    >
 where
     C: QueryContext,
 {
@@ -77,7 +84,7 @@ where
     /// then converts `src` and `dst` to `Dfa`s, and computes an answer using those DFAs.
     #[inline(always)]
     #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))]
-    pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
+    pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Region, <C as QueryContext>::Type> {
         let Self { src, dst, assume, context } = self;
 
         // Unconditionally remove all `Def` nodes from `src`, without pruning away the
@@ -130,12 +137,12 @@ where
     }
 }
 
-impl<C> MaybeTransmutableQuery<Dfa<<C as QueryContext>::Ref>, C>
+impl<C> MaybeTransmutableQuery<Dfa<<C as QueryContext>::Region, <C as QueryContext>::Type>, C>
 where
     C: QueryContext,
 {
     /// Answers whether a `Dfa` is transmutable into another `Dfa`.
-    pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> {
+    pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Region, <C as QueryContext>::Type> {
         self.answer_memo(&mut Map::default(), self.src.start, self.dst.start)
     }
 
@@ -143,10 +150,13 @@ where
     #[instrument(level = "debug", skip(self))]
     fn answer_memo(
         &self,
-        cache: &mut Map<(dfa::State, dfa::State), Answer<<C as QueryContext>::Ref>>,
+        cache: &mut Map<
+            (dfa::State, dfa::State),
+            Answer<<C as QueryContext>::Region, <C as QueryContext>::Type>,
+        >,
         src_state: dfa::State,
         dst_state: dfa::State,
-    ) -> Answer<<C as QueryContext>::Ref> {
+    ) -> Answer<<C as QueryContext>::Region, <C as QueryContext>::Type> {
         if let Some(answer) = cache.get(&(src_state, dst_state)) {
             answer.clone()
         } else {
@@ -160,10 +170,13 @@ where
 
     fn answer_impl(
         &self,
-        cache: &mut Map<(dfa::State, dfa::State), Answer<<C as QueryContext>::Ref>>,
+        cache: &mut Map<
+            (dfa::State, dfa::State),
+            Answer<<C as QueryContext>::Region, <C as QueryContext>::Type>,
+        >,
         src_state: dfa::State,
         dst_state: dfa::State,
-    ) -> Answer<<C as QueryContext>::Ref> {
+    ) -> Answer<<C as QueryContext>::Region, <C as QueryContext>::Type> {
         debug!(?src_state, ?dst_state);
         debug!(src = ?self.src);
         debug!(dst = ?self.dst);
@@ -247,27 +260,51 @@ where
                     // ...there exists a reference transition out of `dst_state`...
                     Quantifier::ThereExists.apply(self.dst.refs_from(dst_state).map(
                         |(dst_ref, dst_state_prime)| {
-                            if !src_ref.is_mutable() && dst_ref.is_mutable() {
+                            if !src_ref.is_mut && dst_ref.is_mut {
                                 Answer::No(Reason::DstIsMoreUnique)
                             } else if !self.assume.alignment
-                                && src_ref.min_align() < dst_ref.min_align()
+                                && src_ref.referent_align < dst_ref.referent_align
                             {
                                 Answer::No(Reason::DstHasStricterAlignment {
-                                    src_min_align: src_ref.min_align(),
-                                    dst_min_align: dst_ref.min_align(),
+                                    src_min_align: src_ref.referent_align,
+                                    dst_min_align: dst_ref.referent_align,
+                                })
+                            } else if dst_ref.referent_size > src_ref.referent_size {
+                                Answer::No(Reason::DstRefIsTooBig {
+                                    src: src_ref.referent,
+                                    src_size: src_ref.referent_size,
+                                    dst: dst_ref.referent,
+                                    dst_size: dst_ref.referent_size,
                                 })
-                            } else if dst_ref.size() > src_ref.size() {
-                                Answer::No(Reason::DstRefIsTooBig { src: src_ref, dst: dst_ref })
                             } else {
-                                // ...such that `src` is transmutable into `dst`, if
-                                // `src_ref` is transmutability into `dst_ref`.
-                                and(
-                                    Answer::If(Condition::IfTransmutable {
-                                        src: src_ref,
-                                        dst: dst_ref,
-                                    }),
-                                    self.answer_memo(cache, src_state_prime, dst_state_prime),
-                                )
+                                let mut conditions = Vec::with_capacity(4);
+                                let mut is_transmutable =
+                                    |src: Reference<_, _>, dst: Reference<_, _>| {
+                                        conditions.push(Condition::Transmutable {
+                                            src: src.referent,
+                                            dst: dst.referent,
+                                        });
+                                        if !self.assume.lifetimes {
+                                            conditions.push(Condition::Outlives {
+                                                long: src.region,
+                                                short: dst.region,
+                                            });
+                                        }
+                                    };
+
+                                is_transmutable(src_ref, dst_ref);
+
+                                if dst_ref.is_mut {
+                                    is_transmutable(dst_ref, src_ref);
+                                } else {
+                                    conditions.push(Condition::Immutable { ty: dst_ref.referent });
+                                }
+
+                                Answer::If(Condition::IfAll(conditions)).and(self.answer_memo(
+                                    cache,
+                                    src_state_prime,
+                                    dst_state_prime,
+                                ))
                             }
                         },
                     ))
@@ -275,67 +312,65 @@ where
             );
 
             if self.assume.validity {
-                or(bytes_answer, refs_answer)
+                bytes_answer.or(refs_answer)
             } else {
-                and(bytes_answer, refs_answer)
+                bytes_answer.and(refs_answer)
             }
         }
     }
 }
 
-fn and<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R>
-where
-    R: PartialEq,
-{
-    match (lhs, rhs) {
-        // If both are errors, then we should return the more specific one
-        (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason))
-        | (Answer::No(reason), Answer::No(_))
-        // If either is an error, return it
-        | (Answer::No(reason), _) | (_, Answer::No(reason)) => Answer::No(reason),
-        // If only one side has a condition, pass it along
-        | (Answer::Yes, other) | (other, Answer::Yes) => other,
-        // If both sides have IfAll conditions, merge them
-        (Answer::If(Condition::IfAll(mut lhs)), Answer::If(Condition::IfAll(ref mut rhs))) => {
-            lhs.append(rhs);
-            Answer::If(Condition::IfAll(lhs))
-        }
-        // If only one side is an IfAll, add the other Condition to it
-        (Answer::If(cond), Answer::If(Condition::IfAll(mut conds)))
-        | (Answer::If(Condition::IfAll(mut conds)), Answer::If(cond)) => {
-            conds.push(cond);
-            Answer::If(Condition::IfAll(conds))
+impl<R, T> Answer<R, T> {
+    fn and(self, rhs: Answer<R, T>) -> Answer<R, T> {
+        let lhs = self;
+        match (lhs, rhs) {
+            // If both are errors, then we should return the more specific one
+            (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason))
+            | (Answer::No(reason), Answer::No(_))
+            // If either is an error, return it
+            | (Answer::No(reason), _) | (_, Answer::No(reason)) => Answer::No(reason),
+            // If only one side has a condition, pass it along
+            | (Answer::Yes, other) | (other, Answer::Yes) => other,
+            // If both sides have IfAll conditions, merge them
+            (Answer::If(Condition::IfAll(mut lhs)), Answer::If(Condition::IfAll(ref mut rhs))) => {
+                lhs.append(rhs);
+                Answer::If(Condition::IfAll(lhs))
+            }
+            // If only one side is an IfAll, add the other Condition to it
+            (Answer::If(cond), Answer::If(Condition::IfAll(mut conds)))
+            | (Answer::If(Condition::IfAll(mut conds)), Answer::If(cond)) => {
+                conds.push(cond);
+                Answer::If(Condition::IfAll(conds))
+            }
+            // Otherwise, both lhs and rhs conditions can be combined in a parent IfAll
+            (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAll(vec![lhs, rhs])),
         }
-        // Otherwise, both lhs and rhs conditions can be combined in a parent IfAll
-        (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAll(vec![lhs, rhs])),
     }
-}
 
-fn or<R>(lhs: Answer<R>, rhs: Answer<R>) -> Answer<R>
-where
-    R: PartialEq,
-{
-    match (lhs, rhs) {
-        // If both are errors, then we should return the more specific one
-        (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason))
-        | (Answer::No(reason), Answer::No(_)) => Answer::No(reason),
-        // Otherwise, errors can be ignored for the rest of the pattern matching
-        (Answer::No(_), other) | (other, Answer::No(_)) => or(other, Answer::Yes),
-        // If only one side has a condition, pass it along
-        (Answer::Yes, other) | (other, Answer::Yes) => other,
-        // If both sides have IfAny conditions, merge them
-        (Answer::If(Condition::IfAny(mut lhs)), Answer::If(Condition::IfAny(ref mut rhs))) => {
-            lhs.append(rhs);
-            Answer::If(Condition::IfAny(lhs))
-        }
-        // If only one side is an IfAny, add the other Condition to it
-        (Answer::If(cond), Answer::If(Condition::IfAny(mut conds)))
-        | (Answer::If(Condition::IfAny(mut conds)), Answer::If(cond)) => {
-            conds.push(cond);
-            Answer::If(Condition::IfAny(conds))
+    fn or(self, rhs: Answer<R, T>) -> Answer<R, T> {
+        let lhs = self;
+        match (lhs, rhs) {
+            // If both are errors, then we should return the more specific one
+            (Answer::No(Reason::DstIsBitIncompatible), Answer::No(reason))
+            | (Answer::No(reason), Answer::No(_)) => Answer::No(reason),
+            // Otherwise, errors can be ignored for the rest of the pattern matching
+            (Answer::No(_), other) | (other, Answer::No(_)) => other.or(Answer::Yes),
+            // If only one side has a condition, pass it along
+            (Answer::Yes, other) | (other, Answer::Yes) => other,
+            // If both sides have IfAny conditions, merge them
+            (Answer::If(Condition::IfAny(mut lhs)), Answer::If(Condition::IfAny(ref mut rhs))) => {
+                lhs.append(rhs);
+                Answer::If(Condition::IfAny(lhs))
+            }
+            // If only one side is an IfAny, add the other Condition to it
+            (Answer::If(cond), Answer::If(Condition::IfAny(mut conds)))
+            | (Answer::If(Condition::IfAny(mut conds)), Answer::If(cond)) => {
+                conds.push(cond);
+                Answer::If(Condition::IfAny(conds))
+            }
+            // Otherwise, both lhs and rhs conditions can be combined in a parent IfAny
+            (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAny(vec![lhs, rhs])),
         }
-        // Otherwise, both lhs and rhs conditions can be combined in a parent IfAny
-        (Answer::If(lhs), Answer::If(rhs)) => Answer::If(Condition::IfAny(vec![lhs, rhs])),
     }
 }
 
@@ -345,24 +380,25 @@ enum Quantifier {
 }
 
 impl Quantifier {
-    fn apply<R, I>(&self, iter: I) -> Answer<R>
+    fn apply<R, T, I>(&self, iter: I) -> Answer<R, T>
     where
-        R: layout::Ref,
-        I: IntoIterator<Item = Answer<R>>,
+        R: layout::Region,
+        T: layout::Type,
+        I: IntoIterator<Item = Answer<R, T>>,
     {
         use std::ops::ControlFlow::{Break, Continue};
 
         let (init, try_fold_f): (_, fn(_, _) -> _) = match self {
             Self::ThereExists => {
-                (Answer::No(Reason::DstIsBitIncompatible), |accum: Answer<R>, next| {
-                    match or(accum, next) {
-                        Answer::Yes => Break(Answer::Yes),
-                        maybe => Continue(maybe),
-                    }
+                (Answer::No(Reason::DstIsBitIncompatible), |accum: Answer<R, T>, next| match accum
+                    .or(next)
+                {
+                    Answer::Yes => Break(Answer::Yes),
+                    maybe => Continue(maybe),
                 })
             }
-            Self::ForAll => (Answer::Yes, |accum: Answer<R>, next| {
-                let answer = and(accum, next);
+            Self::ForAll => (Answer::Yes, |accum: Answer<R, T>, next| {
+                let answer = accum.and(next);
                 match answer {
                     Answer::No(_) => Break(answer),
                     maybe => Continue(maybe),
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
index 214da101be3..6be0cb11904 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs
@@ -3,7 +3,8 @@ 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 Region: layout::Region;
+    type Type: layout::Type;
 }
 
 #[cfg(test)]
@@ -12,9 +13,9 @@ pub(crate) mod test {
 
     use super::QueryContext;
 
-    pub(crate) struct UltraMinimal<R = !>(PhantomData<R>);
+    pub(crate) struct UltraMinimal<R = !, T = !>(PhantomData<(R, T)>);
 
-    impl<R> Default for UltraMinimal<R> {
+    impl<R, T> Default for UltraMinimal<R, T> {
         fn default() -> Self {
             Self(PhantomData)
         }
@@ -32,20 +33,26 @@ pub(crate) mod test {
         }
     }
 
-    impl<R: crate::layout::Ref> QueryContext for UltraMinimal<R> {
+    impl<R, T> QueryContext for UltraMinimal<R, T>
+    where
+        R: crate::layout::Region,
+        T: crate::layout::Type,
+    {
         type Def = Def;
-        type Ref = R;
+        type Region = R;
+        type Type = T;
     }
 }
 
 #[cfg(feature = "rustc")]
 mod rustc {
-    use rustc_middle::ty::TyCtxt;
+    use rustc_middle::ty::{Region, Ty, TyCtxt};
 
     use super::*;
 
     impl<'tcx> super::QueryContext for TyCtxt<'tcx> {
         type Def = layout::rustc::Def<'tcx>;
-        type Ref = layout::rustc::Ref<'tcx>;
+        type Region = Region<'tcx>;
+        type Type = Ty<'tcx>;
     }
 }
diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
index 0227ad71ae6..8440ace2608 100644
--- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
+++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs
@@ -3,17 +3,17 @@ extern crate test;
 use itertools::Itertools;
 
 use super::query_context::test::{Def, UltraMinimal};
-use crate::{Answer, Assume, Reason, layout};
+use crate::{Answer, Assume, Condition, Reason, layout};
 
-type Tree = layout::Tree<Def, !>;
-type Dfa = layout::Dfa<!>;
+type Tree = layout::Tree<Def, !, !>;
+type Dfa = layout::Dfa<!, !>;
 
 trait Representation {
-    fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer<!>;
+    fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer<!, !>;
 }
 
 impl Representation for Tree {
-    fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer<!> {
+    fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer<!, !> {
         crate::maybe_transmutable::MaybeTransmutableQuery::new(
             src,
             dst,
@@ -25,7 +25,7 @@ impl Representation for Tree {
 }
 
 impl Representation for Dfa {
-    fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer<!> {
+    fn is_transmutable(src: Self, dst: Self, assume: Assume) -> Answer<!, !> {
         crate::maybe_transmutable::MaybeTransmutableQuery::new(
             src,
             dst,
@@ -40,7 +40,7 @@ fn is_transmutable<R: Representation + Clone>(
     src: &R,
     dst: &R,
     assume: Assume,
-) -> crate::Answer<!> {
+) -> crate::Answer<!, !> {
     let src = src.clone();
     let dst = dst.clone();
     // The only dimension of the transmutability analysis we want to test
@@ -53,7 +53,7 @@ mod safety {
     use super::*;
     use crate::Answer;
 
-    const DST_HAS_SAFETY_INVARIANTS: Answer<!> =
+    const DST_HAS_SAFETY_INVARIANTS: Answer<!, !> =
         Answer::No(crate::Reason::DstMayHaveSafetyInvariants);
 
     #[test]
@@ -177,14 +177,15 @@ mod bool {
 
     #[test]
     fn should_permit_validity_expansion_and_reject_contraction() {
-        let b0 = layout::Tree::<Def, !>::byte(0);
-        let b1 = layout::Tree::<Def, !>::byte(1);
-        let b2 = layout::Tree::<Def, !>::byte(2);
+        let b0 = layout::Tree::<Def, !, !>::byte(0);
+        let b1 = layout::Tree::<Def, !, !>::byte(1);
+        let b2 = layout::Tree::<Def, !, !>::byte(2);
 
         let alts = [b0, b1, b2];
 
         let into_layout = |alts: Vec<_>| {
-            alts.into_iter().fold(layout::Tree::<Def, !>::uninhabited(), layout::Tree::<Def, !>::or)
+            alts.into_iter()
+                .fold(layout::Tree::<Def, !, !>::uninhabited(), layout::Tree::<Def, !, !>::or)
         };
 
         let into_set = |alts: Vec<_>| {
@@ -277,7 +278,7 @@ mod alt {
 
     #[test]
     fn should_permit_identity_transmutation() {
-        type Tree = layout::Tree<Def, !>;
+        type Tree = layout::Tree<Def, !, !>;
 
         let x = Tree::Seq(vec![Tree::byte(0), Tree::byte(0)]);
         let y = Tree::Seq(vec![Tree::bool(), Tree::byte(1)]);
@@ -331,7 +332,7 @@ mod char {
     fn should_permit_valid_transmutation() {
         for order in [Endian::Big, Endian::Little] {
             use Answer::*;
-            let char_layout = layout::Tree::<Def, !>::char(order);
+            let char_layout = layout::Tree::<Def, !, !>::char(order);
 
             // `char`s can be in the following ranges:
             // - [0, 0xD7FF]
@@ -353,7 +354,7 @@ mod char {
                 (0xFFFFFFFF, no),
             ] {
                 let src_layout =
-                    layout::tree::Tree::<Def, !>::from_big_endian(order, src.to_be_bytes());
+                    layout::tree::Tree::<Def, !, !>::from_big_endian(order, src.to_be_bytes());
 
                 let a = is_transmutable(&src_layout, &char_layout, Assume::default());
                 assert_eq!(a, answer, "endian:{order:?},\nsrc:{src:x}");
@@ -371,7 +372,7 @@ mod nonzero {
     #[test]
     fn should_permit_identity_transmutation() {
         for width in NONZERO_BYTE_WIDTHS {
-            let layout = layout::Tree::<Def, !>::nonzero(width);
+            let layout = layout::Tree::<Def, !, !>::nonzero(width);
             assert_eq!(is_transmutable(&layout, &layout, Assume::default()), Answer::Yes);
         }
     }
@@ -381,8 +382,8 @@ mod nonzero {
         for width in NONZERO_BYTE_WIDTHS {
             use Answer::*;
 
-            let num = layout::Tree::<Def, !>::number(width);
-            let nz = layout::Tree::<Def, !>::nonzero(width);
+            let num = layout::Tree::<Def, !, !>::number(width);
+            let nz = layout::Tree::<Def, !, !>::nonzero(width);
 
             let a = is_transmutable(&num, &nz, Assume::default());
             assert_eq!(a, No(Reason::DstIsBitIncompatible), "width:{width}");
@@ -395,13 +396,23 @@ mod nonzero {
 
 mod r#ref {
     use super::*;
+    use crate::layout::Reference;
 
     #[test]
     fn should_permit_identity_transmutation() {
-        type Tree = crate::layout::Tree<Def, [(); 1]>;
+        type Tree = crate::layout::Tree<Def, usize, ()>;
 
         for validity in [false, true] {
-            let layout = Tree::Seq(vec![Tree::byte(0x00), Tree::Ref([()])]);
+            let layout = Tree::Seq(vec![
+                Tree::byte(0x00),
+                Tree::Ref(Reference {
+                    region: 42,
+                    is_mut: false,
+                    referent: (),
+                    referent_size: 0,
+                    referent_align: 1,
+                }),
+            ]);
 
             let assume = Assume { validity, ..Assume::default() };
 
@@ -414,7 +425,11 @@ mod r#ref {
             .answer();
             assert_eq!(
                 answer,
-                Answer::If(crate::Condition::IfTransmutable { src: [()], dst: [()] })
+                Answer::If(Condition::IfAll(vec![
+                    Condition::Transmutable { src: (), dst: () },
+                    Condition::Outlives { long: 42, short: 42 },
+                    Condition::Immutable { ty: () },
+                ]))
             );
         }
     }