diff --git a/src/impls.rs b/src/impls.rs index c7d77326a8..8a48d209fb 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -112,7 +112,7 @@ const _: () = unsafe { *byte.unaligned_as_ref() < 2 }) }; -impl_size_eq!(bool, u8); +impl_size_eq!(=> bool, u8); // SAFETY: // - `Immutable`: `char` self-evidently does not contain any `UnsafeCell`s. @@ -144,7 +144,7 @@ const _: () = unsafe { }); }; -impl_size_eq!(char, Unalign); +impl_size_eq!(=> char, Unalign); // SAFETY: Per the Reference [1], `str` has the same layout as `[u8]`. // - `Immutable`: `[u8]` does not contain any `UnsafeCell`s. @@ -179,13 +179,13 @@ const _: () = unsafe { }) }; -impl_size_eq!(str, [u8]); +impl_size_eq!(=> str, [u8]); 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>); let n = n.transmute::, invariant::Valid, _>(); $nonzero::new(n.read_unaligned().into_inner()).is_some() @@ -403,13 +403,23 @@ const _: () = unsafe { mod atomics { use super::*; - macro_rules! impl_traits_for_atomics { - ($($atomics:ident [$primitives:ident]),* $(,)?) => { + macro_rules! impl_layout_traits_for_atomics { + ($($($tyvar:ident)? => $atomics:ty [$primitives:ty]),* $(,)?) => { + $( + impl_known_layout!($($tyvar => )? $atomics); + + impl_size_eq!($($tyvar)? => $atomics, $primitives); + impl_size_eq!($($tyvar)? => $atomics, UnsafeCell<$primitives>); + )* + }; + } + + macro_rules! impl_validity_traits_for_atomics { + ($($atomics:tt [$primitives:ty]),* $(,)?) => { $( - impl_known_layout!($atomics); - impl_for_transmute_from!(=> TryFromBytes for $atomics [UnsafeCell<$primitives>]); impl_for_transmute_from!(=> FromZeros for $atomics [UnsafeCell<$primitives>]); impl_for_transmute_from!(=> FromBytes for $atomics [UnsafeCell<$primitives>]); + impl_for_transmute_from!(=> TryFromBytes for $atomics [UnsafeCell<$primitives>]); impl_for_transmute_from!(=> IntoBytes for $atomics [UnsafeCell<$primitives>]); )* }; @@ -425,8 +435,7 @@ mod atomics { ($($($tyvar:ident)? => $atomic:ty [$prim:ty]),*) => {{ crate::util::macros::__unsafe(); - use core::cell::UnsafeCell; - use crate::pointer::{SizeEq, TransmuteFrom, invariant::Valid}; + use crate::pointer::{TransmuteFrom, invariant::Valid}; $( // SAFETY: The caller promised that `$atomic` and `$prim` have @@ -436,30 +445,18 @@ mod atomics { // the same size and bit validity. unsafe impl<$($tyvar)?> TransmuteFrom<$prim, Valid, Valid> for $atomic {} - // SAFETY: The caller promised that `$atomic` and `$prim` have - // the same size. - unsafe impl<$($tyvar)?> SizeEq<$atomic> for $prim { - type CastFrom = $crate::pointer::cast::CastSized; - } - // SAFETY: See previous safety comment. - unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic { - type CastFrom = $crate::pointer::cast::CastSized; - } - // SAFETY: The caller promised that `$atomic` and `$prim` have - // the same size. `UnsafeCell` has the same size as `T` [1]. - // - // [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout: - // - // `UnsafeCell` has the same in-memory representation as - // its inner type `T`. A consequence of this guarantee is that - // it is possible to convert between `T` and `UnsafeCell`. - unsafe impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> { - type CastFrom = $crate::pointer::cast::CastSized; - } - // SAFETY: See previous safety comment. - unsafe impl<$($tyvar)?> SizeEq> for $atomic { - type CastFrom = $crate::pointer::cast::CastSized; - } + // impl<$($tyvar)?> SizeEq<$atomic> for $prim { + // type CastFrom = $crate::pointer::cast::CastSizedExact; + // } + // impl<$($tyvar)?> SizeEq<$prim> for $atomic { + // type CastFrom = $crate::pointer::cast::CastSizedExact; + // } + // impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> { + // type CastFrom = $crate::pointer::cast::CastSizedExact; + // } + // impl<$($tyvar)?> SizeEq> for $atomic { + // type CastFrom = $crate::pointer::cast::CastSizedExact; + // } // SAFETY: The caller promised that `$atomic` and `$prim` have // the same bit validity. `UnsafeCell` has the same bit @@ -484,12 +481,11 @@ mod atomics { use super::*; - impl_traits_for_atomics!(AtomicU8[u8], AtomicI8[i8]); + impl_layout_traits_for_atomics!(=> AtomicBool [bool], => AtomicU8[u8], => AtomicI8[i8]); + impl_validity_traits_for_atomics!(AtomicU8[u8], AtomicI8[i8]); - impl_known_layout!(AtomicBool); - - impl_for_transmute_from!(=> TryFromBytes for AtomicBool [UnsafeCell]); impl_for_transmute_from!(=> FromZeros for AtomicBool [UnsafeCell]); + impl_for_transmute_from!(=> TryFromBytes for AtomicBool [UnsafeCell]); impl_for_transmute_from!(=> IntoBytes for AtomicBool [UnsafeCell]); // SAFETY: Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the @@ -551,7 +547,8 @@ mod atomics { use super::*; - impl_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]); + impl_layout_traits_for_atomics!(=> AtomicU16[u16], => AtomicI16[i16]); + impl_validity_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]); // SAFETY: `AtomicU16` and `AtomicI16` have the same size and bit // validity as `u16` and `i16` respectively [1][2]. @@ -578,7 +575,8 @@ mod atomics { use super::*; - impl_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]); + impl_layout_traits_for_atomics!(=> AtomicU32[u32], => AtomicI32[i32]); + impl_validity_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]); // SAFETY: `AtomicU32` and `AtomicI32` have the same size and bit // validity as `u32` and `i32` respectively [1][2]. @@ -605,7 +603,8 @@ mod atomics { use super::*; - impl_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]); + impl_layout_traits_for_atomics!(=> AtomicU64[u64], => AtomicI64[i64]); + impl_validity_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]); // SAFETY: `AtomicU64` and `AtomicI64` have the same size and bit // validity as `u64` and `i64` respectively [1][2]. @@ -632,9 +631,8 @@ mod atomics { use super::*; - impl_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]); - - impl_known_layout!(T => AtomicPtr); + impl_layout_traits_for_atomics!(T => AtomicPtr [*mut T], => AtomicUsize[usize], => AtomicIsize[isize]); + impl_validity_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]); // FIXME(#170): Implement `FromBytes` and `IntoBytes` once we implement // those traits for `*mut T`. @@ -864,28 +862,29 @@ 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 monomorphization 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()) + T::is_bit_valid(candidate.transmute()) + // // 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 monomorphization 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()) } } @@ -914,11 +913,15 @@ 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| { + let c: Ptr<'_, [ReadOnly; N], _> = c.cast::<_, crate::pointer::cast::CastSized, _>(); + let c: Ptr<'_, [ReadOnly], _> = c.as_slice(); + let c: Ptr<'_, ReadOnly<[T]>, _> = c.cast::<_, crate::pointer::cast::CastUnsized, _>(); + // 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()) + <[T] as TryFromBytes>::is_bit_valid(c) }); unsafe_impl!(const N: usize, T: FromZeros => FromZeros for [T; N]); unsafe_impl!(const N: usize, T: FromBytes => FromBytes for [T; N]); @@ -927,6 +930,8 @@ const _: () = unsafe { assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]); unsafe_impl!(T: Immutable => Immutable for [T]); unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; |c| { + let c: Ptr<'_, [ReadOnly], _> = c.cast::<_, crate::pointer::cast::CastUnsized, _>(); + // SAFETY: Per the reference [1]: // // An array of `[T; N]` has a size of `size_of::() * N` and the @@ -1158,6 +1163,7 @@ mod tests { use super::*; use crate::pointer::invariant; + #[cfg(never)] #[test] fn test_impls() { // A type that can supply test cases for testing diff --git a/src/layout.rs b/src/layout.rs index 84f4ab79a4..6da55b8830 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -756,6 +756,14 @@ mod cast_from { { } + // SAFETY: TODO + unsafe impl crate::pointer::cast::CastExact for CastFrom + where + Src: KnownLayout + ?Sized, + Dst: KnownLayout + ?Sized, + { + } + // SAFETY: `project` produces a pointer which refers to the same referent // bytes as its input, or to a subset of them (see inline comments for a // more detailed proof of this). It does this using provenance-preserving diff --git a/src/lib.rs b/src/lib.rs index 4f42963219..3640be94a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2974,7 +2974,7 @@ 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.cast::<_, crate::pointer::cast::CastSized, _>(); // Since we don't have `T: KnownLayout`, we hack around that by using // `Wrapping`, which implements `KnownLayout` even if `T` doesn't. @@ -4390,9 +4390,7 @@ pub unsafe trait FromBytes: FromZeros { let source = Ptr::from_mut(source); let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count)); match maybe_slf { - Ok(slf) => Ok(slf - .recall_validity::<_, (_, (_, (BecauseExclusive, BecauseExclusive)))>() - .as_mut()), + Ok(slf) => Ok(slf.recall_validity::<_, (_, (_, BecauseExclusive))>().as_mut()), Err(err) => Err(err.map_src(|s| s.as_mut())), } } diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs index a7051f97fd..b5b2d386e2 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -22,12 +22,14 @@ pub use { ptr::Ptr, }; +use crate::wrappers::ReadOnly; + /// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument /// to [`TryFromBytes::is_bit_valid`]. /// /// [`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, ReadOnly, (Aliasing, Alignment, invariant::Initialized)>; /// Checks if the referent is zeroed. pub(crate) fn is_zeroed(ptr: Ptr<'_, T, I>) -> bool @@ -41,7 +43,11 @@ where #[doc(hidden)] pub mod cast { - use core::{marker::PhantomData, mem}; + use core::{ + marker::PhantomData, + mem::{self, MaybeUninit}, + num::Wrapping, + }; use crate::{ layout::{SizeInfo, TrailingSliceLayout}, @@ -72,6 +78,13 @@ pub mod cast { /// shrink the set of referent bytes, and it may change the referent's type. pub unsafe trait Cast: Project {} + /// A [`Cast`] which does not shrink the set of referent bytes. + /// + /// # Safety + /// + /// A `CastExact` projection must preserve the set of referent bytes. + pub unsafe trait CastExact: Cast {} + /// A no-op pointer cast. #[derive(Default, Copy, Clone)] #[allow(missing_debug_implementations)] @@ -90,6 +103,9 @@ pub mod cast { // SAFETY: The `Project::project` impl preserves referent address. unsafe impl Cast for IdCast {} + // SAFETY: The `Project::project` impl preserves referent size. + unsafe impl CastExact for IdCast {} + /// A pointer cast which preserves or shrinks the set of referent bytes of /// a statically-sized referent. /// @@ -116,6 +132,36 @@ pub mod cast { // SAFETY: The `Project::project` impl preserves referent address. unsafe impl Cast for CastSized {} + /// A pointer cast which preserves or shrinks the set of referent bytes of + /// a statically-sized referent. + /// + /// # Safety + /// + /// The implementation of [`Project`] uses a compile-time assertion to + /// guarantee that `Dst` is no larger than `Src`. Thus, `CastSized` has a + /// sound implementation of [`Project`] for all `Src` and `Dst` – the caller + /// may pass any `Src` and `Dst` without being responsible for soundness. + #[allow(missing_debug_implementations, missing_copy_implementations)] + pub enum CastSizedExact {} + + // SAFETY: By the `static_assert!`, `Dst` is no larger than `Src`, + // and so all casts preserve or shrink the set of referent bytes. All + // operations preserve provenance. + unsafe impl Project for CastSizedExact { + #[inline(always)] + fn project(src: PtrInner<'_, Src>) -> *mut Dst { + static_assert!(Src, Dst => mem::size_of::() == mem::size_of::()); + src.as_ptr().cast::() + } + } + + // SAFETY: The `Project::project` impl preserves referent address. + unsafe impl Cast for CastSizedExact {} + + // SAFETY: By the `static_assert!`, `Project::project` impl preserves + // referent size. + unsafe impl CastExact for CastSizedExact {} + /// A pointer cast which preserves or shrinks the set of referent bytes of /// a dynamically-sized referent. /// @@ -143,7 +189,8 @@ pub mod cast { // - Is the alignment check necessary for soundness? It's not // necessary for the soundness of the `Project` impl, but what // about the soundness of particular use sites? - // - Do we want this to support shrinking casts as well? + // - Do we want this to support shrinking casts as well? If so, + // we'll need to remove the `CastExact` impl. static_assert!(Src: ?Sized + KnownLayout, Dst: ?Sized + KnownLayout => { let t = ::LAYOUT; let u = ::LAYOUT; @@ -170,6 +217,14 @@ pub mod cast { { } + // SAFETY: TODO + unsafe impl CastExact for CastUnsized + where + Src: ?Sized + KnownLayout, + Dst: ?Sized + KnownLayout, + { + } + /// A field projection /// /// A `Projection` is a [`Project`] which implements projection by @@ -193,6 +248,111 @@ pub mod cast { } } + /// TODO + /// + /// # Safety + /// + /// TODO + pub unsafe trait Wrapped { + type Unwrapped: ?Sized; + type CastToUnwrapped: CastExact; + type CastFromUnwrapped: CastExact; + } + + /// TODO + /// + /// # Safety + /// + /// TODO + pub unsafe trait HasWrappedField: Wrapped { + type WrappedField: ?Sized + Wrapped; + } + + // SAFETY: TODO + unsafe impl Wrapped for MaybeUninit { + type Unwrapped = T; + type CastToUnwrapped = CastSizedExact; + type CastFromUnwrapped = CastSizedExact; + } + + // SAFETY: TODO + unsafe impl HasWrappedField for MaybeUninit { + type WrappedField = MaybeUninit; + } + + // SAFETY: TODO + unsafe impl Wrapped for Wrapping { + type Unwrapped = T; + type CastToUnwrapped = CastSizedExact; + type CastFromUnwrapped = CastSizedExact; + } + + // SAFETY: TODO + unsafe impl HasWrappedField for Wrapping { + type WrappedField = Wrapping; + } + + #[allow(missing_debug_implementations, missing_copy_implementations)] + pub struct WrappedProjection { + _never: core::convert::Infallible, + _phantom: PhantomData<(F, W)>, + } + + // SAFETY: TODO + unsafe impl + Project for WrappedProjection + where + W: Wrapped + + HasWrappedField<<::Unwrapped as HasField>::Type>, + W::Unwrapped: HasField, + { + #[inline(always)] + fn project(src: PtrInner<'_, W>) -> *mut W::WrappedField { + src.project::<_, W::CastToUnwrapped>() + .project::<_, Projection>() + .project::<_, ::CastFromUnwrapped>() + .as_ptr() + } + } + + #[allow(missing_debug_implementations, missing_copy_implementations)] + pub struct TransposeProjection { + _never: core::convert::Infallible, + _phantom_w: PhantomData, + _phantom_v: PhantomData, + } + + /* + pub unsafe trait Wrapped { + type Unwrapped: ?Sized; + type CastToUnwrapped: CastExact; + type CastFromUnwrapped: CastExact; + } + + pub unsafe trait HasWrappedField: Wrapped { + type WrappedField: ?Sized + Wrapped; + } + */ + + // SAFETY: TODO + unsafe impl Project< + W, + >::WrappedField>>::WrappedField + > for TransposeProjection + where + W: Wrapped + HasWrappedField, + V: Wrapped + HasWrappedField<::Unwrapped>>::WrappedField>, + { + #[inline(always)] + fn project(src: PtrInner<'_, W>) -> *mut >::WrappedField>>::WrappedField { + src.project::<_, W::CastToUnwrapped>() + .project::<_, V::CastToUnwrapped>() + .project::<_, <>::WrappedField as Wrapped>::CastFromUnwrapped>() + .project::<_, <>::WrappedField>>::WrappedField as Wrapped>::CastFromUnwrapped>() + .as_ptr() + } + } + /// A transitive sequence of projections. /// /// Given `TU: Project` and `UV: Project`, `TransitiveProject<_, TU, UV>` is @@ -239,6 +399,19 @@ pub mod cast { { } + // SAFETY: Since the `Project::project` impl delegates to `TU::project` and + // `UV::project`, and since `TU` and `UV` are `CastExact`, the `Project::project` + // impl preserves the set of referent bytes. + unsafe impl CastExact for TransitiveProject + where + T: ?Sized, + U: ?Sized, + V: ?Sized, + TU: CastExact, + UV: CastExact, + { + } + /// A cast from `T` to `[u8]`. pub(crate) struct AsBytesCast; @@ -268,4 +441,7 @@ pub mod cast { // SAFETY: The `Project::project` impl preserves referent address. unsafe impl Cast for AsBytesCast {} + + // SAFETY: The `Project::project` impl preserves the set of referent bytes. + unsafe impl CastExact for AsBytesCast {} } diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 21b733fc87..9f08efcfd5 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -521,6 +521,24 @@ mod _conversions { } } + // /// `Ptr<'a, T, (_, _, _)>` → `Ptr<'a, ReadOnly, (_, _, _)>` + // impl<'a, T, I> Ptr<'a, T, I> + // where + // T: ?Sized, + // I: Invariants, + // { + // /// TODO + // pub(crate) fn into_read_only( + // self, + // ) -> Ptr<'a, crate::ReadOnly, (I::Aliasing, I::Alignment, I::Validity)> + // where + // T: Read, + // { + // let ro = self.transmute::<_, _, (_, _)>(); + // unsafe { ro.assume_alignment() } + // } + // } + impl<'a, T, I> Ptr<'a, T, I> where T: ?Sized, @@ -562,42 +580,42 @@ mod _transitions { T: 'a + ?Sized, I: Invariants, { - /// Returns a `Ptr` with [`Exclusive`] aliasing if `self` already has - /// `Exclusive` aliasing, or generates a compile-time assertion failure. - /// - /// This allows code which is generic over aliasing to down-cast to a - /// concrete aliasing. - /// - /// [`Exclusive`]: crate::pointer::invariant::Exclusive - #[inline] - pub(crate) fn into_exclusive_or_pme( - self, - ) -> Ptr<'a, T, (Exclusive, I::Alignment, I::Validity)> { - // NOTE(https://github.com/rust-lang/rust/issues/131625): We do this - // rather than just having `Aliasing::IS_EXCLUSIVE` have the panic - // behavior because doing it that way causes rustdoc to fail while - // attempting to document hidden items (since it evaluates the - // constant - and thus panics). - trait AliasingExt: Aliasing { - const IS_EXCL: bool; - } - - impl AliasingExt for A { - const IS_EXCL: bool = { - const_assert!(Self::IS_EXCLUSIVE); - true - }; - } - - assert!(I::Aliasing::IS_EXCL); - - // SAFETY: We've confirmed that `self` already has the aliasing - // `Exclusive`. If it didn't, either the preceding assert would fail - // or evaluating `I::Aliasing::IS_EXCL` would fail. We're *pretty* - // sure that it's guaranteed to fail const eval, but the `assert!` - // provides a backstop in case that doesn't work. - unsafe { self.assume_exclusive() } - } + // /// Returns a `Ptr` with [`Exclusive`] aliasing if `self` already has + // /// `Exclusive` aliasing, or generates a compile-time assertion failure. + // /// + // /// This allows code which is generic over aliasing to down-cast to a + // /// concrete aliasing. + // /// + // /// [`Exclusive`]: crate::pointer::invariant::Exclusive + // #[inline] + // pub(crate) fn into_exclusive_or_pme( + // self, + // ) -> Ptr<'a, T, (Exclusive, I::Alignment, I::Validity)> { + // // NOTE(https://github.com/rust-lang/rust/issues/131625): We do this + // // rather than just having `Aliasing::IS_EXCLUSIVE` have the panic + // // behavior because doing it that way causes rustdoc to fail while + // // attempting to document hidden items (since it evaluates the + // // constant - and thus panics). + // trait AliasingExt: Aliasing { + // const IS_EXCL: bool; + // } + + // impl AliasingExt for A { + // const IS_EXCL: bool = { + // const_assert!(Self::IS_EXCLUSIVE); + // true + // }; + // } + + // assert!(I::Aliasing::IS_EXCL); + + // // SAFETY: We've confirmed that `self` already has the aliasing + // // `Exclusive`. If it didn't, either the preceding assert would fail + // // or evaluating `I::Aliasing::IS_EXCL` would fail. We're *pretty* + // // sure that it's guaranteed to fail const eval, but the `assert!` + // // provides a backstop in case that doesn't work. + // unsafe { self.assume_exclusive() } + // } /// Assumes that `self` satisfies the invariants `H`. /// @@ -623,37 +641,37 @@ mod _transitions { unsafe { self.assume_invariants::() } } - /// Assumes that `self` satisfies the aliasing requirement of `A`. - /// - /// # Safety - /// - /// The caller promises that `self` satisfies the aliasing requirement - /// of `A`. - #[inline] - pub(crate) unsafe fn assume_aliasing( - self, - ) -> Ptr<'a, T, (A, I::Alignment, I::Validity)> { - // SAFETY: The caller promises that `self` satisfies the aliasing - // requirements of `A`. - unsafe { self.assume_invariants() } - } - - /// Assumes `self` satisfies the aliasing requirement of [`Exclusive`]. - /// - /// # Safety - /// - /// The caller promises that `self` satisfies the aliasing requirement - /// of `Exclusive`. - /// - /// [`Exclusive`]: crate::pointer::invariant::Exclusive - #[inline] - pub(crate) unsafe fn assume_exclusive( - self, - ) -> Ptr<'a, T, (Exclusive, I::Alignment, I::Validity)> { - // SAFETY: The caller promises that `self` satisfies the aliasing - // requirements of `Exclusive`. - unsafe { self.assume_aliasing::() } - } + // /// Assumes that `self` satisfies the aliasing requirement of `A`. + // /// + // /// # Safety + // /// + // /// The caller promises that `self` satisfies the aliasing requirement + // /// of `A`. + // #[inline] + // pub(crate) unsafe fn assume_aliasing( + // self, + // ) -> Ptr<'a, T, (A, I::Alignment, I::Validity)> { + // // SAFETY: The caller promises that `self` satisfies the aliasing + // // requirements of `A`. + // unsafe { self.assume_invariants() } + // } + + // /// Assumes `self` satisfies the aliasing requirement of [`Exclusive`]. + // /// + // /// # Safety + // /// + // /// The caller promises that `self` satisfies the aliasing requirement + // /// of `Exclusive`. + // /// + // /// [`Exclusive`]: crate::pointer::invariant::Exclusive + // #[inline] + // pub(crate) unsafe fn assume_exclusive( + // self, + // ) -> Ptr<'a, T, (Exclusive, I::Alignment, I::Validity)> { + // // SAFETY: The caller promises that `self` satisfies the aliasing + // // requirements of `Exclusive`. + // unsafe { self.assume_aliasing::() } + // } /// Assumes that `self`'s referent is validly-aligned for `T` if /// required by `A`. @@ -820,20 +838,31 @@ mod _transitions { /// On error, unsafe code may rely on this method's returned /// `ValidityError` containing `self`. #[inline] - pub(crate) fn try_into_valid( + pub(crate) fn try_into_valid( mut self, ) -> Result, ValidityError> where T: TryFromBytes + Read - + TryTransmuteFromPtr, + + TryTransmuteFromPtr + + MutationCompatible< + crate::wrappers::ReadOnly, + I::Aliasing, + Initialized, + Initialized, + TT, + >, + // T: MutationCompatible, I::Aliasing: Reference, I: Invariants, { // 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( + self.reborrow() + .cast::<_, as SizeEq>::CastFrom, _>(), + ) { // SAFETY: If `T::is_bit_valid`, code may assume that `self` // contains a bit-valid instance of `T`. By `T: // TryTransmuteFromPtr`, so @@ -966,6 +995,9 @@ mod _casts { T: 'a + KnownLayout + ?Sized, I: Invariants, { + // TODO: Is there any way to teach Rust that, for all `T, A, R`, `T: + // Read` implies `[u8]: Read`? + /// Casts this pointer-to-initialized into a pointer-to-bytes. #[allow(clippy::wrong_self_convention)] #[must_use] @@ -973,6 +1005,7 @@ mod _casts { pub fn as_bytes(self) -> Ptr<'a, [u8], (I::Aliasing, Aligned, Valid)> where T: Read, + [u8]: Read, I::Aliasing: Reference, { let ptr = self.cast::<_, AsBytesCast, _>(); @@ -1125,6 +1158,7 @@ mod _casts { where I::Aliasing: Reference, U: 'a + ?Sized + KnownLayout + Read, + [u8]: Read, { // FIXME(#67): Remove this allow. See NonNulSlicelExt for more // details. diff --git a/src/pointer/transmute.rs b/src/pointer/transmute.rs index b938d70b2d..be798fb0b0 100644 --- a/src/pointer/transmute.rs +++ b/src/pointer/transmute.rs @@ -187,12 +187,16 @@ pub unsafe trait MutationCompatible {} #[allow(missing_copy_implementations, missing_debug_implementations)] pub enum BecauseRead {} +// TODO: Maybe use the same reason for both? There shouldn't be any situation in +// which the reasons are different (one is BecauseExclusive and the other is +// BecauseImmutable). + // SAFETY: `Src: Read` and `Dst: Read`. -unsafe impl - MutationCompatible for Dst +unsafe impl + MutationCompatible for Dst where Src: Read, - Dst: Read, + Dst: Read, { } @@ -287,24 +291,11 @@ where /// DV>` conveys no safety guarantee. pub unsafe trait TransmuteFrom {} -/// # Safety -/// -/// `T` and `Self` must have the same vtable kind (`Sized`, slice DST, `dyn`, -/// etc) and have the same size. In particular: -/// - If `T: Sized` and `Self: Sized`, then their sizes must be equal -/// - If `T: ?Sized` and `Self: ?Sized`, then `Self::CastFrom` must be a -/// size-preserving cast. *Note that it is **not** guaranteed that an `as` -/// cast preserves referent size: it may be the case that `Self::CastFrom` -/// modifies the pointer's metadata in order to preserve referent size, which -/// an `as` cast does not do.* -pub unsafe trait SizeEq { - type CastFrom: cast::Cast; +pub trait SizeEq { + type CastFrom: cast::CastExact; } -// SAFETY: `T` trivially has the same size and vtable kind as `T`, and since -// pointer `*mut T -> *mut T` pointer casts are no-ops, this cast trivially -// preserves referent size (when `T: ?Sized`). -unsafe impl SizeEq for T { +impl SizeEq for T { type CastFrom = cast::IdCast; } @@ -458,19 +449,12 @@ impl_transitive_transmute_from!(T: ?Sized => UnsafeCell => T => Cell); // https://doc.rust-lang.org/1.85.0/core/mem/union.MaybeUninit.html unsafe impl TransmuteFrom for MaybeUninit {} -// SAFETY: `MaybeUninit` has the same size as `T` [1]. -// -// [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` -unsafe impl SizeEq for MaybeUninit { - type CastFrom = cast::CastSized; +impl SizeEq for MaybeUninit { + type CastFrom = cast::CastSizedExact; } -// SAFETY: See previous safety comment. -unsafe impl SizeEq> for T { - type CastFrom = cast::CastSized; +impl SizeEq> for T { + type CastFrom = cast::CastSizedExact; } #[cfg(test)] diff --git a/src/ref.rs b/src/ref.rs index 0b97ba686c..49d70b45d4 100644 --- a/src/ref.rs +++ b/src/ref.rs @@ -681,7 +681,7 @@ where cast_for_sized::< T, _, - (BecauseRead, (BecauseExclusive, BecauseExclusive)), + (BecauseRead, BecauseExclusive), (BecauseMutationCompatible, BecauseInvariantsEq), >(ptr) }; @@ -857,7 +857,7 @@ where cast_for_sized::< T, _, - (BecauseRead, (BecauseExclusive, BecauseExclusive)), + (BecauseRead, BecauseExclusive), (BecauseMutationCompatible, BecauseInvariantsEq), >(ptr) }; @@ -876,7 +876,7 @@ where let ptr = Ptr::from_mut(b) .try_cast_into_no_leftover::(None) .expect("zerocopy internal error: DerefMut::deref_mut should be infallible"); - let ptr = ptr.recall_validity::<_, (_, (_, (BecauseExclusive, BecauseExclusive)))>(); + let ptr = ptr.recall_validity::<_, (_, (_, BecauseExclusive))>(); ptr.as_mut() } } diff --git a/src/util/macro_util.rs b/src/util/macro_util.rs index ebbaf9e6ae..582a3c3f7c 100644 --- a/src/util/macro_util.rs +++ b/src/util/macro_util.rs @@ -30,9 +30,10 @@ use core::{ use crate::{ pointer::{ invariant::{self, BecauseExclusive, BecauseImmutable, Invariants}, - BecauseInvariantsEq, InvariantsEq, SizeEq, TryTransmuteFromPtr, + BecauseInvariantsEq, InvariantsEq, MutationCompatible, 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` without regard for field @@ -611,7 +612,7 @@ pub const fn hash_name(name: &str) -> i128 { /// [`is_bit_valid`]: TryFromBytes::is_bit_valid #[doc(hidden)] #[inline] -fn try_cast_or_pme( +fn try_cast_or_pme( src: Ptr<'_, Src, I>, ) -> Result< Ptr<'_, Dst, (I::Aliasing, invariant::Unaligned, invariant::Valid)>, @@ -623,7 +624,14 @@ where Src: invariant::Read, Dst: TryFromBytes + invariant::Read - + TryTransmuteFromPtr, + + TryTransmuteFromPtr + + MutationCompatible< + ReadOnly, + I::Aliasing, + invariant::Initialized, + invariant::Initialized, + TT, + > + MutationCompatible, I: Invariants, I::Aliasing: invariant::Reference, { @@ -636,7 +644,7 @@ where Err(err) => { // Re-cast `Ptr` to `Ptr`. let ptr = err.into_src(); - let ptr = ptr.cast::<_, crate::pointer::cast::CastSized, _>(); + let ptr: Ptr<'_, Src, _> = ptr.cast::<_, crate::pointer::cast::CastSized, _>(); // SAFETY: `ptr` is `src`, and has the same alignment invariant. let ptr = unsafe { ptr.assume_alignment::() }; // SAFETY: `ptr` is `src` and has the same validity invariant. @@ -684,7 +692,7 @@ where let ptr: Ptr<'_, Dst, _> = ptr.cast::<_, crate::pointer::cast::CastSized, _>(); - if Dst::is_bit_valid(ptr.forget_aligned()) { + if Dst::is_bit_valid(ptr.transmute::<_, _, (_, (_, BecauseExclusive))>().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 // operations have mutated it, so it is a bit-valid `Dst`. @@ -716,7 +724,7 @@ where { let ptr = Ptr::from_ref(src); let ptr = ptr.bikeshed_recall_initialized_immutable(); - match try_cast_or_pme::(ptr) { + match try_cast_or_pme::(ptr) { Ok(ptr) => { static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); // SAFETY: We have checked that `Dst` does not have a stricter @@ -760,7 +768,7 @@ where { let ptr = Ptr::from_mut(src); let ptr = ptr.bikeshed_recall_initialized_from_bytes(); - match try_cast_or_pme::(ptr) { + match try_cast_or_pme::(ptr) { Ok(ptr) => { static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); // SAFETY: We have checked that `Dst` does not have a stricter diff --git a/src/util/macros.rs b/src/util/macros.rs index 7f91e3a4a7..ac777d451b 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -219,8 +219,8 @@ macro_rules! impl_for_transmute_from { ) => { #[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)))>(); + // let c: Maybe<'_, Self, crate::pointer::invariant::Exclusive> = candidate.into_exclusive_or_pme(); + let c: Maybe<'_, $repr, _> = candidate.transmute::<_, _, (_, _)>(); // 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`. @@ -732,28 +732,63 @@ macro_rules! define_cast { /// # Safety /// /// `T` and `$wrapper` must have the same bit validity, and must have the -/// same size in the sense of `SizeEq`. +/// same size in the sense of `CastExact`. macro_rules! unsafe_impl_for_transparent_wrapper { ($vis:vis T $(: ?$optbound:ident)? => $wrapper:ident) => {{ crate::util::macros::__unsafe(); - use crate::pointer::{TransmuteFrom, SizeEq, invariant::Valid}; + use crate::pointer::{TransmuteFrom, cast::{CastExact, TransitiveProject}, SizeEq, invariant::Valid}; + use crate::wrappers::ReadOnly; // SAFETY: The caller promises that `T` and `$wrapper` have the same // bit validity. unsafe impl TransmuteFrom for $wrapper {} // SAFETY: See previous safety comment. unsafe impl TransmuteFrom<$wrapper, Valid, Valid> for T {} - define_cast!(unsafe { $vis CastA = T => $wrapper }); - // SAFETY: The caller promises that `T` and `$wrapper` satisfy - // `SizeEq`. - unsafe impl SizeEq for $wrapper { - type CastFrom = CastA; + define_cast!(unsafe { $vis CastToWrapper = T => $wrapper }); + // SAFETY: The caller promises that `T` and `$wrapper` have the same + // size in the sense of `CastExact`. + unsafe impl CastExact> for CastToWrapper {} + define_cast!(unsafe { $vis CastFromWrapper = $wrapper => T }); + // SAFETY: The caller promises that `T` and `$wrapper` have the same + // size in the sense of `CastExact`. + unsafe impl CastExact<$wrapper, T> for CastFromWrapper {} + + impl SizeEq for $wrapper { + type CastFrom = CastToWrapper; } - define_cast!(unsafe { $vis CastB = $wrapper => T }); - // SAFETY: See previous safety comment. - unsafe impl SizeEq<$wrapper> for T { - type CastFrom = CastB; + impl SizeEq<$wrapper> for T { + type CastFrom = CastFromWrapper; + } + + impl SizeEq> for $wrapper { + type CastFrom = TransitiveProject< + T, + >>::CastFrom, + CastToWrapper, + >; + } + impl SizeEq<$wrapper> for ReadOnly { + type CastFrom = TransitiveProject< + T, + CastFromWrapper, + as SizeEq>::CastFrom, + >; + } + + impl SizeEq> for ReadOnly<$wrapper> { + type CastFrom = TransitiveProject< + $wrapper, + <$wrapper as SizeEq>>::CastFrom, + > as SizeEq<$wrapper>>::CastFrom, + >; + } + impl SizeEq>> for ReadOnly { + type CastFrom = TransitiveProject< + $wrapper, + <$wrapper as SizeEq>>>::CastFrom, + as SizeEq<$wrapper>>::CastFrom, + >; } }}; } @@ -765,7 +800,7 @@ macro_rules! impl_transitive_transmute_from { // SAFETY: Since `$u: SizeEq<$t>` and `$v: SizeEq`, this impl is // transitively sound. - unsafe impl<$($tyvar $(: ?$optbound)?)?> SizeEq<$t> for $v + impl<$($tyvar $(: ?$optbound)?)?> SizeEq<$t> for $v where $u: SizeEq<$t>, $v: SizeEq<$u>, @@ -792,16 +827,38 @@ macro_rules! impl_transitive_transmute_from { #[rustfmt::skip] macro_rules! impl_size_eq { - ($t:ty, $u:ty) => { + ($($tyvar:ident)? => $t:ty, $u:ty) => { const _: () = { use $crate::{pointer::{cast::CastUnsized, SizeEq}}; - // SAFETY: See inline. - unsafe impl SizeEq<$t> for $u { + impl<$($tyvar)?> SizeEq<$t> for $u { + type CastFrom = CastUnsized; + } + impl<$($tyvar)?> SizeEq<$u> for $t { type CastFrom = CastUnsized; } - // SAFETY: See previous safety comment. - unsafe impl SizeEq<$u> for $t { + + impl<$($tyvar)?> SizeEq> for $u { + type CastFrom = CastUnsized; + } + + impl<$($tyvar)?> SizeEq<$u> for ReadOnly<$t> { + type CastFrom = CastUnsized; + } + + impl<$($tyvar)?> SizeEq<$t> for ReadOnly<$u> { + type CastFrom = CastUnsized; + } + + impl<$($tyvar)?> SizeEq> for $t { + type CastFrom = CastUnsized; + } + + impl<$($tyvar)?> SizeEq> for ReadOnly<$u> { + type CastFrom = CastUnsized; + } + + impl<$($tyvar)?> SizeEq> for ReadOnly<$t> { type CastFrom = CastUnsized; } }; @@ -823,7 +880,7 @@ macro_rules! unsafe_with_size_eq { (<$src:ident<$t:ident>, $dst:ident<$u:ident>> $blk:expr) => {{ crate::util::macros::__unsafe(); - use crate::{KnownLayout, pointer::cast::TransitiveProject}; + use crate::{KnownLayout, pointer::cast::{TransitiveProject, CastExact}}; #[repr(transparent)] struct $src(T); @@ -847,8 +904,16 @@ macro_rules! unsafe_with_size_eq { // no added semantics. unsafe impl InvariantsEq<$dst> for T {} + // SAFETY: `$src` is a `#[repr(transparent)]` wrapper around `T`, and + // so this cast exactly preserves the set of referent bytes. define_cast!(unsafe { SrcCast = $src => T }); + // SAFETY: See previous safety comment. + unsafe impl CastExact<$src, T> for SrcCast {} + // SAFETY: `$dst` is a `#[repr(transparent)]` wrapper around `U`, and + // so this cast exactly preserves the set of referent bytes. define_cast!(unsafe { DstCast = U => $dst }); + // SAFETY: See previous safety comment. + unsafe impl CastExact> for DstCast {} // SAFETY: See inline for the soundness of this impl when // `CastFrom::project` is actually instantiated (otherwise, PMEs may not @@ -857,7 +922,7 @@ macro_rules! unsafe_with_size_eq { // We manually instantiate `CastFrom::project` below to ensure that this // PME can be triggered, and the caller promises not to use `$src` and // `$dst` with any wrapped types other than `$t` and `$u` respectively. - unsafe impl SizeEq<$src> for $dst + impl SizeEq<$src> for $dst where T: KnownLayout, U: KnownLayout, diff --git a/src/wrappers.rs b/src/wrappers.rs index 1267888d0d..d80db7cfbc 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -9,6 +9,7 @@ use core::{fmt, hash::Hash}; use super::*; +use crate::pointer::{invariant::Valid, SizeEq, TransmuteFrom}; /// A type with no alignment requirement. /// @@ -594,6 +595,141 @@ impl fmt::Debug for MaybeUninit { } } +/// TODO +#[cfg_attr(any(feature = "derive", test), derive(FromBytes, IntoBytes, Unaligned))] +#[repr(transparent)] +pub struct ReadOnly(T); + +// SAFETY: TODO +const _: () = unsafe { + unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] ReadOnly); +}; + +// Unused when `feature = "derive"`. +#[allow(unused_unsafe, clippy::multiple_unsafe_ops_per_block)] +// 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`. +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::<_, _, _>()) + ); + 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); +}; + +// SAFETY: TODO +const _: () = unsafe { + unsafe_impl!(T: ?Sized => Immutable for ReadOnly); +}; + +const _: () = { + use crate::pointer::cast::CastExact; + + // SAFETY: TODO + define_cast!(unsafe { pub CastFromReadOnly = ReadOnly => T}); + // SAFETY: TODO + unsafe impl CastExact, T> for CastFromReadOnly {} + // SAFETY: TODO + define_cast!(unsafe { pub CastToReadOnly = T => ReadOnly}); + // SAFETY: TODO + unsafe impl CastExact> for CastToReadOnly {} + + impl SizeEq> for T { + type CastFrom = CastFromReadOnly; + } + + impl SizeEq for ReadOnly { + type CastFrom = CastToReadOnly; + } + + // SAFETY: TODO + unsafe impl crate::pointer::cast::Wrapped for ReadOnly { + type Unwrapped = T; + type CastToUnwrapped = CastFromReadOnly; + type CastFromUnwrapped = CastToReadOnly; + } + + // SAFETY: TODO + unsafe impl crate::pointer::cast::HasWrappedField for ReadOnly { + type WrappedField = ReadOnly; + } +}; + +// SAFETY: TODO +unsafe impl TransmuteFrom for ReadOnly {} + +// SAFETY: TODO +unsafe impl TransmuteFrom, Valid, Valid> for T {} + +// enum BecauseReadOnly {} + +// /// Denotes that `src: Ptr` and `dst: Ptr`, +// /// referencing the same referent at the same time, cannot be used by safe code +// /// to break library safety invariants of `Src` or `Self`. +// /// +// /// # Safety +// /// +// /// At least one of the following must hold: +// /// - `Src: Read` and `Self: Read` +// /// - `Self: InvariantsEq`, and, for some `V`: +// /// - `Dst: TransmuteFrom` +// /// - `Src: TransmuteFrom` + +// // SAFETY: TODO +// unsafe impl MutationCompatible +// for ReadOnly +// { +// } + +impl ReadOnly { + /// TODO + #[inline(always)] + pub const fn new(t: T) -> ReadOnly { + ReadOnly(t) + } +} + +impl<'a, T: ?Sized + Immutable> From<&'a T> for &'a ReadOnly { + #[inline(always)] + fn from(t: &'a T) -> &'a ReadOnly { + let ro = Ptr::from_ref(t).transmute::<_, _, (_, _)>(); + // SAFETY: TODO + let ro = unsafe { ro.assume_alignment() }; + ro.as_ref() + } +} + +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 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 2fbd3c74ef..dec268bb08 100644 --- a/zerocopy-derive/src/enum.rs +++ b/zerocopy-derive/src/enum.rs @@ -332,16 +332,18 @@ pub(crate) fn derive_is_bit_valid( // SAFETY: Since we know that the tag is `#tag_ident`, we // know that no other `&`s exist which refer to this enum // as any other variant. - let variant_md = unsafe { variants.cast_unchecked::< - core_reexport::mem::ManuallyDrop<#variant_struct_ident #ty_generics>, - #zerocopy_crate::pointer::cast::Projection< + let variant_md = variants.cast::< + ReadOnly>, + #zerocopy_crate::pointer::cast::WrappedProjection< + _, _, { #zerocopy_crate::UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#variants_union_field_ident) } - > - >() }; + >, + (_, _), + >(); let variant = variant_md.cast::< - #variant_struct_ident #ty_generics, + ReadOnly<#variant_struct_ident #ty_generics>, #zerocopy_crate::pointer::cast::CastSized, #zerocopy_crate::pointer::BecauseInvariantsEq >(); @@ -388,7 +390,7 @@ pub(crate) fn derive_is_bit_valid( where ___ZerocopyAliasing: #zerocopy_crate::pointer::invariant::Reference, { - use #zerocopy_crate::util::macro_util::core_reexport; + use #zerocopy_crate::{pointer::cast::WrappedProjection, ReadOnly, util::macro_util::core_reexport}; #tag_enum @@ -427,16 +429,22 @@ pub(crate) fn derive_is_bit_valid( #(#has_fields)* let mut raw_enum = candidate.cast::< - ___ZerocopyRawEnum #ty_generics, + ReadOnly<___ZerocopyRawEnum #ty_generics>, #zerocopy_crate::pointer::cast::CastSized, #zerocopy_crate::pointer::BecauseInvariantsEq >(); let tag = { - let tag_ptr = raw_enum.reborrow().project::< - (), - { #zerocopy_crate::ident_id!(tag) } - >().cast::< + let tag_ptr = raw_enum.reborrow().cast::< + _, + WrappedProjection<_, _, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(tag) }>, + _, + >() + // let tag_ptr = raw_enum.reborrow().project::< + // (), + // { #zerocopy_crate::ident_id!(tag) } + // >() + .cast::< ___ZerocopyTagPrimitive, #zerocopy_crate::pointer::cast::CastSized, _ @@ -444,7 +452,12 @@ pub(crate) fn derive_is_bit_valid( tag_ptr.recall_validity::<_, (_, (_, _))>().read_unaligned::<#zerocopy_crate::BecauseImmutable>() }; - let variants = raw_enum.project::<_, { #zerocopy_crate::ident_id!(variants) }>(); + let variants = raw_enum.cast::< + _, + WrappedProjection<_, _, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(variants) }>, + _, + >(); + // let variants = raw_enum.project::<_, { #zerocopy_crate::ident_id!(variants) }>(); #[allow(non_upper_case_globals)] match tag { diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index ae8b98c9a7..60e9ec484a 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -905,10 +905,19 @@ fn derive_try_from_bytes_struct( use #zerocopy_crate::pointer::PtrInner; true #(&& { - let field_candidate = candidate.reborrow().project::< - _, - { #zerocopy_crate::ident_id!(#field_names) } - >(); + let field_candidate = unsafe { + candidate.reborrow().project_transmute_unchecked::< + _, + _, + // WrappedProjection + #zerocopy_crate::pointer::cast::WrappedProjection<_, _, { #zerocopy_crate::STRUCT_VARIANT_ID }, { #zerocopy_crate::ident_id!(#field_names) }> + >() + }; + + // let field_candidate = candidate.reborrow().project::< + // _, + // { #zerocopy_crate::ident_id!(#field_names) } + // >(); <#field_tys as #zerocopy_crate::TryFromBytes>::is_bit_valid(field_candidate) })* @@ -971,7 +980,8 @@ fn derive_try_from_bytes_union( candidate.reborrow().project_transmute_unchecked::< _, _, - #zerocopy_crate::pointer::cast::Projection<_, { #zerocopy_crate::UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#field_names) }> + // WrappedProjection + #zerocopy_crate::pointer::cast::WrappedProjection<_, _, { #zerocopy_crate::UNION_VARIANT_ID }, { #zerocopy_crate::ident_id!(#field_names) }> >() }; diff --git a/zerocopy-derive/tests/struct_try_from_bytes.rs b/zerocopy-derive/tests/struct_try_from_bytes.rs index 3a4537733e..eb4ab18935 100644 --- a/zerocopy-derive/tests/struct_try_from_bytes.rs +++ b/zerocopy-derive/tests/struct_try_from_bytes.rs @@ -76,7 +76,7 @@ fn two_bad() { let candidate = { use imp::pointer::{cast::CastSized, BecauseExclusive}; - candidate.cast::<_, CastSized, (_, (BecauseExclusive, BecauseExclusive))>() + candidate.cast::<_, CastSized, (_, BecauseExclusive)>() }; // SAFETY: `candidate`'s referent is as-initialized as `Two`. @@ -105,7 +105,7 @@ fn un_sized() { let candidate = { use imp::pointer::{cast::CastUnsized, BecauseExclusive}; - candidate.cast::<_, CastUnsized, (_, (BecauseExclusive, BecauseExclusive))>() + candidate.cast::<_, CastUnsized, (_, BecauseExclusive)>() }; // SAFETY: `candidate`'s referent is as-initialized as `Two`. @@ -161,8 +161,7 @@ fn test_maybe_from_bytes() { let candidate = { use imp::pointer::{cast::CastSized, BecauseExclusive}; - candidate - .cast::, CastSized, (_, (BecauseExclusive, BecauseExclusive))>() + candidate.cast::, CastSized, (_, BecauseExclusive)>() }; // SAFETY: `[u8]` consists entirely of initialized bytes. diff --git a/zerocopy-derive/tests/union_try_from_bytes.rs b/zerocopy-derive/tests/union_try_from_bytes.rs index ce37197ecb..382c30a972 100644 --- a/zerocopy-derive/tests/union_try_from_bytes.rs +++ b/zerocopy-derive/tests/union_try_from_bytes.rs @@ -71,7 +71,7 @@ fn two_bad() { let candidate = { use imp::pointer::{cast::CastSized, BecauseExclusive}; - candidate.cast::() + candidate.cast::() }; // SAFETY: `candidate`'s referent is as-initialized as `Two`. @@ -99,7 +99,7 @@ fn bool_and_zst() { let candidate = { use imp::pointer::{cast::CastSized, BecauseExclusive}; - candidate.cast::() + candidate.cast::() }; // SAFETY: `candidate`'s referent is fully initialized. @@ -127,8 +127,7 @@ fn test_maybe_from_bytes() { let candidate = { use imp::pointer::{cast::CastSized, BecauseExclusive}; - candidate - .cast::, CastSized, (_, (BecauseExclusive, BecauseExclusive))>() + candidate.cast::, CastSized, (_, BecauseExclusive)>() }; // SAFETY: `[u8]` consists entirely of initialized bytes.