diff --git a/src/byteorder.rs b/src/byteorder.rs index 2575052034..965ce27cb8 100644 --- a/src/byteorder.rs +++ b/src/byteorder.rs @@ -505,6 +505,9 @@ example of how it can be used for parsing UDP packets. impl_or_verify!(O => Unaligned for $name); }; + #[cfg(not(any(feature = "derive", test)))] + impl_initialize_into_bytes!(O => $name); + impl Default for $name { #[inline(always)] fn default() -> $name { diff --git a/src/impls.rs b/src/impls.rs index 80538bfc8a..e85b3e995f 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -31,6 +31,8 @@ const _: () = unsafe { assert_unaligned!(()); }; +impl_initialize_into_bytes!(()); + // SAFETY: // - `Immutable`: These types self-evidently do not contain any `UnsafeCell`s. // - `TryFromBytes` (with no validator), `FromZeros`, `FromBytes`: all bit @@ -84,6 +86,10 @@ const _: () = unsafe { unsafe_impl!(#[cfg_attr(doc_cfg, doc(cfg(feature = "float-nightly")))] f128: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes); }; +impl_initialize_into_bytes!( + u8, i8, u16, i16, u32, i32, u64, i64, u128, i128, usize, isize, f32, f64 +); + // SAFETY: // - `Immutable`: `bool` self-evidently does not contain any `UnsafeCell`s. // - `FromZeros`: Valid since "[t]he value false has the bit pattern 0x00" [1]. @@ -98,6 +104,7 @@ const _: () = unsafe { #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl!(bool: Immutable, FromZeros, IntoBytes, Unaligned) }; assert_unaligned!(bool); +impl_initialize_into_bytes!(bool); // SAFETY: The impl must only return `true` for its argument if the original // `Maybe` refers to a valid `bool`. We only return true if the `u8` value @@ -127,6 +134,7 @@ const _: () = unsafe { // [1] https://doc.rust-lang.org/1.81.0/reference/types/textual.html #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl!(char: Immutable, FromZeros, IntoBytes) }; +impl_initialize_into_bytes!(char); // SAFETY: The impl must only return `true` for its argument if the original // `Maybe` refers to a valid `char`. `char::from_u32` guarantees that it @@ -162,6 +170,20 @@ const _: () = unsafe { #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl!(str: Immutable, FromZeros, IntoBytes, Unaligned) }; +// SAFETY: By invariant on `Self: IntoBytes`, values of type `Self` never +// contain padding and therefore require no additional initialization. +unsafe impl InitializeIntoBytes for str +where + Self: IntoBytes, +{ + #[inline(always)] + fn initialize_padding( + _: Ptr<'_, Self, (invariant::Exclusive, invariant::Unaligned, invariant::AsInitialized)>, + ) { + // By invariant on `Self: IntoBytes`, values of type `Self` never contain padding. + } +} + // SAFETY: The impl must only return `true` for its argument if the original // `Maybe` refers to a valid `str`. `str::from_utf8` guarantees that it // returns `Err` if its input is not a valid `str` [1]. @@ -235,6 +257,20 @@ const _: () = unsafe { unsafe_impl!(NonZeroI128: Immutable, IntoBytes); unsafe_impl!(NonZeroUsize: Immutable, IntoBytes); unsafe_impl!(NonZeroIsize: Immutable, IntoBytes); + impl_initialize_into_bytes!( + NonZeroU8, + NonZeroI8, + NonZeroU16, + NonZeroI16, + NonZeroU32, + NonZeroI32, + NonZeroU64, + NonZeroI64, + NonZeroU128, + NonZeroI128, + NonZeroUsize, + NonZeroIsize + ); unsafe_impl_try_from_bytes_for_nonzero!( NonZeroU8[u8], NonZeroI8[i8], @@ -277,19 +313,28 @@ const _: () = unsafe { // are guaranteed to have the same size and alignment: #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); - assert_unaligned!(Option, Option); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes); - unsafe_impl!(Option: TryFromBytes, FromZeros, FromBytes, IntoBytes); + macro_rules! impl_for_options { + ($($inner:ty,)*) => { + $( + unsafe_impl!(Option<$inner>: TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); + impl_initialize_into_bytes!(Option<$inner>); + )* + } + } + impl_for_options!( + NonZeroU8, + NonZeroI8, + NonZeroU16, + NonZeroI16, + NonZeroU32, + NonZeroI32, + NonZeroU64, + NonZeroI64, + NonZeroU128, + NonZeroI128, + NonZeroUsize, + NonZeroIsize, + ); }; // SAFETY: While it's not fully documented, the consensus is that `Box` does @@ -404,6 +449,7 @@ mod atomics { impl_for_transmute_from!(=> FromZeros for $atomics [$primitives]); impl_for_transmute_from!(=> FromBytes for $atomics [$primitives]); impl_for_transmute_from!(=> TryFromBytes for $atomics [$primitives]); + impl_for_transmute_from!(=> InitializeIntoBytes for $atomics [$primitives]); impl_for_transmute_from!(=> IntoBytes for $atomics [$primitives]); )* }; @@ -419,7 +465,7 @@ mod atomics { ($($($tyvar:ident)? => $atomic:ty [$prim:ty]),*) => {{ crate::util::macros::__unsafe(); - use crate::pointer::{SizeEq, TransmuteFrom, invariant::Valid}; + use crate::pointer::{SizeEq, TransmuteFrom, invariant::{AsInitialized, Valid}}; $( // SAFETY: The caller promised that `$atomic` and `$prim` have @@ -429,6 +475,17 @@ 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 and bit validity. + unsafe impl<$($tyvar)?> TransmuteFrom<$atomic, AsInitialized, AsInitialized> for $prim {} + // SAFETY: The caller promised that `$atomic` and `$prim` have + // the same size and bit validity. + unsafe impl<$($tyvar)?> TransmuteFrom<$prim, AsInitialized, AsInitialized> for $atomic {} + + impl<$($tyvar)?> SizeEq<$atomic> for $prim { + type CastFrom = $crate::pointer::cast::CastSizedExact; + } + impl<$($tyvar)?> SizeEq> for ReadOnly<$prim> { type CastFrom = $crate::pointer::cast::CastSizedExact; } @@ -461,6 +518,7 @@ mod atomics { impl_known_layout!(AtomicBool); impl_for_transmute_from!(=> FromZeros for AtomicBool [bool]); impl_for_transmute_from!(=> TryFromBytes for AtomicBool [bool]); + impl_for_transmute_from!(=> InitializeIntoBytes for AtomicBool [bool]); impl_for_transmute_from!(=> IntoBytes for AtomicBool [bool]); // SAFETY: Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the @@ -653,6 +711,7 @@ const _: () = unsafe { unsafe_impl!(T: ?Sized => TryFromBytes for PhantomData); unsafe_impl!(T: ?Sized => FromZeros for PhantomData); unsafe_impl!(T: ?Sized => FromBytes for PhantomData); + impl_initialize_into_bytes!(T: ?Sized => PhantomData); unsafe_impl!(T: ?Sized => IntoBytes for PhantomData); unsafe_impl!(T: ?Sized => Unaligned for PhantomData); assert_unaligned!(PhantomData<()>, PhantomData, PhantomData); @@ -661,6 +720,7 @@ const _: () = unsafe { impl_for_transmute_from!(T: TryFromBytes => TryFromBytes for Wrapping[T]); impl_for_transmute_from!(T: FromZeros => FromZeros for Wrapping[T]); impl_for_transmute_from!(T: FromBytes => FromBytes for Wrapping[T]); +impl_for_transmute_from!(T: InitializeIntoBytes => InitializeIntoBytes for Wrapping[T]); impl_for_transmute_from!(T: IntoBytes => IntoBytes for Wrapping[T]); assert_unaligned!(Wrapping<()>, Wrapping); @@ -735,6 +795,7 @@ const _: () = unsafe { unsafe_impl!(T: ?Sized + Immutable => Immutable for Manua impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop[T]); impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for ManuallyDrop[T]); impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop[T]); +impl_for_transmute_from!(T: ?Sized + InitializeIntoBytes => InitializeIntoBytes for ManuallyDrop[T]); impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for ManuallyDrop[T]); // SAFETY: `ManuallyDrop` has the same layout as `T` [1], and thus has the // same alignment as `T`. @@ -825,6 +886,7 @@ const _: () = { impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for Cell[T]); impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for Cell[T]); impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for Cell[T]); +impl_for_transmute_from!(T: ?Sized + InitializeIntoBytes => InitializeIntoBytes for Cell[T]); impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for Cell[T]); // SAFETY: `Cell` has the same in-memory representation as `T` [1], and thus // has the same alignment as `T`. @@ -836,6 +898,7 @@ const _: () = unsafe { unsafe_impl!(T: ?Sized + Unaligned => Unaligned for Cell< impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for UnsafeCell[T]); impl_for_transmute_from!(T: ?Sized + FromBytes => FromBytes for UnsafeCell[T]); +impl_for_transmute_from!(T: ?Sized + InitializeIntoBytes => InitializeIntoBytes for UnsafeCell[T]); impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for UnsafeCell[T]); // SAFETY: `UnsafeCell` has the same in-memory representation as `T` [1], and // thus has the same alignment as `T`. @@ -902,6 +965,9 @@ const _: () = unsafe { }); unsafe_impl!(const N: usize, T: FromZeros => FromZeros for [T; N]); unsafe_impl!(const N: usize, T: FromBytes => FromBytes for [T; N]); + unsafe_impl!(const N: usize, T: InitializeIntoBytes => InitializeIntoBytes for [T; N]; |slf| { + slf.as_slice().iter().for_each(InitializeIntoBytes::initialize_padding); + }); unsafe_impl!(const N: usize, T: IntoBytes => IntoBytes for [T; N]); unsafe_impl!(const N: usize, T: Unaligned => Unaligned for [T; N]); assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]); @@ -933,6 +999,9 @@ const _: () = unsafe { }); unsafe_impl!(T: FromZeros => FromZeros for [T]); unsafe_impl!(T: FromBytes => FromBytes for [T]); + unsafe_impl!(T: InitializeIntoBytes => InitializeIntoBytes for [T]; |slf| { + slf.iter().for_each(InitializeIntoBytes::initialize_padding); + }); unsafe_impl!(T: IntoBytes => IntoBytes for [T]); unsafe_impl!(T: Unaligned => Unaligned for [T]); }; @@ -1310,7 +1379,7 @@ mod simd { // SAFETY: See comment on module definition for justification. #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { - $( unsafe_impl!($typ: Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes); )* + $( unsafe_impl!($typ: Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes); )* }; } }; @@ -2187,18 +2256,18 @@ mod tests { !Unaligned ); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); - assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); + assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); // Implements none of the ZC traits. struct NotZerocopy; @@ -2229,39 +2298,39 @@ mod tests { assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned); assert_impls!(Option: KnownLayout, Immutable, TryFromBytes, FromZeros, !FromBytes, !IntoBytes, !Unaligned); - assert_impls!(PhantomData: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); - assert_impls!(PhantomData>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); - assert_impls!(PhantomData<[u8]>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); + assert_impls!(PhantomData: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned); + assert_impls!(PhantomData>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned); + assert_impls!(PhantomData<[u8]>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned); - assert_impls!(ManuallyDrop: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); + assert_impls!(ManuallyDrop: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned); // This test is important because it allows us to test our hand-rolled // implementation of ` as TryFromBytes>::is_bit_valid`. - assert_impls!(ManuallyDrop: KnownLayout, Immutable, TryFromBytes, FromZeros, IntoBytes, Unaligned, !FromBytes); - assert_impls!(ManuallyDrop<[u8]>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); + assert_impls!(ManuallyDrop: KnownLayout, Immutable, TryFromBytes, FromZeros, InitializeIntoBytes, IntoBytes, Unaligned, !FromBytes); + assert_impls!(ManuallyDrop<[u8]>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned); // This test is important because it allows us to test our hand-rolled // implementation of ` as TryFromBytes>::is_bit_valid`. - assert_impls!(ManuallyDrop<[bool]>: KnownLayout, Immutable, TryFromBytes, FromZeros, IntoBytes, Unaligned, !FromBytes); + assert_impls!(ManuallyDrop<[bool]>: KnownLayout, Immutable, TryFromBytes, FromZeros, InitializeIntoBytes, IntoBytes, Unaligned, !FromBytes); assert_impls!(ManuallyDrop: !Immutable, !TryFromBytes, !KnownLayout, !FromZeros, !FromBytes, !IntoBytes, !Unaligned); assert_impls!(ManuallyDrop<[NotZerocopy]>: KnownLayout, !Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned); - assert_impls!(ManuallyDrop>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !Immutable); - assert_impls!(ManuallyDrop<[UnsafeCell]>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !Immutable); - assert_impls!(ManuallyDrop<[UnsafeCell]>: KnownLayout, TryFromBytes, FromZeros, IntoBytes, Unaligned, !Immutable, !FromBytes); + assert_impls!(ManuallyDrop>: KnownLayout, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned, !Immutable); + assert_impls!(ManuallyDrop<[UnsafeCell]>: KnownLayout, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned, !Immutable); + assert_impls!(ManuallyDrop<[UnsafeCell]>: KnownLayout, TryFromBytes, FromZeros, InitializeIntoBytes, IntoBytes, Unaligned, !Immutable, !FromBytes); assert_impls!(CoreMaybeUninit: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, Unaligned, !IntoBytes); assert_impls!(CoreMaybeUninit: KnownLayout, TryFromBytes, FromZeros, FromBytes, !Immutable, !IntoBytes, !Unaligned); assert_impls!(CoreMaybeUninit>: KnownLayout, TryFromBytes, FromZeros, FromBytes, Unaligned, !Immutable, !IntoBytes); - assert_impls!(Wrapping: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); + assert_impls!(Wrapping: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned); // This test is important because it allows us to test our hand-rolled // implementation of ` as TryFromBytes>::is_bit_valid`. - assert_impls!(Wrapping: KnownLayout, Immutable, TryFromBytes, FromZeros, IntoBytes, Unaligned, !FromBytes); + assert_impls!(Wrapping: KnownLayout, Immutable, TryFromBytes, FromZeros, InitializeIntoBytes, IntoBytes, Unaligned, !FromBytes); assert_impls!(Wrapping: KnownLayout, !Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes, !Unaligned); - assert_impls!(Wrapping>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !Immutable); + assert_impls!(Wrapping>: KnownLayout, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned, !Immutable); - assert_impls!(Unalign: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned); + assert_impls!(Unalign: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, Unaligned); // This test is important because it allows us to test our hand-rolled // implementation of ` as TryFromBytes>::is_bit_valid`. - assert_impls!(Unalign: KnownLayout, Immutable, TryFromBytes, FromZeros, IntoBytes, Unaligned, !FromBytes); + assert_impls!(Unalign: KnownLayout, Immutable, TryFromBytes, FromZeros, InitializeIntoBytes, IntoBytes, Unaligned, !FromBytes); assert_impls!(Unalign: KnownLayout, Unaligned, !Immutable, !TryFromBytes, !FromZeros, !FromBytes, !IntoBytes); assert_impls!( @@ -2335,7 +2404,7 @@ mod tests { { use core::arch::$arch::{$($typ),*}; use crate::*; - $( assert_impls!($typ: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, !Unaligned); )* + $( assert_impls!($typ: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, InitializeIntoBytes, IntoBytes, !Unaligned); )* } }; } diff --git a/src/layout.rs b/src/layout.rs index 19ad5ca85f..d23148ed29 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -750,6 +750,13 @@ impl DstLayout { } } +/*pub enum Fields { + Primitive, + Struct { fields: &'static [(usize, DstLayout)] }, + Union { fields: &'static [(usize, DstLayout)] }, + Enum, +}*/ + pub(crate) use cast_from::CastFrom; mod cast_from { use crate::*; diff --git a/src/lib.rs b/src/lib.rs index 0c66144932..9a9bc70302 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5496,6 +5496,27 @@ fn mut_from_prefix_suffix( Ok((slf.recall_validity::<_, (_, (_, _))>().as_mut(), prefix_suffix.as_mut())) } +/// # Safety +/// +/// TODO +pub unsafe trait InitializeIntoBytes { + #[doc(hidden)] + fn only_derive_is_allowed_to_implement_this_trait() + where + Self: Sized; + + #[doc(hidden)] + fn initialize_padding( + ptr: Ptr<'_, Self, (invariant::Exclusive, invariant::Unaligned, invariant::AsInitialized)>, + ); + + /// Produce an [`IntoBytes`] reference to `Self` by initializing its + /// padding. + fn initialize_into_bytes(&mut self) -> &(impl IntoBytes + Immutable + ?Sized) { + &42 + } +} + /// Analyzes whether a type is [`IntoBytes`]. /// /// This derive analyzes, at compile time, whether the annotated type satisfies @@ -5676,7 +5697,7 @@ pub use zerocopy_derive::IntoBytes; not(no_zerocopy_diagnostic_on_unimplemented_1_78_0), diagnostic::on_unimplemented(note = "Consider adding `#[derive(IntoBytes)]` to `{Self}`") )] -pub unsafe trait IntoBytes { +pub unsafe trait IntoBytes: InitializeIntoBytes { // The `Self: Sized` bound makes it so that this function doesn't prevent // `IntoBytes` from being object safe. Note that other `IntoBytes` methods // prevent object safety, but those provide a benefit in exchange for object diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 30e7a7080f..eaafa01216 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -96,7 +96,7 @@ mod def { /// Note that this method does not consume `self`. The caller should /// watch out for `unsafe` code which uses the returned value in a way /// that violates the safety invariants of `self`. - pub(crate) fn as_inner(&self) -> PtrInner<'a, T> { + pub fn as_inner(&self) -> PtrInner<'a, T> { self.ptr } } diff --git a/src/util/macros.rs b/src/util/macros.rs index 7dca5410c8..65dc714c66 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -128,7 +128,18 @@ macro_rules! unsafe_impl { unsafe_impl!(@method $trait $(; |$candidate| $is_bit_valid)?); } }}; + (@method InitializeIntoBytes ; |$slf:ident| $initialize_padding:expr) => { + #[allow(clippy::missing_inline_in_public_items, dead_code)] + #[cfg_attr(all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), coverage(off))] + fn only_derive_is_allowed_to_implement_this_trait() {} + #[allow(unused_mut)] + #[inline(always)] + fn initialize_padding($slf: Ptr<'_, Self, (invariant::Exclusive, invariant::Unaligned, invariant::AsInitialized)>) + { + $initialize_padding + } + }; (@method TryFromBytes ; |$candidate:ident| $is_bit_valid:expr) => { #[allow(clippy::missing_inline_in_public_items, dead_code)] #[cfg_attr(all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), coverage(off))] @@ -174,6 +185,7 @@ macro_rules! impl_for_transmute_from { $(#[$attr:meta])* $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?)? => $trait:ident for $ty:ty [$repr:ty] + $(|$slf:ident| $b:block)* ) => { const _: () = { $(#[$attr])* @@ -214,12 +226,20 @@ macro_rules! impl_for_transmute_from { $(<$tyvar $(: $(? $optbound +)* $($bound +)*)?>)? $trait for $ty [$repr] ); + + impl_for_transmute_from!( + @initialize_into_bytes + $(<$tyvar $(: $(? $optbound +)* $($bound +)*)?>)? + $trait for $ty [$repr] + $(|$slf| $b)* + ); } }; }; (@assert_is_supported_trait TryFromBytes) => {}; (@assert_is_supported_trait FromZeros) => {}; (@assert_is_supported_trait FromBytes) => {}; + (@assert_is_supported_trait InitializeIntoBytes) => {}; (@assert_is_supported_trait IntoBytes) => {}; ( @is_bit_valid @@ -242,7 +262,29 @@ macro_rules! impl_for_transmute_from { $(<$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?>)? $trait:ident for $ty:ty [$repr:ty] ) => { - // Trait other than `TryFromBytes`; no `is_bit_valid` impl. + // Other trait; no additional items. + }; + ( + @initialize_into_bytes + $(<$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?>)? + InitializeIntoBytes for $ty:ty [$repr:ty] + ) => { + #[inline(always)] + fn initialize_padding(ptr: Ptr<'_, Self, (invariant::Exclusive, invariant::Unaligned, invariant::AsInitialized)>) + { + let ptr = ptr.transmute::<$repr, _, (_, (_, _))>(); + // 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 InitializeIntoBytes>::initialize_padding(ptr) + } + }; + ( + @initialize_into_bytes + $(<$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?>)? + $trait:ident for $ty:ty [$repr:ty] $(|$slf:ident| $b:block)* + ) => { + // Other trait; no additional items. }; } @@ -525,6 +567,36 @@ macro_rules! unsafe_impl_known_layout { }}; } +macro_rules! impl_initialize_into_bytes { + ($(const $constvar:ident : $constty:ty, $tyvar:ident $(: ?$optbound:ident)? => $ty:ty),* $(,)?) => { + $(impl_initialize_into_bytes!(@inner const $constvar: $constty, $tyvar $(: ?$optbound)? => $ty);)* + }; + ($($tyvar:ident $(: ?$optbound:ident)? => $ty:ty),* $(,)?) => { + $(impl_initialize_into_bytes!(@inner , $tyvar $(: ?$optbound)? => $ty);)* + }; + ($($(#[$attrs:meta])* $ty:ty),*) => { $(impl_initialize_into_bytes!(@inner , => $(#[$attrs])* $ty);)* }; + (@inner $(const $constvar:ident : $constty:ty)? , $($tyvar:ident $(: ?$optbound:ident)?)? => $(#[$attrs:meta])* $ty:ty) => { + const _: () = { + #[allow(non_local_definitions)] + $(#[$attrs])* + // SAFETY: TODO + unsafe impl<$($tyvar $(: ?$optbound)?)? $(, const $constvar : $constty)?> InitializeIntoBytes for $ty + where + Self: IntoBytes + { + #[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() where Self: Sized {} + + #[inline(always)] + fn initialize_padding(_: Ptr<'_, Self, (invariant::Exclusive, invariant::Unaligned, invariant::AsInitialized)>) { + // By invariant on `Self: IntoBytes`, values of type `Self` never contain padding. + } + } + }; + }; +} + /// Uses `align_of` to confirm that a type or set of types have alignment 1. /// /// Note that `align_of` requires `T: Sized`, so this macro doesn't work for @@ -740,7 +812,7 @@ macro_rules! unsafe_impl_for_transparent_wrapper { ($vis:vis T $(: ?$optbound:ident)? => $wrapper:ident) => {{ crate::util::macros::__unsafe(); - use crate::pointer::{TransmuteFrom, cast::{CastExact, TransitiveProject}, SizeEq, invariant::Valid}; + use crate::pointer::{TransmuteFrom, cast::{CastExact, TransitiveProject}, SizeEq, invariant::{AsInitialized, Valid}}; use crate::wrappers::ReadOnly; // SAFETY: The caller promises that `T` and `$wrapper` have the same @@ -748,6 +820,11 @@ macro_rules! unsafe_impl_for_transparent_wrapper { unsafe impl TransmuteFrom for $wrapper {} // SAFETY: See previous safety comment. unsafe impl TransmuteFrom<$wrapper, Valid, Valid> for T {} + // SAFETY: See previous safety comment. + unsafe impl TransmuteFrom for $wrapper {} + // SAFETY: See previous safety comment. + unsafe impl TransmuteFrom<$wrapper, AsInitialized, AsInitialized> for T {} + // SAFETY: The caller promises that a `T` to `$wrapper` cast is // size-preserving. define_cast!(unsafe { $vis CastToWrapper = T => $wrapper }); diff --git a/src/util/mod.rs b/src/util/mod.rs index 4016f8f048..3efa326271 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -656,6 +656,26 @@ mod len_of { pub(crate) use len_of::MetadataOf; +#[doc(hidden)] +#[inline(always)] +pub const fn sort_fields(mut arr: [(usize, usize); N]) -> [(usize, usize); N] { + let mut i = 0; + while i < N { + let mut j = 0; + while j + 1 < N - i { + if arr[j].0 > arr[j + 1].0 { + let temp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = temp; + } + j += 1; + } + i += 1; + } + + arr +} + /// Since we support multiple versions of Rust, there are often features which /// have been stabilized in the most recent stable release which do not yet /// exist (stably) on our MSRV. This module provides polyfills for those diff --git a/src/wrappers.rs b/src/wrappers.rs index f3930eb7eb..395c6d605b 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -9,7 +9,10 @@ use core::{fmt, hash::Hash}; use super::*; -use crate::pointer::{invariant::Valid, SizeEq, TransmuteFrom}; +use crate::pointer::{ + invariant::{AsInitialized, Valid}, + SizeEq, TransmuteFrom, +}; /// A type with no alignment requirement. /// @@ -153,6 +156,14 @@ const _: () = unsafe { ); impl_or_verify!(T: FromZeros => FromZeros for Unalign); impl_or_verify!(T: FromBytes => FromBytes for Unalign); + impl_or_verify!( + T: InitializeIntoBytes => InitializeIntoBytes for Unalign; + |c| { + let _ = c; + // TODO + //T::initialize_padding(c.transmute::() + } + ); impl_or_verify!(T: IntoBytes => IntoBytes for Unalign); }; @@ -636,6 +647,14 @@ mod read_only_def { } impl ReadOnly { + /// TODO + #[inline(always)] + pub fn from_mut(t: &mut T) -> &mut ReadOnly { + let ptr = crate::Ptr::from_mut(t).transmute::(); + let ptr = unsafe { ptr.assume_alignment() }; + ptr.as_mut() + } + #[inline(always)] pub(crate) fn as_mut(r: &mut ReadOnly) -> &mut T { // SAFETY: `r: &mut ReadOnly`, so this doesn't violate the invariant @@ -675,6 +694,10 @@ const _: () = unsafe { ); unsafe_impl!(T: ?Sized + FromZeros => FromZeros for ReadOnly); unsafe_impl!(T: ?Sized + FromBytes => FromBytes for ReadOnly); + unsafe_impl!(T: ?Sized + InitializeIntoBytes => InitializeIntoBytes for ReadOnly; |slf| { + // TODO: Fix alignment. + T::initialize_padding(slf.transmute()); + }); unsafe_impl!(T: ?Sized + IntoBytes => IntoBytes for ReadOnly); }; @@ -713,6 +736,14 @@ unsafe impl TransmuteFrom for ReadOnly {} // it has the same bit validity as `T`. unsafe impl TransmuteFrom, Valid, Valid> for T {} +// SAFETY: `ReadOnly` is a `#[repr(transparent)]` wrapper around `T`, and so +// it has the same bit validity as `T`. +unsafe impl TransmuteFrom for ReadOnly {} + +// SAFETY: `ReadOnly` is a `#[repr(transparent)]` wrapper around `T`, and so +// it has the same bit validity as `T`. +unsafe impl TransmuteFrom, AsInitialized, AsInitialized> for T {} + impl<'a, T: ?Sized + Immutable> From<&'a T> for &'a ReadOnly { #[inline(always)] fn from(t: &'a T) -> &'a ReadOnly { diff --git a/zerocopy-derive/src/derive/initialize_into_bytes.rs b/zerocopy-derive/src/derive/initialize_into_bytes.rs new file mode 100644 index 0000000000..ae86c24961 --- /dev/null +++ b/zerocopy-derive/src/derive/initialize_into_bytes.rs @@ -0,0 +1,162 @@ +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{Data, DataEnum, DataStruct, DataUnion, Error, Type}; + +use crate::{ + repr::{EnumRepr, StructUnionRepr}, + util::{ + generate_tag_enum, Ctx, DataExt, FieldBounds, ImplBlockBuilder, PaddingCheck, Trait, + TraitBound, + }, + SelfBounds, +}; + +pub(crate) fn derive_initialize_into_bytes( + ctx: &Ctx, + top_level: Trait, +) -> Result { + try_gen_trivial_initialize_into_bytes(ctx, top_level).map(Ok).unwrap_or_else(|| { + match &ctx.ast.data { + Data::Struct(strct) => derive_initialize_into_bytes_struct(ctx, strct), + Data::Enum(enm) => derive_initialize_into_bytes_enum(ctx, enm), + Data::Union(unn) => derive_initialize_into_bytes_union(ctx, unn), + } + }) +} + +fn try_gen_trivial_initialize_into_bytes( + ctx: &Ctx, + top_level: Trait, +) -> Option { + // If the top-level trait is `IntoBytes`, `IntoBytes` derive will fail + // compilation if `Self` is not actually soundly `IntoBytes`, and so we can + // rely on that for our `zeroize` impl. It's plausible that we could + // make changes - or Rust could make changes (such as the "trivial bounds" + // language feature) - that make this no longer true. To hedge against + // these, we include an explicit `Self: IntoBytes` check in the generated + // `is_bit_valid`, which is bulletproof. + // + // If `ctx.skip_on_error` is true, we can't rely on the `IntoBytes` derive + // to fail compilation if `Self` is not actually soundly `IntoBytes`. + if matches!(top_level, Trait::IntoBytes) + && ctx.ast.generics.params.is_empty() + && !ctx.skip_on_error + { + let zerocopy_crate = &ctx.zerocopy_crate; + let core = ctx.core_path(); + + Some( + ImplBlockBuilder::new( + ctx, + &ctx.ast.data, + Trait::InitializeIntoBytes, + FieldBounds::ALL_SELF, + ) + .self_type_trait_bounds(SelfBounds::All(&[Trait::IntoBytes])) + .inner_extras(quote!( + // SAFETY: See inline. + #[inline(always)] + fn initialize_padding(ptr: #zerocopy_crate::Ptr<'_, Self, ( + #zerocopy_crate::invariant::Exclusive, + #zerocopy_crate::invariant::Unaligned, + #zerocopy_crate::invariant::AsInitialized)> + ) { + if false { + fn assert_is_into_bytes() + where + T: #zerocopy_crate::IntoBytes, + T: ?#core::marker::Sized, + { + } + + assert_is_into_bytes::(); + } + } + )) + .build(), + ) + } else { + None + } +} + +fn derive_initialize_into_bytes_struct( + ctx: &Ctx, + strct: &DataStruct, +) -> Result { + let zerocopy_crate = &ctx.zerocopy_crate; + let core: TokenStream = ctx.core_path(); + + // TODO: This is just the default-repr sized case. We also need a + + let field_offsets_and_layouts = ctx.ast.data.fields().into_iter().map( + |(_, name, ty)| quote!((#core::mem::offset_of!(Self, #name), #core::mem::size_of::<#ty>())), + ); + + let subfield_zeroizations = ctx.ast.data.fields().into_iter().map(|(_, name, ty)| { + quote! {{ + // TODO: Need to also emit `AsInitialized`/`Valid` projection impls + // either here or in `TryFromBytes`. + let field = #zerocopy_crate::into_inner!(ptr.reborrow().project::< + _, + { #zerocopy_crate::STRUCT_VARIANT_ID }, + { #zerocopy_crate::ident_id!(#name) } + >()); + <#ty as #zerocopy_crate::InitializeIntoBytes>::initialize_padding(field); + }} + }); + + Ok(ImplBlockBuilder::new(ctx, &ctx.ast.data, Trait::InitializeIntoBytes, FieldBounds::ALL_SELF) + .self_type_trait_bounds(SelfBounds::All(&[Trait::Sized, Trait::TryFromBytes])) + .inner_extras(quote!( + // SAFETY: See inline. TODO + #[inline(always)] + fn initialize_padding(mut ptr: #zerocopy_crate::Ptr<'_, Self, ( + #zerocopy_crate::invariant::Exclusive, + #zerocopy_crate::invariant::Unaligned, + #zerocopy_crate::invariant::AsInitialized)> + ) { + let mut fields = &#zerocopy_crate::util::sort_fields([ + #(#field_offsets_and_layouts,)* + ])[..]; + + let mut start = 0; + + // Zeroize padding between fields. + while let [(offset, size), rest @ ..] = fields { + fields = rest; + + // Zero-out any padding between `start` and the field. + { + let ptr = ptr.as_inner().as_non_null().as_ptr() as *mut _ as *mut u8; + let ptr = unsafe { ptr.add(start) }; + unsafe { #core::ptr::write_bytes(ptr, 0, *offset - start) }; + } + + // Advance `start`. + start += size; + } + + // Zeroize padding after the trailing field. + { + let ptr = ptr.as_inner().as_non_null().as_ptr() as *mut _ as *mut u8; + let ptr = unsafe { ptr.add(start) }; + unsafe { #core::ptr::write_bytes(ptr, 0, #core::mem::size_of::() - start) }; + } + + // Zeroize padding inside fields. + #(#subfield_zeroizations)* + } + )) + .build()) +} + +fn derive_initialize_into_bytes_enum(ctx: &Ctx, enm: &DataEnum) -> Result { + // TODO: Can this be outside the MVP? + todo!() +} + +fn derive_initialize_into_bytes_union(ctx: &Ctx, unn: &DataUnion) -> Result { + // TODO: Can this be outside the MVP? + todo!() +} diff --git a/zerocopy-derive/src/derive/into_bytes.rs b/zerocopy-derive/src/derive/into_bytes.rs index 8c1e1009dd..df1f7a3a26 100644 --- a/zerocopy-derive/src/derive/into_bytes.rs +++ b/zerocopy-derive/src/derive/into_bytes.rs @@ -3,18 +3,21 @@ use quote::quote; use syn::{Data, DataEnum, DataStruct, DataUnion, Error, Type}; use crate::{ + derive::initialize_into_bytes::derive_initialize_into_bytes, repr::{EnumRepr, StructUnionRepr}, util::{ generate_tag_enum, Ctx, DataExt, FieldBounds, ImplBlockBuilder, PaddingCheck, Trait, TraitBound, }, }; -pub(crate) fn derive_into_bytes(ctx: &Ctx, _top_level: Trait) -> Result { - match &ctx.ast.data { +pub(crate) fn derive_into_bytes(ctx: &Ctx, top_level: Trait) -> Result { + let initialize_into_bytes = derive_initialize_into_bytes(ctx, top_level)?; + let into_bytes = match &ctx.ast.data { Data::Struct(strct) => derive_into_bytes_struct(ctx, strct), Data::Enum(enm) => derive_into_bytes_enum(ctx, enm), Data::Union(unn) => derive_into_bytes_union(ctx, unn), - } + }?; + Ok(IntoIterator::into_iter([initialize_into_bytes, into_bytes]).collect()) } fn derive_into_bytes_struct(ctx: &Ctx, strct: &DataStruct) -> Result { let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?; diff --git a/zerocopy-derive/src/derive/mod.rs b/zerocopy-derive/src/derive/mod.rs index a3d066ed2b..7c0d382a74 100644 --- a/zerocopy-derive/src/derive/mod.rs +++ b/zerocopy-derive/src/derive/mod.rs @@ -1,4 +1,5 @@ pub mod from_bytes; +pub mod initialize_into_bytes; pub mod into_bytes; pub mod known_layout; pub mod try_from_bytes; diff --git a/zerocopy-derive/src/derive/try_from_bytes.rs b/zerocopy-derive/src/derive/try_from_bytes.rs index 4e36ab40bc..2266dc0d0a 100644 --- a/zerocopy-derive/src/derive/try_from_bytes.rs +++ b/zerocopy-derive/src/derive/try_from_bytes.rs @@ -514,44 +514,57 @@ fn derive_has_field_struct_union(ctx: &Ctx, data: &dyn DataExt) -> TokenStream { // allocation. unsafe { #core::ptr::addr_of_mut!((*slf).#ident) } } - }).outer_extras(if matches!(&ctx.ast.data, Data::Struct(..)) { - let fields_preserve_alignment = StructUnionRepr::from_attrs(&ctx.ast.attrs) - .map(|repr| repr.get_packed().is_none()) - .unwrap(); - let alignment = if fields_preserve_alignment { - quote! { Alignment } - } else { - quote! { #zerocopy_crate::invariant::Unaligned } - }; - // SAFETY: See comments on items. - ImplBlockBuilder::new( - ctx, - data, - Trait::ProjectField { - variant_id: variant_id.clone(), - field: field.clone(), - field_id: field_id.clone(), - invariants: parse_quote!((Aliasing, Alignment, #zerocopy_crate::invariant::Initialized)), - }, - FieldBounds::None, - ) - .param_extras(vec![ - parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing), - parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment), - ]) - .inner_extras(quote! { - // SAFETY: Projection into structs is always infallible. - type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible; - // SAFETY: The alignment of the projected `Ptr` is `Unaligned` - // if the structure is packed; otherwise inherited from the - // outer `Ptr`. If the validity of the outer pointer is - // `Initialized`, so too is the validity of its fields. - type Invariants = (Aliasing, #alignment, #zerocopy_crate::invariant::Initialized); }) - .build() - } else { - quote! {} - }) + .outer_extras(if matches!(&ctx.ast.data, Data::Struct(..)) { + let fields_preserve_alignment = StructUnionRepr::from_attrs(&ctx.ast.attrs) + .map(|repr| repr.get_packed().is_none()) + .unwrap(); + let alignment = if fields_preserve_alignment { + quote! { Alignment } + } else { + quote! { #zerocopy_crate::invariant::Unaligned } + }; + // SAFETY: Must be invoked with `Initialized` or + // `AsInitialized`. See comments on items. + let impl_project_field = + |validity| ImplBlockBuilder::new( + ctx, + data, + Trait::ProjectField { + variant_id: variant_id.clone(), + field: field.clone(), + field_id: field_id.clone(), + invariants: parse_quote!((Aliasing, Alignment, #validity)), + }, + FieldBounds::None, + ) + .param_extras(vec![ + parse_quote!(Aliasing: #zerocopy_crate::invariant::Aliasing), + parse_quote!(Alignment: #zerocopy_crate::invariant::Alignment), + ]) + .inner_extras(quote! { + // SAFETY: Projection into structs is always infallible. + type Error = #zerocopy_crate::util::macro_util::core_reexport::convert::Infallible; + // SAFETY: The alignment of the projected `Ptr` is + // `Unaligned` if the structure is packed; otherwise + // inherited from the outer `Ptr`. If the validity of the + // outer pointer is `Initialized` or `AsInitialized`, so too + // is the validity of its fields. + type Invariants = (Aliasing, #alignment, #validity); + }) + .build(); + // SAFETY: Invoked with `Initialized`. + let project_initialized = impl_project_field(quote!(#zerocopy_crate::invariant::Initialized)); + // SAFETY: Invoked with `AsInitialized`. + let project_as_initialized = impl_project_field(quote!(#zerocopy_crate::invariant::AsInitialized)); + quote!{ + #project_initialized + #project_as_initialized + } + } else { + quote! {} + } + ) .build() }); diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index a1d10a2ada..4fa82800a4 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -121,6 +121,7 @@ derive!(Immutable => derive_immutable => crate::derive::derive_immutable); derive!(TryFromBytes => derive_try_from_bytes => crate::derive::try_from_bytes::derive_try_from_bytes); derive!(FromZeros => derive_from_zeros => crate::derive::from_bytes::derive_from_zeros); derive!(FromBytes => derive_from_bytes => crate::derive::from_bytes::derive_from_bytes); +derive!(InitializeIntoBytes => derive_initialize_into_bytes => crate::derive::initialize_into_bytes::derive_initialize_into_bytes); derive!(IntoBytes => derive_into_bytes => crate::derive::into_bytes::derive_into_bytes); derive!(Unaligned => derive_unaligned => crate::derive::unaligned::derive_unaligned); derive!(ByteHash => derive_hash => crate::derive::derive_hash); diff --git a/zerocopy-derive/src/util.rs b/zerocopy-derive/src/util.rs index 4ec28bf957..17cfc82f66 100644 --- a/zerocopy-derive/src/util.rs +++ b/zerocopy-derive/src/util.rs @@ -149,6 +149,8 @@ pub(crate) trait DataExt { fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)>; fn tag(&self) -> Option; + + fn is_struct(&self) -> bool; } impl DataExt for Data { @@ -175,6 +177,10 @@ impl DataExt for Data { Data::Union(un) => un.tag(), } } + + fn is_struct(&self) -> bool { + matches!(self, Data::Struct(_)) + } } impl DataExt for DataStruct { @@ -189,6 +195,10 @@ impl DataExt for DataStruct { fn tag(&self) -> Option { None } + + fn is_struct(&self) -> bool { + true + } } impl DataExt for DataEnum { @@ -203,6 +213,10 @@ impl DataExt for DataEnum { fn tag(&self) -> Option { Some(Ident::new("___ZerocopyTag", Span::call_site())) } + + fn is_struct(&self) -> bool { + false + } } impl DataExt for DataUnion { @@ -217,6 +231,10 @@ impl DataExt for DataUnion { fn tag(&self) -> Option { None } + + fn is_struct(&self) -> bool { + false + } } fn map_fields<'a>( @@ -309,6 +327,7 @@ pub(crate) enum Trait { TryFromBytes, FromZeros, FromBytes, + InitializeIntoBytes, IntoBytes, Unaligned, Sized, @@ -337,6 +356,7 @@ impl ToTokens for Trait { Trait::TryFromBytes => "TryFromBytes", Trait::FromZeros => "FromZeros", Trait::FromBytes => "FromBytes", + Trait::InitializeIntoBytes => "InitializeIntoBytes", Trait::IntoBytes => "IntoBytes", Trait::Unaligned => "Unaligned", Trait::Sized => "Sized", @@ -359,6 +379,7 @@ impl ToTokens for Trait { | Trait::FromZeros | Trait::FromBytes | Trait::IntoBytes + | Trait::InitializeIntoBytes | Trait::Unaligned | Trait::Sized | Trait::ByteHash