diff --git a/src/impls.rs b/src/impls.rs index 296c9bd8c5..53673fb32a 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -14,6 +14,10 @@ use core::{ }; use super::*; +use crate::pointer::{ + invariant::{Initialized, Shared}, + SizeEq, +}; // SAFETY: Per the reference [1], "the unit tuple (`()`) ... is guaranteed as a // zero-sized type to have a size of 0 and an alignment of 1." @@ -105,10 +109,11 @@ assert_unaligned!(bool); // pattern 0x01. const _: () = unsafe { unsafe_impl!(=> TryFromBytes for bool; |byte| { - let byte = byte.transmute::(); + let byte = byte.transmute::(); *byte.unaligned_as_ref() < 2 }) }; +impl_size_eq!(ReadOnly, u8); impl_size_eq!(bool, u8); // SAFETY: @@ -134,7 +139,8 @@ const _: () = unsafe { unsafe_impl!(char: Immutable, FromZeros, IntoBytes) }; // `char`. const _: () = unsafe { unsafe_impl!(=> TryFromBytes for char; |c| { - let c = c.transmute::, invariant::Valid, _>(); + impl_size_eq!(ReadOnly, Unalign); + let c = c.transmute::, invariant::Valid, BecauseImmutable>(); let c = c.read_unaligned().into_inner(); char::from_u32(c).is_some() }); @@ -167,7 +173,8 @@ const _: () = unsafe { unsafe_impl!(str: Immutable, FromZeros, IntoBytes, Unalig // Returns `Err` if the slice is not UTF-8. const _: () = unsafe { unsafe_impl!(=> TryFromBytes for str; |c| { - let c = c.transmute::<[u8], invariant::Valid, _>(); + impl_size_eq!(ReadOnly, [u8]); + let c = c.transmute::<[u8], invariant::Valid, BecauseImmutable>(); let c = c.unaligned_as_ref(); core::str::from_utf8(c).is_ok() }) @@ -179,9 +186,10 @@ macro_rules! unsafe_impl_try_from_bytes_for_nonzero { ($($nonzero:ident[$prim:ty]),*) => { $( unsafe_impl!(=> TryFromBytes for $nonzero; |n| { - impl_size_eq!($nonzero, Unalign<$prim>); + // impl_size_eq!($nonzero, Unalign<$prim>); + impl_size_eq!(ReadOnly<$nonzero>, Unalign<$prim>); - let n = n.transmute::, invariant::Valid, _>(); + let n = n.transmute::, invariant::Valid, BecauseImmutable>(); $nonzero::new(n.read_unaligned().into_inner()).is_some() }); )* @@ -390,6 +398,9 @@ mod atomics { macro_rules! impl_traits_for_atomics { ($($atomics:ident [$primitives:ident]),* $(,)?) => { $( + // impl_transitive_transmute_from!(T => ReadOnly => T => Wrapping => ReadOnly>); + // impl_size_eq!($atomics, ReadOnly<$atomics>); + impl_transitive_transmute_from!(=> ReadOnly<$atomics> => $atomics => $primitives => ReadOnly<$primitives>); impl_known_layout!($atomics); impl_for_transmute_from!(=> TryFromBytes for $atomics [UnsafeCell<$primitives>]); impl_for_transmute_from!(=> FromZeros for $atomics [UnsafeCell<$primitives>]); @@ -490,6 +501,7 @@ mod atomics { impl_known_layout!(AtomicBool); + impl_transitive_transmute_from!(=> ReadOnly => AtomicBool => bool => ReadOnly); impl_for_transmute_from!(=> TryFromBytes for AtomicBool [UnsafeCell]); impl_for_transmute_from!(=> FromZeros for AtomicBool [UnsafeCell]); impl_for_transmute_from!(=> IntoBytes for AtomicBool [UnsafeCell]); @@ -635,6 +647,7 @@ mod atomics { // FIXME(#170): Implement `FromBytes` and `IntoBytes` once we implement // those traits for `*mut T`. + impl_transitive_transmute_from!(T => ReadOnly> => AtomicPtr => *mut T => ReadOnly<*mut T>); impl_for_transmute_from!(T => TryFromBytes for AtomicPtr [UnsafeCell<*mut T>]); impl_for_transmute_from!(T => FromZeros for AtomicPtr [UnsafeCell<*mut T>]); @@ -682,6 +695,7 @@ const _: () = unsafe { assert_unaligned!(PhantomData<()>, PhantomData, PhantomData); }; +impl_transitive_transmute_from!(T => ReadOnly => T => Wrapping => ReadOnly>); impl_for_transmute_from!(T: TryFromBytes => TryFromBytes for Wrapping[]); impl_for_transmute_from!(T: FromZeros => FromZeros for Wrapping[]); impl_for_transmute_from!(T: FromBytes => FromBytes for Wrapping[]); @@ -755,6 +769,7 @@ assert_unaligned!(CoreMaybeUninit<()>, CoreMaybeUninit); // [2] https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html#impl-Deref-for-ManuallyDrop%3CT%3E const _: () = unsafe { unsafe_impl!(T: ?Sized + Immutable => Immutable for ManuallyDrop) }; +impl_transitive_transmute_from!(T: ?Sized => ReadOnly => T => ManuallyDrop => ReadOnly>); impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop[]); impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for ManuallyDrop[]); impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop[]); @@ -769,6 +784,7 @@ impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for ManuallyDrop[ const _: () = unsafe { unsafe_impl!(T: ?Sized + Unaligned => Unaligned for ManuallyDrop) }; assert_unaligned!(ManuallyDrop<()>, ManuallyDrop); +impl_transitive_transmute_from!(T: ?Sized => ReadOnly => T => Cell => ReadOnly>); impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for Cell[UnsafeCell]); impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for Cell[UnsafeCell]); impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for Cell[UnsafeCell]); @@ -804,29 +820,9 @@ unsafe impl TryFromBytes for UnsafeCell { } #[inline] - fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool { - // The only way to implement this function is using an exclusive-aliased - // pointer. `UnsafeCell`s cannot be read via shared-aliased pointers - // (other than by using `unsafe` code, which we can't use since we can't - // guarantee how our users are accessing or modifying the `UnsafeCell`). - // - // `is_bit_valid` is documented as panicking or failing to monomorphize - // if called with a shared-aliased pointer on a type containing an - // `UnsafeCell`. In practice, it will always be a monorphization error. - // Since `is_bit_valid` is `#[doc(hidden)]` and only called directly - // from this crate, we only need to worry about our own code incorrectly - // calling `UnsafeCell::is_bit_valid`. The post-monomorphization error - // makes it easier to test that this is truly the case, and also means - // that if we make a mistake, it will cause downstream code to fail to - // compile, which will immediately surface the mistake and give us a - // chance to fix it quickly. - let c = candidate.into_exclusive_or_pme(); - - // SAFETY: Since `UnsafeCell` and `T` have the same layout and bit - // validity, `UnsafeCell` is bit-valid exactly when its wrapped `T` - // is. Thus, this is a sound implementation of - // `UnsafeCell::is_bit_valid`. - T::is_bit_valid(c.get_mut()) + fn is_bit_valid(candidate: Maybe<'_, Self>) -> bool { + impl_transitive_transmute_from!(T: ?Sized => ReadOnly> => UnsafeCell => T => ReadOnly); + T::is_bit_valid(candidate.transmute::<_, _, BecauseImmutable>()) } } @@ -854,11 +850,14 @@ unsafe impl TryFromBytes for UnsafeCell { const _: () = unsafe { unsafe_impl!(const N: usize, T: Immutable => Immutable for [T; N]); unsafe_impl!(const N: usize, T: TryFromBytes => TryFromBytes for [T; N]; |c| { + unsafe { impl_size_eq!(T => ReadOnly<[T]>, [ReadOnly]) }; + unsafe { impl_size_eq!(const N: usize, T => ReadOnly<[T; N]>, [ReadOnly; N]) }; // Note that this call may panic, but it would still be sound even if it // did. `is_bit_valid` does not promise that it will not panic (in fact, // it explicitly warns that it's a possibility), and we have not // violated any safety invariants that we must fix before returning. - <[T] as TryFromBytes>::is_bit_valid(c.as_slice()) + let c: Ptr<'_, [ReadOnly; N], _> = c.transmute::<_, _, BecauseImmutable>(); + <[T] as TryFromBytes>::is_bit_valid(c.as_slice().transmute::<_, _, BecauseImmutable>()) }); unsafe_impl!(const N: usize, T: FromZeros => FromZeros for [T; N]); unsafe_impl!(const N: usize, T: FromBytes => FromBytes for [T; N]); @@ -887,6 +886,7 @@ const _: () = unsafe { // not panic (in fact, it explicitly warns that it's a possibility), and // we have not violated any safety invariants that we must fix before // returning. + let c: Ptr<'_, [ReadOnly], _> = c.transmute::<_, _, BecauseImmutable>(); c.iter().all(::is_bit_valid) }); unsafe_impl!(T: FromZeros => FromZeros for [T]); @@ -1096,12 +1096,12 @@ mod tests { // types must implement `TryFromBytesTestable` directly (ie using // `impl_try_from_bytes_testable!`). trait TryFromBytesTestable { - fn with_passing_test_cases)>(f: F); + fn with_passing_test_cases>)>(f: F); fn with_failing_test_cases(f: F); } impl TryFromBytesTestable for T { - fn with_passing_test_cases)>(f: F) { + fn with_passing_test_cases>)>(f: F) { // Test with a zeroed value. f(Self::new_box_zeroed().unwrap()); @@ -1124,9 +1124,9 @@ mod tests { ($($tys:ty),*) => { $( impl TryFromBytesTestable for Option<$tys> { - fn with_passing_test_cases)>(f: F) { + fn with_passing_test_cases>)>(f: F) { // Test with a zeroed value. - f(Box::new(None)); + f(Box::new(ReadOnly::new(None))); } fn with_failing_test_cases(f: F) { @@ -1164,7 +1164,7 @@ mod tests { // Implements only the methods; caller must invoke this from inside // an impl block. (@methods @success $($success_case:expr),* $(, @failure $($failure_case:expr),*)?) => { - fn with_passing_test_cases)>(_f: F) { + fn with_passing_test_cases>)>(_f: F) { $( _f(Box::::from($success_case)); )* @@ -1266,17 +1266,15 @@ mod tests { pub(super) trait TestIsBitValidShared { #[allow(clippy::needless_lifetimes)] - fn test_is_bit_valid_shared<'ptr, A: invariant::Reference>( - &self, - candidate: Maybe<'ptr, T, A>, - ) -> Option; + fn test_is_bit_valid_shared<'ptr>(&self, candidate: Maybe<'ptr, T>) + -> Option; } impl TestIsBitValidShared for AutorefWrapper { #[allow(clippy::needless_lifetimes)] - fn test_is_bit_valid_shared<'ptr, A: invariant::Reference>( + fn test_is_bit_valid_shared<'ptr>( &self, - candidate: Maybe<'ptr, T, A>, + candidate: Maybe<'ptr, T>, ) -> Option { Some(T::is_bit_valid(candidate)) } @@ -1330,12 +1328,12 @@ mod tests { pub(super) trait TestAsBytes { #[allow(clippy::needless_lifetimes)] - fn test_as_bytes<'slf, 't>(&'slf self, t: &'t T) -> Option<&'t [u8]>; + fn test_as_bytes<'slf, 't>(&'slf self, t: &'t ReadOnly) -> Option<&'t [u8]>; } impl TestAsBytes for AutorefWrapper { #[allow(clippy::needless_lifetimes)] - fn test_as_bytes<'slf, 't>(&'slf self, t: &'t T) -> Option<&'t [u8]> { + fn test_as_bytes<'slf, 't>(&'slf self, t: &'t ReadOnly) -> Option<&'t [u8]> { Some(t.as_bytes()) } } @@ -1382,9 +1380,9 @@ mod tests { #[allow(unused, non_local_definitions)] impl AutorefWrapper<$ty> { #[allow(clippy::needless_lifetimes)] - fn test_is_bit_valid_shared<'ptr, A: invariant::Reference>( + fn test_is_bit_valid_shared<'ptr>( &mut self, - candidate: Maybe<'ptr, $ty, A>, + candidate: Maybe<'ptr, $ty>, ) -> Option { assert_on_allowlist!( test_is_bit_valid_shared($ty): @@ -1442,7 +1440,7 @@ mod tests { None } - fn test_as_bytes(&mut self, _t: &$ty) -> Option<&[u8]> { + fn test_as_bytes(&mut self, _t: &ReadOnly<$ty>) -> Option<&[u8]> { assert_on_allowlist!( test_as_bytes($ty): Option<&'static UnsafeCell>, @@ -1491,6 +1489,7 @@ mod tests { // necessarily `IntoBytes`, but that's the corner we've // backed ourselves into by using `Ptr::from_ref`. let c = unsafe { c.assume_initialized() }; + let c = unsafe { c.assume_aliasing::() }; let res = w.test_is_bit_valid_shared(c); if let Some(res) = res { assert!(res, "{}::is_bit_valid({:?}) (shared `Ptr`): got false, expected true", stringify!($ty), val); @@ -1502,6 +1501,7 @@ mod tests { // necessarily `IntoBytes`, but that's the corner we've // backed ourselves into by using `Ptr::from_ref`. let c = unsafe { c.assume_initialized() }; + let c = unsafe { c.assume_aliasing() }; let res = <$ty as TryFromBytes>::is_bit_valid(c); assert!(res, "{}::is_bit_valid({:?}) (exclusive `Ptr`): got false, expected true", stringify!($ty), val); diff --git a/src/lib.rs b/src/lib.rs index 66e54e034d..abd515b396 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -386,7 +386,7 @@ use core::{ #[cfg(feature = "std")] use std::io; -use crate::pointer::invariant::{self, BecauseExclusive}; +use crate::pointer::invariant::{self, BecauseExclusive, Initialized}; pub use crate::{ byte_slice::*, byteorder::*, @@ -1520,7 +1520,7 @@ pub unsafe trait TryFromBytes { /// [`UnsafeCell`]: core::cell::UnsafeCell /// [`Shared`]: invariant::Shared #[doc(hidden)] - fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool; + fn is_bit_valid(candidate: Maybe<'_, Self>) -> bool; /// Attempts to interpret the given `source` as a `&Self`. /// @@ -2928,7 +2928,12 @@ unsafe fn try_read_from( // via `c_ptr` so long as it is live, so we don't need to worry about the // fact that `c_ptr` may have more restricted validity than `candidate`. let c_ptr = unsafe { c_ptr.assume_validity::() }; - let c_ptr = c_ptr.transmute(); + let c_ptr = c_ptr.transmute::(); + // unsafe { impl_size_eq!(T => T, Wrapping) }; + impl_transitive_transmute_from!(T => T => Wrapping => ReadOnly>); + let c_ptr: Ptr<'_, _, (invariant::Exclusive, invariant::Aligned, Initialized)> = todo!(); + // let c_ptr = c_ptr.transmute::>, Initialized, BecauseExclusive>(); + // let c_ptr = c_ptr.transmute(); // Since we don't have `T: KnownLayout`, we hack around that by using // `Wrapping`, which implements `KnownLayout` even if `T` doesn't. @@ -2941,7 +2946,8 @@ unsafe fn try_read_from( // `try_into_valid` (and thus `is_bit_valid`) with a shared pointer when // `Self: !Immutable`. Since `Self: Immutable`, this panic condition will // not happen. - if !Wrapping::::is_bit_valid(c_ptr.forget_aligned()) { + let shared = unsafe { c_ptr.assume_aliasing::() }; + if !Wrapping::::is_bit_valid(shared.forget_aligned()) { return Err(ValidityError::new(source).into()); } diff --git a/src/macros.rs b/src/macros.rs index 6fe9a9e825..007e6d7a25 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -895,10 +895,7 @@ macro_rules! cryptocorrosion_derive_traits { $($field_ty: $crate::FromBytes,)* )? { - fn is_bit_valid(_c: $crate::Maybe<'_, Self, A>) -> bool - where - A: $crate::pointer::invariant::Reference - { + fn is_bit_valid(_c: $crate::Maybe<'_, Self>) -> bool { // SAFETY: This macro only accepts `#[repr(C)]` and // `#[repr(transparent)]` structs, and this `impl` block // requires all field types to be `FromBytes`. Thus, all @@ -1038,10 +1035,7 @@ macro_rules! cryptocorrosion_derive_traits { $field_ty: $crate::FromBytes, )* { - fn is_bit_valid(_c: $crate::Maybe<'_, Self, A>) -> bool - where - A: $crate::pointer::invariant::Reference - { + fn is_bit_valid(_c: $crate::Maybe<'_, Self>) -> bool { // SAFETY: This macro only accepts `#[repr(C)]` unions, and this // `impl` block requires all field types to be `FromBytes`. // Thus, all initialized byte sequences constitutes valid diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs index 6005bc78f7..ca9db26651 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -27,7 +27,7 @@ pub use { /// /// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unaligned> = - Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>; + Ptr<'a, crate::wrappers::ReadOnly, (Aliasing, Alignment, invariant::Initialized)>; /// Checks if the referent is zeroed. pub(crate) fn is_zeroed(ptr: Ptr<'_, T, I>) -> bool diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index d40a1e8143..e922e076e1 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -532,7 +532,7 @@ mod _conversions { /// State transitions between invariants. mod _transitions { use super::*; - use crate::pointer::transmute::TryTransmuteFromPtr; + use crate::{pointer::transmute::TryTransmuteFromPtr, ReadOnly}; impl<'a, T, I> Ptr<'a, T, I> where @@ -807,10 +807,12 @@ mod _transitions { I::Aliasing: Reference, I: Invariants, { + let ro: Ptr<'_, ReadOnly, _> = self.reborrow().transmute(); + let shared = unsafe { ro.assume_aliasing::() }; // This call may panic. If that happens, it doesn't cause any soundness // issues, as we have not generated any invalid state which we need to // fix before returning. - if T::is_bit_valid(self.reborrow().forget_aligned()) { + if T::is_bit_valid(shared) { // SAFETY: If `T::is_bit_valid`, code may assume that `self` // contains a bit-valid instance of `T`. By `T: // TryTransmuteFromPtr`, so diff --git a/src/pointer/transmute.rs b/src/pointer/transmute.rs index 1fa0540526..47499dea35 100644 --- a/src/pointer/transmute.rs +++ b/src/pointer/transmute.rs @@ -231,7 +231,6 @@ macro_rules! unsafe_impl_invariants_eq { } impl_transitive_transmute_from!(T => MaybeUninit => T => Wrapping); -impl_transitive_transmute_from!(T => Wrapping => T => MaybeUninit); // SAFETY: `ManuallyDrop` has the same size and bit validity as `T` [1], and // implements `Deref` [2]. Thus, it is already possible for safe @@ -445,7 +444,6 @@ const _: () = unsafe { unsafe_impl_for_transparent_wrapper!(T: ?Sized => UnsafeC const _: () = unsafe { unsafe_impl_for_transparent_wrapper!(T: ?Sized => Cell) }; impl_transitive_transmute_from!(T: ?Sized => Cell => T => UnsafeCell); -impl_transitive_transmute_from!(T: ?Sized => UnsafeCell => T => Cell); // SAFETY: `MaybeUninit` has no validity requirements. Currently this is not // explicitly guaranteed, but it's obvious from `MaybeUninit`'s documentation diff --git a/src/util/macro_util.rs b/src/util/macro_util.rs index 5565d3bd11..69f478bb65 100644 --- a/src/util/macro_util.rs +++ b/src/util/macro_util.rs @@ -29,10 +29,11 @@ use core::{ use crate::{ pointer::{ - invariant::{self, BecauseExclusive, BecauseImmutable, Invariants}, + invariant::{self, BecauseExclusive, BecauseImmutable, Exclusive, Invariants}, BecauseInvariantsEq, InvariantsEq, SizeEq, TryTransmuteFromPtr, }, - FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout, Ptr, TryFromBytes, ValidityError, + FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout, Ptr, ReadOnly, TryFromBytes, + ValidityError, }; /// Projects the type of the field at `Index` in `Self`. @@ -587,19 +588,22 @@ where // initialized. let ptr = unsafe { ptr.assume_validity::() }; - // SAFETY: `MaybeUninit` and `T` have the same size [1], so this cast - // preserves the referent's size. This cast preserves provenance. + // SAFETY: `MaybeUninit` and `T` have the same size [1], and + // `ReadOnly` and `T` have the same size, so this cast preserves the + // referent's size. This cast preserves provenance. // // [1] Per https://doc.rust-lang.org/1.81.0/std/mem/union.MaybeUninit.html#layout-1: // // `MaybeUninit` is guaranteed to have the same size, alignment, and // ABI as `T` - let ptr: Ptr<'_, Dst, _> = unsafe { - ptr.cast_unsized(|ptr: crate::pointer::PtrInner<'_, mem::MaybeUninit>| { - ptr.cast_sized() - }) + let ptr: Ptr<'_, ReadOnly, (Exclusive, _, _)> = unsafe { + ptr.cast_unsized::<_, _, (_, (BecauseExclusive, _))>( + |ptr: crate::pointer::PtrInner<'_, mem::MaybeUninit>| ptr.cast_sized(), + ) }; + // TODO: Replace this with `ptr.into_shared()` or something. + let ptr = unsafe { ptr.assume_aliasing() }; if Dst::is_bit_valid(ptr.forget_aligned()) { // SAFETY: Since `Dst::is_bit_valid`, we know that `ptr`'s referent is // bit-valid for `Dst`. `ptr` points to `mu_dst`, and no intervening diff --git a/src/util/macros.rs b/src/util/macros.rs index fa5b8d3b05..947491da3a 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -135,7 +135,7 @@ macro_rules! unsafe_impl { fn only_derive_is_allowed_to_implement_this_trait() {} #[inline] - fn is_bit_valid($candidate: Maybe<'_, Self, AA>) -> bool { + fn is_bit_valid($candidate: Maybe<'_, Self>) -> bool { $is_bit_valid } }; @@ -143,7 +143,7 @@ macro_rules! unsafe_impl { #[allow(clippy::missing_inline_in_public_items)] #[cfg_attr(all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), coverage(off))] fn only_derive_is_allowed_to_implement_this_trait() {} - #[inline(always)] fn is_bit_valid(_: Maybe<'_, Self, AA>) -> bool { true } + #[inline(always)] fn is_bit_valid(_: Maybe<'_, Self>) -> bool { true } }; (@method $trait:ident) => { #[allow(clippy::missing_inline_in_public_items, dead_code)] @@ -218,9 +218,8 @@ macro_rules! impl_for_transmute_from { TryFromBytes for $ty:ty [UnsafeCell<$repr:ty>] ) => { #[inline] - fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool { - let c: Maybe<'_, Self, crate::pointer::invariant::Exclusive> = candidate.into_exclusive_or_pme(); - let c: Maybe<'_, $repr, _> = c.transmute::<_, _, (_, (_, (BecauseExclusive, BecauseExclusive)))>(); + fn is_bit_valid(candidate: Maybe<'_, Self>) -> bool { + let c: Maybe<'_, $repr, _> = candidate.transmute::<_, _, (_, (_, (BecauseImmutable, BecauseImmutable)))>(); // SAFETY: This macro ensures that `$repr` and `Self` have the same // size and bit validity. Thus, a bit-valid instance of `$repr` is // also a bit-valid instance of `Self`. @@ -233,11 +232,11 @@ macro_rules! impl_for_transmute_from { TryFromBytes for $ty:ty [<$repr:ty>] ) => { #[inline] - fn is_bit_valid(candidate: $crate::Maybe<'_, Self, A>) -> bool { + fn is_bit_valid(candidate: $crate::Maybe<'_, Self>) -> bool { // SAFETY: This macro ensures that `$repr` and `Self` have the same // size and bit validity. Thus, a bit-valid instance of `$repr` is // also a bit-valid instance of `Self`. - <$repr as TryFromBytes>::is_bit_valid(candidate.transmute()) + <$repr as TryFromBytes>::is_bit_valid(candidate.transmute::<_, _, BecauseImmutable>()) } }; ( @@ -755,6 +754,10 @@ macro_rules! unsafe_impl_for_transparent_wrapper { } macro_rules! impl_transitive_transmute_from { + ($($tyvar:ident $(: ?$optbound:ident)?)? => $t:ty => $u:ty => $v:ty => $w:ty) => { + impl_transitive_transmute_from!($($tyvar $(: ?$optbound)?)? => $t => $u => $v); + impl_transitive_transmute_from!($($tyvar $(: ?$optbound)?)? => $t => $v => $w); + }; ($($tyvar:ident $(: ?$optbound:ident)?)? => $t:ty => $u:ty => $v:ty) => { const _: () = { use crate::pointer::{TransmuteFrom, PtrInner, SizeEq, invariant::Valid}; @@ -773,6 +776,20 @@ macro_rules! impl_transitive_transmute_from { } } + // SAFETY: Since `$u: SizeEq<$t>` and `$v: SizeEq`, this impl is + // transitively sound. + unsafe impl<$($tyvar $(: ?$optbound)?)?> SizeEq<$v> for $t + where + $v: SizeEq<$t>, + $u: SizeEq<$v>, + { + #[inline(always)] + fn cast_from_raw(v: PtrInner<'_, $v>) -> PtrInner<'_, $t> { + let u = <$u as SizeEq<_>>::cast_from_raw(v); + <$t as SizeEq<_>>::cast_from_raw(u) + } + } + // SAFETY: Since `$u: TransmuteFrom<$t, Valid, Valid>`, it is sound // to transmute a bit-valid `$t` to a bit-valid `$u`. Since `$v: // TransmuteFrom<$u, Valid, Valid>`, it is sound to transmute that @@ -782,15 +799,25 @@ macro_rules! impl_transitive_transmute_from { $u: TransmuteFrom<$t, Valid, Valid>, $v: TransmuteFrom<$u, Valid, Valid>, {} + + // SAFETY: Since `$u: TransmuteFrom<$t, Valid, Valid>`, it is sound + // to transmute a bit-valid `$t` to a bit-valid `$u`. Since `$v: + // TransmuteFrom<$u, Valid, Valid>`, it is sound to transmute that + // bit-valid `$u` to a bit-valid `$v`. + unsafe impl<$($tyvar $(: ?$optbound)?)?> TransmuteFrom<$v, Valid, Valid> for $t + where + $v: TransmuteFrom<$t, Valid, Valid>, + $u: TransmuteFrom<$v, Valid, Valid>, + {} }; }; } #[rustfmt::skip] macro_rules! impl_size_eq { - ($t:ty, $u:ty) => { - const _: () = { - use crate::{KnownLayout, pointer::{PtrInner, SizeEq}}; + ($t:ty, $u:ty) => { + const _: () = { + use crate::{KnownLayout, layout::{SizeInfo, TrailingSliceLayout}}; static_assert!(=> { let t = <$t as KnownLayout>::LAYOUT; @@ -803,29 +830,52 @@ macro_rules! impl_size_eq { ) => t_offset == u_offset && t_elem_size == u_elem_size, _ => false, } - }); + }) + }; - // SAFETY: See inline. - unsafe impl SizeEq<$t> for $u { - #[inline(always)] - fn cast_from_raw(t: PtrInner<'_, $t>) -> PtrInner<'_, $u> { - // SAFETY: We've asserted that their - // `KnownLayout::LAYOUT.size_info`s are equal, and so this - // cast is guaranteed to preserve address and referent size. - // It trivially preserves provenance. - unsafe { cast!(t) } - } + const _: () = unsafe { impl_size_eq!(@inner [] [] $t, $u); }; + }; + ( + $tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )? => + $t:ty, $u:ty + ) => { + impl_size_eq!(@inner [] [$tyvar $(: $(? $optbound)* $($bound)* )?] $t, $u); + }; + ( + const $constname:ident : $constty:ident $(,)? + $tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )? => + $t:ty, $u:ty + ) => { + impl_size_eq!(@inner [const $constname: $constty] [$tyvar $(: $(? $optbound)* $($bound)* )?] $t, $u); + }; + ( + @inner [$(const $constname:ident : $constty:ident)?] [$($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?)?] + $t:ty, $u:ty + ) => {{ + $crate::util::macros::__unsafe(); + + use crate::pointer::{PtrInner, SizeEq}; + + // SAFETY: See inline. + unsafe impl<$(const $constname: $constty,)? $($tyvar $(: $(? $optbound +)* $($bound +)*)?)?> SizeEq<$t> for $u { + #[inline(always)] + fn cast_from_raw(t: PtrInner<'_, $t>) -> PtrInner<'_, $u> { + // SAFETY: We've asserted that their + // `KnownLayout::LAYOUT.size_info`s are equal, and so this + // cast is guaranteed to preserve address and referent size. + // It trivially preserves provenance. + unsafe { cast!(t) } } - // SAFETY: See previous safety comment. - unsafe impl SizeEq<$u> for $t { - #[inline(always)] - fn cast_from_raw(u: PtrInner<'_, $u>) -> PtrInner<'_, $t> { - // SAFETY: See previous safety comment. - unsafe { cast!(u) } - } + } + // SAFETY: See previous safety comment. + unsafe impl<$(const $constname: $constty,)? $($tyvar $(: $(? $optbound +)* $($bound +)*)?)?> SizeEq<$u> for $t { + #[inline(always)] + fn cast_from_raw(u: PtrInner<'_, $u>) -> PtrInner<'_, $t> { + // SAFETY: See previous safety comment. + unsafe { cast!(u) } } - }; - }; + } + }}; } /// Invokes `$blk` in a context in which `$src<$t>` and `$dst<$u>` implement @@ -930,6 +980,9 @@ macro_rules! unsafe_with_size_eq { // so permits interior mutation exactly when `T` does. unsafe_impl!(T: ?Sized + Immutable => Immutable for $dst); + impl_transitive_transmute_from!(T: ?Sized => ReadOnly => T => S => ReadOnly>); + impl_transitive_transmute_from!(U: ?Sized => ReadOnly => U => D => ReadOnly>); + $blk }}; } diff --git a/src/wrappers.rs b/src/wrappers.rs index 6d7891354a..096a9fdfc3 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -9,6 +9,10 @@ use core::{fmt, hash::Hash}; use super::*; +use crate::pointer::{ + invariant::{Valid, Validity}, + InvariantsEq, MutationCompatible, SizeEq, TransmuteFrom, +}; /// A type with no alignment requirement. /// @@ -134,7 +138,10 @@ const _: () = unsafe { impl_or_verify!(T: Immutable => Immutable for Unalign); impl_or_verify!( T: TryFromBytes => TryFromBytes for Unalign; - |c| T::is_bit_valid(c.transmute()) + |c| { + impl_transitive_transmute_from!(T => ReadOnly> => Unalign => T => ReadOnly); + T::is_bit_valid(c.transmute::<_, _, BecauseImmutable>()) + } ); impl_or_verify!(T: FromZeros => FromZeros for Unalign); impl_or_verify!(T: FromBytes => FromBytes for Unalign); @@ -580,6 +587,81 @@ impl fmt::Debug for MaybeUninit { } } +/// TODO +#[cfg_attr(any(feature = "derive", test), derive(FromBytes, IntoBytes, Unaligned))] +#[repr(transparent)] +pub struct ReadOnly(T); + +const _: () = unsafe { + unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] ReadOnly); +}; + +// SAFETY: +// - `ReadOnly` has the same alignment as `T`, and so it is `Unaligned` +// exactly when `T` is as well. +// - `ReadOnly` has the same bit validity as `T`, and so it is `FromZeros`, +// `FromBytes`, or `IntoBytes` exactly when `T` is as well. +// - `TryFromBytes`: `ReadOnly` has the same the same bit validity as `T`, so +// `T::is_bit_valid` is a sound implementation of `is_bit_valid`. +#[allow(unused_unsafe)] // Unused when `feature = "derive"`. +const _: () = unsafe { + impl_or_verify!(T: ?Sized + Unaligned => Unaligned for ReadOnly); + impl_or_verify!( + T: ?Sized + TryFromBytes => TryFromBytes for ReadOnly; + |c| T::is_bit_valid(c.transmute::<_, _, BecauseImmutable>()) + ); + impl_or_verify!(T: ?Sized + FromZeros => FromZeros for ReadOnly); + impl_or_verify!(T: ?Sized + FromBytes => FromBytes for ReadOnly); + impl_or_verify!(T: ?Sized + IntoBytes => IntoBytes for ReadOnly); +}; + +const _: () = unsafe { + unsafe_impl!(T: ?Sized => Immutable for ReadOnly); +}; + +const _: () = unsafe { impl_size_eq!(T: ?Sized => ReadOnly, T) }; + +unsafe impl TransmuteFrom for ReadOnly {} +unsafe impl TransmuteFrom, Valid, Valid> for T {} + +impl ReadOnly { + /// TODO + #[inline(always)] + pub const fn new(t: T) -> ReadOnly { + ReadOnly(t) + } +} + +impl Deref for ReadOnly { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for ReadOnly { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<'a, T: ?Sized + Immutable> From<&'a T> for &'a ReadOnly { + #[inline(always)] + fn from(t: &'a T) -> &'a ReadOnly { + todo!() + } +} + +impl Debug for ReadOnly { + #[inline(always)] + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.deref().fmt(f) + } +} + #[cfg(test)] mod tests { use core::panic::AssertUnwindSafe; diff --git a/zerocopy-derive/src/enum.rs b/zerocopy-derive/src/enum.rs index 50e632d383..f8e6ec1f72 100644 --- a/zerocopy-derive/src/enum.rs +++ b/zerocopy-derive/src/enum.rs @@ -263,8 +263,8 @@ pub(crate) fn derive_is_bit_valid( // original type. let variant = unsafe { variants.cast_unsized_unchecked( - |p: #zerocopy_crate::pointer::PtrInner<'_, ___ZerocopyVariants #ty_generics>| { - p.cast_sized::<#variant_struct_ident #ty_generics>() + |p: #zerocopy_crate::pointer::PtrInner<'_, ReadOnly<___ZerocopyVariants #ty_generics>>| { + p.cast_sized::>() } ) }; @@ -285,13 +285,11 @@ pub(crate) fn derive_is_bit_valid( // enum's tag corresponds to one of the enum's discriminants. Then, we // check the bit validity of each field of the corresponding variant. // Thus, this is a sound implementation of `is_bit_valid`. - fn is_bit_valid<___ZerocopyAliasing>( - mut candidate: #zerocopy_crate::Maybe<'_, Self, ___ZerocopyAliasing>, - ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool - where - ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference, - { + fn is_bit_valid( + mut candidate: #zerocopy_crate::Maybe<'_, Self>, + ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool { use #zerocopy_crate::util::macro_util::core_reexport; + use #zerocopy_crate::ReadOnly; #tag_enum @@ -324,7 +322,7 @@ pub(crate) fn derive_is_bit_valid( // - There are no `UnsafeCell`s in the tag because it is a // primitive integer. let tag_ptr = unsafe { - candidate.reborrow().cast_unsized_unchecked(|p: #zerocopy_crate::pointer::PtrInner<'_, Self>| { + candidate.reborrow().cast_unsized_unchecked(|p: #zerocopy_crate::pointer::PtrInner<'_, ReadOnly>| { p.cast_sized::<___ZerocopyTagPrimitive>() }) }; @@ -346,8 +344,8 @@ pub(crate) fn derive_is_bit_valid( // original enum, and so preserves the locations of any // `UnsafeCell`s. let raw_enum = unsafe { - candidate.cast_unsized_unchecked(|p: #zerocopy_crate::pointer::PtrInner<'_, Self>| { - p.cast_sized::<___ZerocopyRawEnum #ty_generics>() + candidate.cast_unsized_unchecked(|p: #zerocopy_crate::pointer::PtrInner<'_, ReadOnly>| { + p.cast_sized::>() }) }; // SAFETY: `cast_unsized_unchecked` removes the initialization @@ -365,15 +363,20 @@ pub(crate) fn derive_is_bit_valid( // overall struct. let variants = unsafe { use #zerocopy_crate::pointer::PtrInner; - raw_enum.cast_unsized_unchecked(|p: PtrInner<'_, ___ZerocopyRawEnum #ty_generics>| { - let p = p.as_non_null().as_ptr(); - let ptr = core_reexport::ptr::addr_of_mut!((*p).variants); - // SAFETY: `ptr` is a projection into `p`, which is - // `NonNull`, and guaranteed not to wrap around the address - // space. Thus, `ptr` cannot be null. - let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(ptr) }; - unsafe { PtrInner::new(ptr) } - }) + raw_enum.cast_unsized_unchecked(| + p: PtrInner<'_, ReadOnly<___ZerocopyRawEnum #ty_generics>> + | -> PtrInner<'_, ReadOnly<___ZerocopyVariants #ty_generics>> { + let p = p.as_non_null().as_ptr(); + let p = p as *mut ___ZerocopyRawEnum #ty_generics; + let ptr = core_reexport::ptr::addr_of_mut!((*p).variants); + let ptr = ptr as *mut ReadOnly<_>; + // SAFETY: `ptr` is a projection into `p`, which is + // `NonNull`, and guaranteed not to wrap around the address + // space. Thus, `ptr` cannot be null. + let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(ptr) }; + unsafe { PtrInner::new(ptr) } + } + ) }; #[allow(non_upper_case_globals)] diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index a4c4c2dbf8..a280aa7cbd 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -746,14 +746,12 @@ fn derive_try_from_bytes_struct( // validity of a struct is just the composition of the bit // validities of its fields, so this is a sound implementation of // `is_bit_valid`. - fn is_bit_valid<___ZerocopyAliasing>( - mut candidate: #zerocopy_crate::Maybe, - ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool - where - ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference, - { + fn is_bit_valid( + mut candidate: #zerocopy_crate::Maybe, + ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool { use #zerocopy_crate::util::macro_util::core_reexport; use #zerocopy_crate::pointer::PtrInner; + use #zerocopy_crate::ReadOnly; true #(&& { // SAFETY: @@ -764,9 +762,11 @@ fn derive_try_from_bytes_struct( // the same byte ranges in the returned pointer's referent // as they do in `*slf` let field_candidate = unsafe { - let project = |slf: PtrInner<'_, Self>| { + let project = |slf: PtrInner<'_, ReadOnly>| -> PtrInner<'_, ReadOnly<#field_tys>> { let slf = slf.as_non_null().as_ptr(); + let slf = slf as *mut Self; let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names); + let field = field as *mut ReadOnly<_>; // SAFETY: `cast_unsized_unchecked` promises that // `slf` will either reference a zero-sized byte // range, or else will reference a byte range that @@ -829,14 +829,12 @@ fn derive_try_from_bytes_union( // bit validity of a union is not yet well defined in Rust, but it // is guaranteed to be no more strict than this definition. See #696 // for a more in-depth discussion. - fn is_bit_valid<___ZerocopyAliasing>( - mut candidate: #zerocopy_crate::Maybe<'_, Self,___ZerocopyAliasing> - ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool - where - ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference, - { + fn is_bit_valid( + mut candidate: #zerocopy_crate::Maybe<'_, Self> + ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool { use #zerocopy_crate::util::macro_util::core_reexport; use #zerocopy_crate::pointer::PtrInner; + use #zerocopy_crate::ReadOnly; false #(|| { // SAFETY: @@ -847,9 +845,11 @@ fn derive_try_from_bytes_union( // `self_type_trait_bounds`, neither `*slf` nor the // returned pointer's referent contain any `UnsafeCell`s let field_candidate = unsafe { - let project = |slf: PtrInner<'_, Self>| { + let project = |slf: PtrInner<'_, ReadOnly>| -> PtrInner<'_, ReadOnly<#field_tys>> { let slf = slf.as_non_null().as_ptr(); + let slf = slf as *mut Self; let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names); + let field = field as *mut ReadOnly<_>; // SAFETY: `cast_unsized_unchecked` promises that // `slf` will either reference a zero-sized byte // range, or else will reference a byte range that @@ -954,12 +954,9 @@ fn try_gen_trivial_is_bit_valid( if top_level == Trait::FromBytes && ast.generics.params.is_empty() { Some(quote!( // SAFETY: See inline. - fn is_bit_valid<___ZerocopyAliasing>( - _candidate: #zerocopy_crate::Maybe, - ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool - where - ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference, - { + fn is_bit_valid( + _candidate: #zerocopy_crate::Maybe, + ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool { if false { fn assert_is_from_bytes() where @@ -997,12 +994,9 @@ unsafe fn gen_trivial_is_bit_valid_unchecked(zerocopy_crate: &Path) -> proc_macr quote!( // SAFETY: The caller of `gen_trivial_is_bit_valid_unchecked` has // promised that all initialized bit patterns are valid for `Self`. - fn is_bit_valid<___ZerocopyAliasing>( - _candidate: #zerocopy_crate::Maybe, - ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool - where - ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference, - { + fn is_bit_valid( + _candidate: #zerocopy_crate::Maybe, + ) -> #zerocopy_crate::util::macro_util::core_reexport::primitive::bool { true } ) diff --git a/zerocopy-derive/tests/struct_try_from_bytes.rs b/zerocopy-derive/tests/struct_try_from_bytes.rs index 2224d38bc5..925aec37ec 100644 --- a/zerocopy-derive/tests/struct_try_from_bytes.rs +++ b/zerocopy-derive/tests/struct_try_from_bytes.rs @@ -18,7 +18,7 @@ include!("include.rs"); #[test] fn zst() { // FIXME(#5): Use `try_transmute` in this test once it's available. - let candidate = ::zerocopy::Ptr::from_ref(&()); + let candidate = ::zerocopy::Ptr::from_ref(&const { imp::ReadOnly::new(()) }); let candidate = candidate.forget_aligned(); // SAFETY: `&()` trivially consists entirely of initialized bytes. let candidate = unsafe { candidate.assume_initialized() }; @@ -37,7 +37,7 @@ util_assert_impl_all!(One: imp::TryFromBytes); #[test] fn one() { // FIXME(#5): Use `try_transmute` in this test once it's available. - let candidate = ::zerocopy::Ptr::from_ref(&One { a: 42 }); + let candidate = ::zerocopy::Ptr::from_ref(&const { imp::ReadOnly::new(One { a: 42 }) }); let candidate = candidate.forget_aligned(); // SAFETY: `&One` consists entirely of initialized bytes. let candidate = unsafe { candidate.assume_initialized() }; @@ -57,7 +57,8 @@ util_assert_impl_all!(Two: imp::TryFromBytes); #[test] fn two() { // FIXME(#5): Use `try_transmute` in this test once it's available. - let candidate = ::zerocopy::Ptr::from_ref(&Two { a: false, b: () }); + let candidate = + ::zerocopy::Ptr::from_ref(&const { imp::ReadOnly::new(Two { a: false, b: () }) }); let candidate = candidate.forget_aligned(); // SAFETY: `&Two` consists entirely of initialized bytes. let candidate = unsafe { candidate.assume_initialized() }; @@ -78,7 +79,7 @@ fn two_bad() { // the same bytes as `c`. // - The cast preserves provenance. // - Neither the input nor output types contain any `UnsafeCell`s. - let candidate = unsafe { candidate.cast_unsized_unchecked(|p| p.cast::()) }; + let candidate = unsafe { candidate.cast_unsized_unchecked(|p| p.cast::>()) }; // SAFETY: `candidate`'s referent is as-initialized as `Two`. let candidate = unsafe { candidate.assume_initialized() }; @@ -110,8 +111,9 @@ fn un_sized() { // - Neither the input nor output types contain any `UnsafeCell`s. let candidate = unsafe { candidate.cast_unsized_unchecked(|p| { - let ptr = - imp::core::ptr::NonNull::new_unchecked(p.as_non_null().as_ptr() as *mut Unsized); + let ptr = imp::core::ptr::NonNull::new_unchecked( + p.as_non_null().as_ptr() as *mut imp::ReadOnly + ); ::zerocopy::pointer::PtrInner::new(ptr) }) }; @@ -170,8 +172,9 @@ fn test_maybe_from_bytes() { // the same bytes as `c`. // - The cast preserves provenance. // - Neither the input nor output types contain any `UnsafeCell`s. - let candidate = - unsafe { candidate.cast_unsized_unchecked(|p| p.cast::>()) }; + let candidate = unsafe { + candidate.cast_unsized_unchecked(|p| p.cast::>>()) + }; // SAFETY: `[u8]` consists entirely of initialized bytes. let candidate = unsafe { candidate.assume_initialized() }; diff --git a/zerocopy-derive/tests/union_try_from_bytes.rs b/zerocopy-derive/tests/union_try_from_bytes.rs index 80bae235ba..0feb74f244 100644 --- a/zerocopy-derive/tests/union_try_from_bytes.rs +++ b/zerocopy-derive/tests/union_try_from_bytes.rs @@ -25,7 +25,7 @@ util_assert_impl_all!(One: imp::TryFromBytes); #[test] fn one() { // FIXME(#5): Use `try_transmute` in this test once it's available. - let candidate = ::zerocopy::Ptr::from_ref(&One { a: 42 }); + let candidate = ::zerocopy::Ptr::from_ref(&const { imp::ReadOnly::new(One { a: 42 }) }); let candidate = candidate.forget_aligned(); // SAFETY: `&One` consists entirely of initialized bytes. let candidate = unsafe { candidate.assume_initialized() }; @@ -45,14 +45,14 @@ util_assert_impl_all!(Two: imp::TryFromBytes); #[test] fn two() { // FIXME(#5): Use `try_transmute` in this test once it's available. - let candidate_a = ::zerocopy::Ptr::from_ref(&Two { a: false }); + let candidate_a = ::zerocopy::Ptr::from_ref(&const { imp::ReadOnly::new(Two { a: false }) }); let candidate_a = candidate_a.forget_aligned(); // SAFETY: `&Two` consists entirely of initialized bytes. let candidate_a = unsafe { candidate_a.assume_initialized() }; let is_bit_valid = ::is_bit_valid(candidate_a); assert!(is_bit_valid); - let candidate_b = ::zerocopy::Ptr::from_ref(&Two { b: true }); + let candidate_b = ::zerocopy::Ptr::from_ref(&const { imp::ReadOnly::new(Two { b: true }) }); let candidate_b = candidate_b.forget_aligned(); // SAFETY: `&Two` consists entirely of initialized bytes. let candidate_b = unsafe { candidate_b.assume_initialized() }; @@ -62,8 +62,10 @@ fn two() { #[test] fn two_bad() { + use imp::From; + // FIXME(#5): Use `try_transmute` in this test once it's available. - let candidate = ::zerocopy::Ptr::from_ref(&[2u8][..]); + let candidate = ::zerocopy::Ptr::from_ref(<&imp::ReadOnly<[u8]>>::from(&[2u8][..])); let candidate = candidate.forget_aligned(); // SAFETY: `&[u8]` consists entirely of initialized bytes. let candidate = unsafe { candidate.assume_initialized() }; @@ -73,7 +75,7 @@ fn two_bad() { // the same bytes as `c`. // - The cast preserves provenance. // - Neither the input nor output types contain any `UnsafeCell`s. - let candidate = unsafe { candidate.cast_unsized_unchecked(|p| p.cast::()) }; + let candidate = unsafe { candidate.cast_unsized_unchecked(|p| p.cast::>()) }; // SAFETY: `candidate`'s referent is as-initialized as `Two`. let candidate = unsafe { candidate.assume_initialized() }; @@ -102,7 +104,8 @@ fn bool_and_zst() { // the same bytes as `c`. // - The cast preserves provenance. // - Neither the input nor output types contain any `UnsafeCell`s. - let candidate = unsafe { candidate.cast_unsized_unchecked(|p| p.cast::()) }; + let candidate = + unsafe { candidate.cast_unsized_unchecked(|p| p.cast::>()) }; // SAFETY: `candidate`'s referent is fully initialized. let candidate = unsafe { candidate.assume_initialized() }; @@ -131,8 +134,9 @@ fn test_maybe_from_bytes() { // the same bytes as `c`. // - The cast preserves provenance. // - Neither the input nor output types contain any `UnsafeCell`s. - let candidate = - unsafe { candidate.cast_unsized_unchecked(|p| p.cast::>()) }; + let candidate = unsafe { + candidate.cast_unsized_unchecked(|p| p.cast::>>()) + }; // SAFETY: `[u8]` consists entirely of initialized bytes. let candidate = unsafe { candidate.assume_initialized() };