diff options
Diffstat (limited to 'compiler/rustc_transmute/src')
| -rw-r--r-- | compiler/rustc_transmute/src/layout/mod.rs | 41 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/layout/tree.rs | 25 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/layout/tree/tests.rs | 69 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/lib.rs | 30 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/maybe_transmutable/mod.rs | 76 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/maybe_transmutable/query_context.rs | 45 | ||||
| -rw-r--r-- | compiler/rustc_transmute/src/maybe_transmutable/tests.rs | 65 |
7 files changed, 223 insertions, 128 deletions
diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index 76d97e0e6e7..a7c60c3b490 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -29,18 +29,30 @@ impl fmt::Debug for Byte { } } -pub(crate) trait Def: Debug + Hash + Eq + PartialEq + Copy + Clone {} +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; + fn is_mutable(&self) -> bool; } -impl Def for ! {} +impl Def for ! { + fn has_safety_invariants(&self) -> bool { + unreachable!() + } +} + impl Ref for ! { fn min_align(&self) -> usize { unreachable!() } + fn size(&self) -> usize { + unreachable!() + } fn is_mutable(&self) -> bool { unreachable!() } @@ -50,6 +62,7 @@ impl Ref for ! { pub mod rustc { use rustc_middle::mir::Mutability; use rustc_middle::ty::{self, Ty}; + use std::fmt::{self, Write}; /// A reference in the layout. #[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Copy)] @@ -58,6 +71,7 @@ pub mod rustc { pub ty: Ty<'tcx>, pub mutability: Mutability, pub align: usize, + pub size: usize, } impl<'tcx> super::Ref for Ref<'tcx> { @@ -65,6 +79,10 @@ pub mod rustc { self.align } + fn size(&self) -> usize { + self.size + } + fn is_mutable(&self) -> bool { match self.mutability { Mutability::Mut => true, @@ -74,6 +92,16 @@ pub mod rustc { } 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) + } + } + /// A visibility node in the layout. #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] pub enum Def<'tcx> { @@ -83,5 +111,12 @@ pub mod rustc { Primitive, } - impl<'tcx> super::Def for Def<'tcx> {} + impl<'tcx> super::Def for Def<'tcx> { + fn has_safety_invariants(&self) -> bool { + // Rust presently has no notion of 'unsafe fields', so for now we + // make the conservative assumption that everything besides + // primitive types carry safety invariants. + self != &Self::Primitive + } + } } diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 49f24f66b24..9a43d67d435 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -81,7 +81,8 @@ where Self::Seq(vec![Self::uninit(); width_in_bytes]) } - /// Remove all `Def` nodes, and all branches of the layout for which `f` produces false. + /// 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> where F: Fn(D) -> bool, @@ -106,7 +107,7 @@ where Self::Byte(b) => Tree::Byte(b), Self::Ref(r) => Tree::Ref(r), Self::Def(d) => { - if !f(d) { + if f(d) { Tree::uninhabited() } else { Tree::unit() @@ -185,8 +186,8 @@ pub(crate) mod rustc { #[derive(Debug, Copy, Clone)] pub(crate) enum Err { - /// The layout of the type is unspecified. - Unspecified, + /// The layout of the type is not yet supported. + NotYetSupported, /// This error will be surfaced elsewhere by rustc, so don't surface it. UnknownLayout, /// Overflow size @@ -199,6 +200,7 @@ pub(crate) mod rustc { match err { LayoutError::Unknown(..) | LayoutError::ReferencesError(..) => Self::UnknownLayout, LayoutError::SizeOverflow(..) => Self::SizeOverflow, + LayoutError::Cycle(err) => Self::TypeError(*err), err => unimplemented!("{:?}", err), } } @@ -286,14 +288,14 @@ pub(crate) mod rustc { if members.len() == 0 { Ok(Tree::unit()) } else { - Err(Err::Unspecified) + Err(Err::NotYetSupported) } } ty::Array(ty, len) => { let len = len .try_eval_target_usize(tcx, ParamEnv::reveal_all()) - .ok_or(Err::Unspecified)?; + .ok_or(Err::NotYetSupported)?; let elt = Tree::from_ty(*ty, tcx)?; Ok(std::iter::repeat(elt) .take(len as usize) @@ -305,7 +307,7 @@ pub(crate) mod rustc { // If the layout is ill-specified, halt. if !(adt_def.repr().c() || adt_def.repr().int.is_some()) { - return Err(Err::Unspecified); + return Err(Err::NotYetSupported); } // Compute a summary of the type's layout. @@ -346,7 +348,7 @@ pub(crate) mod rustc { AdtKind::Union => { // is the layout well-defined? if !adt_def.repr().c() { - return Err(Err::Unspecified); + return Err(Err::NotYetSupported); } let ty_layout = layout_of(tcx, ty)?; @@ -370,16 +372,19 @@ pub(crate) mod rustc { } ty::Ref(lifetime, ty, mutability) => { - let align = layout_of(tcx, *ty)?.align(); + let layout = layout_of(tcx, *ty)?; + let align = layout.align(); + let size = layout.size(); Ok(Tree::Ref(Ref { lifetime: *lifetime, ty: *ty, mutability: *mutability, align, + size, })) } - _ => Err(Err::Unspecified), + _ => Err(Err::NotYetSupported), } } diff --git a/compiler/rustc_transmute/src/layout/tree/tests.rs b/compiler/rustc_transmute/src/layout/tree/tests.rs index 90515e92f7a..3cb47517c21 100644 --- a/compiler/rustc_transmute/src/layout/tree/tests.rs +++ b/compiler/rustc_transmute/src/layout/tree/tests.rs @@ -2,11 +2,15 @@ use super::Tree; #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] pub enum Def { - Visible, - Invisible, + NoSafetyInvariants, + HasSafetyInvariants, } -impl super::Def for Def {} +impl super::Def for Def { + fn has_safety_invariants(&self) -> bool { + self == &Self::HasSafetyInvariants + } +} mod prune { use super::*; @@ -16,17 +20,22 @@ mod prune { #[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)); + let layout: Tree<Def, !> = + Tree::def(Def::NoSafetyInvariants).then(Tree::from_bits(0x00)); + assert_eq!( + layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), + 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)); + let layout: Tree<Def, !> = Tree::from_bits(0x00) + .then(Tree::def(Def::NoSafetyInvariants)) + .then(Tree::from_bits(0x01)); assert_eq!( - layout.prune(&|d| matches!(d, Def::Visible)), + layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::from_bits(0x00).then(Tree::from_bits(0x01)) ); } @@ -37,21 +46,32 @@ mod prune { #[test] fn invisible_def() { - let layout: Tree<Def, !> = Tree::def(Def::Invisible); - assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::uninhabited()); + let layout: Tree<Def, !> = Tree::def(Def::HasSafetyInvariants); + assert_eq!( + layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), + 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()); + let layout: Tree<Def, !> = + Tree::def(Def::NoSafetyInvariants).then(Tree::def(Def::HasSafetyInvariants)); + assert_eq!( + layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), + 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()); + let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants) + .then(Tree::from_bits(0x00)) + .then(Tree::def(Def::HasSafetyInvariants)); + assert_eq!( + layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), + Tree::uninhabited() + ); } } @@ -60,21 +80,26 @@ mod prune { #[test] fn visible_def() { - let layout: Tree<Def, !> = Tree::def(Def::Visible); - assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::unit()); + 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, !> = Tree::def(Def::Visible).then(Tree::def(Def::Visible)); - assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::unit()); + 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::Visible).then(Tree::from_bits(0x00)).then(Tree::def(Def::Visible)); - assert_eq!(layout.prune(&|d| matches!(d, Def::Visible)), Tree::from_bits(0x00)); + let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants) + .then(Tree::from_bits(0x00)) + .then(Tree::def(Def::NoSafetyInvariants)); + assert_eq!( + layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), + Tree::from_bits(0x00) + ); } } } diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs index 4b559632017..e871c4659b4 100644 --- a/compiler/rustc_transmute/src/lib.rs +++ b/compiler/rustc_transmute/src/lib.rs @@ -1,7 +1,6 @@ -#![feature(alloc_layout_extra, decl_macro, iterator_try_reduce, never_type)] +#![feature(alloc_layout_extra)] +#![feature(never_type)] #![allow(dead_code, unused_variables)] -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate tracing; @@ -24,7 +23,7 @@ pub struct Assume { #[derive(Debug, Hash, Eq, PartialEq, Clone)] pub enum Answer<R> { Yes, - No(Reason), + No(Reason<R>), If(Condition<R>), } @@ -43,17 +42,24 @@ pub enum Condition<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, +pub enum Reason<T> { + /// The layout of the source type is not yet supported. + SrcIsNotYetSupported, + /// The layout of the destination type is not yet supported. + DstIsNotYetSupported, /// The layout of the destination type is bit-incompatible with the source type. DstIsBitIncompatible, - /// There aren't any public constructors for `Dst`. - DstIsPrivate, + /// The destination type may carry safety invariants. + 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`. + DstRefIsTooBig { + /// The referent of the source type. + src: T, + /// The too-large referent of the destination type. + dst: T, + }, /// Src should have a stricter alignment than Dst, but it does not. DstHasStricterAlignment { src_min_align: usize, dst_min_align: usize }, /// Can't go from shared pointer to unique pointer @@ -107,13 +113,11 @@ mod rustc { &mut self, cause: ObligationCause<'tcx>, types: Types<'tcx>, - scope: Ty<'tcx>, assume: crate::Assume, ) -> crate::Answer<crate::layout::rustc::Ref<'tcx>> { crate::maybe_transmutable::MaybeTransmutableQuery::new( types.src, types.dst, - scope, assume, self.infcx.tcx, ) diff --git a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs index bf3c390c800..16d15580a05 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/mod.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/mod.rs @@ -3,7 +3,7 @@ pub(crate) mod query_context; mod tests; use crate::{ - layout::{self, dfa, Byte, Dfa, Nfa, Ref, Tree, Uninhabited}, + layout::{self, dfa, Byte, Def, Dfa, Nfa, Ref, Tree, Uninhabited}, maybe_transmutable::query_context::QueryContext, Answer, Condition, Map, Reason, }; @@ -14,7 +14,6 @@ where { src: L, dst: L, - scope: <C as QueryContext>::Scope, assume: crate::Assume, context: C, } @@ -23,14 +22,8 @@ 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 new(src: L, dst: L, assume: crate::Assume, context: C) -> Self { + Self { src, dst, assume, context } } } @@ -48,7 +41,7 @@ mod rustc { /// then computes an answer using those trees. #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] pub fn answer(self) -> Answer<<TyCtxt<'tcx> as QueryContext>::Ref> { - let Self { src, dst, scope, assume, context } = self; + let Self { src, dst, assume, context } = self; // Convert `src` and `dst` from their rustc representations, to `Tree`-based // representations. If these conversions fail, conclude that the transmutation is @@ -63,13 +56,11 @@ mod rustc { } (Err(Err::UnknownLayout), _) => Answer::No(Reason::SrcLayoutUnknown), (_, Err(Err::UnknownLayout)) => Answer::No(Reason::DstLayoutUnknown), - (Err(Err::Unspecified), _) => Answer::No(Reason::SrcIsUnspecified), - (_, Err(Err::Unspecified)) => Answer::No(Reason::DstIsUnspecified), + (Err(Err::NotYetSupported), _) => Answer::No(Reason::SrcIsNotYetSupported), + (_, Err(Err::NotYetSupported)) => Answer::No(Reason::DstIsNotYetSupported), (Err(Err::SizeOverflow), _) => Answer::No(Reason::SrcSizeOverflow), (_, Err(Err::SizeOverflow)) => Answer::No(Reason::DstSizeOverflow), - (Ok(src), Ok(dst)) => { - MaybeTransmutableQuery { src, dst, scope, assume, context }.answer() - } + (Ok(src), Ok(dst)) => MaybeTransmutableQuery { src, dst, assume, context }.answer(), } } } @@ -86,43 +77,51 @@ where #[inline(always)] #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> { - let assume_visibility = self.assume.safety; - - let Self { src, dst, scope, assume, context } = self; + let Self { src, dst, assume, context } = self; - // Remove all `Def` nodes from `src`, without checking their visibility. - let src = src.prune(&|def| true); + // Unconditionally all `Def` nodes from `src`, without pruning away the + // branches they appear in. This is valid to do for value-to-value + // transmutations, but not for `&mut T` to `&mut U`; we will need to be + // more sophisticated to handle transmutations between mutable + // references. + let src = src.prune(&|def| false); trace!(?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) + let dst = if assume.safety { + // ...if safety is assumed, don't check if they carry safety + // invariants; retain all paths. + dst.prune(&|def| false) } else { - // ...otherwise, prune away all unreachable paths through the `Dst` layout. - dst.prune(&|def| context.is_accessible_from(def, scope)) + // ...otherwise, prune away all paths with safety invariants from + // the `Dst` layout. + dst.prune(&|def| def.has_safety_invariants()) }; trace!(?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. + // 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 = match Nfa::from_tree(src) { Ok(src) => src, Err(Uninhabited) => return 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. + // 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. Valid instances of + // the `dst` type do not exist, either because it's genuinely + // uninhabited, or because there are no branches of the tree that are + // free of safety invariants. let dst = match Nfa::from_tree(dst) { Ok(dst) => dst, - Err(Uninhabited) => return Answer::No(Reason::DstIsPrivate), + Err(Uninhabited) => return Answer::No(Reason::DstMayHaveSafetyInvariants), }; - MaybeTransmutableQuery { src, dst, scope, assume, context }.answer() + MaybeTransmutableQuery { src, dst, assume, context }.answer() } } @@ -136,10 +135,10 @@ where #[inline(always)] #[instrument(level = "debug", skip(self), fields(src = ?self.src, dst = ?self.dst))] pub(crate) fn answer(self) -> Answer<<C as QueryContext>::Ref> { - let Self { src, dst, scope, assume, context } = self; + let Self { src, dst, assume, context } = self; let src = Dfa::from_nfa(src); let dst = Dfa::from_nfa(dst); - MaybeTransmutableQuery { src, dst, scope, assume, context }.answer() + MaybeTransmutableQuery { src, dst, assume, context }.answer() } } @@ -267,6 +266,11 @@ where src_min_align: src_ref.min_align(), dst_min_align: dst_ref.min_align(), }) + } 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`. diff --git a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs index 0cae0377ee8..54ed03d44e6 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/query_context.rs @@ -6,9 +6,6 @@ pub(crate) trait QueryContext { 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; } @@ -20,21 +17,21 @@ pub(crate) mod test { #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] pub(crate) enum Def { - Visible, - Invisible, + HasSafetyInvariants, + NoSafetyInvariants, } - impl crate::layout::Def for Def {} + impl crate::layout::Def for Def { + fn has_safety_invariants(&self) -> bool { + self == &Self::HasSafetyInvariants + } + } 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!() } @@ -52,34 +49,6 @@ mod rustc { type Scope = Ty<'tcx>; - #[instrument(level = "debug", 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() { - self.parent(adt_def.did()) - } 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: bool = self.visibility(def_id).is_accessible_from(parent, *self); - - trace!(?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 index e49bebf571d..9c7abf1cbd6 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -3,6 +3,65 @@ use crate::maybe_transmutable::MaybeTransmutableQuery; use crate::{layout, Reason}; use itertools::Itertools; +mod safety { + use crate::Answer; + + use super::*; + + type Tree = layout::Tree<Def, !>; + + const DST_HAS_SAFETY_INVARIANTS: Answer<!> = + Answer::No(crate::Reason::DstMayHaveSafetyInvariants); + + fn is_transmutable(src: &Tree, dst: &Tree, assume_safety: bool) -> crate::Answer<!> { + let src = src.clone(); + let dst = dst.clone(); + // The only dimension of the transmutability analysis we want to test + // here is the safety analysis. To ensure this, we disable all other + // toggleable aspects of the transmutability analysis. + let assume = crate::Assume { + alignment: true, + lifetimes: true, + validity: true, + safety: assume_safety, + }; + crate::maybe_transmutable::MaybeTransmutableQuery::new(src, dst, assume, UltraMinimal) + .answer() + } + + #[test] + fn src_safe_dst_safe() { + let src = Tree::Def(Def::NoSafetyInvariants).then(Tree::u8()); + let dst = Tree::Def(Def::NoSafetyInvariants).then(Tree::u8()); + assert_eq!(is_transmutable(&src, &dst, false), Answer::Yes); + assert_eq!(is_transmutable(&src, &dst, true), Answer::Yes); + } + + #[test] + fn src_safe_dst_unsafe() { + let src = Tree::Def(Def::NoSafetyInvariants).then(Tree::u8()); + let dst = Tree::Def(Def::HasSafetyInvariants).then(Tree::u8()); + assert_eq!(is_transmutable(&src, &dst, false), DST_HAS_SAFETY_INVARIANTS); + assert_eq!(is_transmutable(&src, &dst, true), Answer::Yes); + } + + #[test] + fn src_unsafe_dst_safe() { + let src = Tree::Def(Def::HasSafetyInvariants).then(Tree::u8()); + let dst = Tree::Def(Def::NoSafetyInvariants).then(Tree::u8()); + assert_eq!(is_transmutable(&src, &dst, false), Answer::Yes); + assert_eq!(is_transmutable(&src, &dst, true), Answer::Yes); + } + + #[test] + fn src_unsafe_dst_unsafe() { + let src = Tree::Def(Def::HasSafetyInvariants).then(Tree::u8()); + let dst = Tree::Def(Def::HasSafetyInvariants).then(Tree::u8()); + assert_eq!(is_transmutable(&src, &dst, false), DST_HAS_SAFETY_INVARIANTS); + assert_eq!(is_transmutable(&src, &dst, true), Answer::Yes); + } +} + mod bool { use crate::Answer; @@ -10,11 +69,9 @@ mod bool { #[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, safety: false }, UltraMinimal, ) @@ -27,7 +84,6 @@ mod bool { let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( layout::Dfa::<!>::bool(), layout::Dfa::<!>::bool(), - (), crate::Assume { alignment: false, lifetimes: false, validity: true, safety: false }, UltraMinimal, ) @@ -71,7 +127,6 @@ mod bool { MaybeTransmutableQuery::new( src_layout.clone(), dst_layout.clone(), - (), crate::Assume { validity: false, ..crate::Assume::default() }, UltraMinimal, ) @@ -86,7 +141,6 @@ mod bool { MaybeTransmutableQuery::new( src_layout.clone(), dst_layout.clone(), - (), crate::Assume { validity: true, ..crate::Assume::default() }, UltraMinimal, ) @@ -101,7 +155,6 @@ mod bool { MaybeTransmutableQuery::new( src_layout.clone(), dst_layout.clone(), - (), crate::Assume { validity: false, ..crate::Assume::default() }, UltraMinimal, ) |
