diff --git a/src/byteorder.rs b/src/byteorder.rs index 2769410451e..949f015173f 100644 --- a/src/byteorder.rs +++ b/src/byteorder.rs @@ -74,6 +74,9 @@ use core::{ num::TryFromIntError, }; +// Ensures proc-macro derive works in the context of this crate. +use crate as zerocopy; + // We don't reexport `WriteBytesExt` or `ReadBytesExt` because those are only // available with the `std` feature enabled, and zerocopy is `no_std` by // default. diff --git a/src/lib.rs b/src/lib.rs index c42584d7dca..8df78d7a603 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -225,7 +225,7 @@ #![cfg_attr(feature = "simd-nightly", feature(stdsimd))] #![cfg_attr( __INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS, - feature(layout_for_ptr, strict_provenance) + feature(const_size_of_val_raw, layout_for_ptr, strict_provenance) )] #[macro_use] mod macros; @@ -427,6 +427,55 @@ impl DstLayout { } } + /// Constructs a `DstLayout` which describes a dynamically-sized type with + /// the given properties. + /// + /// # Safety + /// + /// Unsafe code may assume that the returned `DstLayout` is the correct + /// layout for the described type so long as: + /// - The type has the alignment `align` + /// - The type has at least one field + /// - The byte offset of the first byte of the trailing field is equal to + /// `trailing_field_offset` + /// - The trailing field's layout is correctly described by + /// `trailing_field_layout` + #[doc(hidden)] + #[inline] + pub const fn for_dst( + align: NonZeroUsize, + trailing_field_offset: usize, + trailing_field_layout: DstLayout, + ) -> Option { + Some(DstLayout { + // SAFETY: The caller has promised that this is the correct + // alignment. + _align: align, + _size_info: match trailing_field_layout._size_info { + SizeInfo::Sized { _size } => { + let without_padding = match trailing_field_offset.checked_add(_size) { + Some(without_padding) => without_padding, + None => return None, + }; + + let padding = util::core_layout::_padding_needed_for(without_padding, align); + // SAFETY: TODO + #[allow(clippy::arithmetic_side_effects)] + SizeInfo::Sized { _size: without_padding + padding } + } + SizeInfo::SliceDst(TrailingSliceLayout { _offset, _elem_size }) => { + let offset = match trailing_field_offset.checked_add(_offset) { + Some(offset) => offset, + None => return None, + }; + + // SAFETY: TODO + SizeInfo::SliceDst(TrailingSliceLayout { _offset: offset, _elem_size }) + } + }, + }) + } + /// Validates that a cast is sound from a layout perspective. /// /// Validates that the size and alignment requirements of a type with the @@ -3684,6 +3733,20 @@ mod tests { } } + #[test] + fn test_dst_layout_for_dst() { + macro_rules! test { + ($align:expr, $trailing_field_offset:expr, $trailing_field_layout:expr, $expect:expr) => {{ + let align = NonZeroUsize::new($align).unwrap(); + let layout = + DstLayout::for_dst(align, $trailing_field_offset, $trailing_field_layout); + assert_eq!(layout, $expect); + }}; + } + + // TODO: Write tests. + } + // This test takes a long time when running under Miri, so we skip it in // that case. This is acceptable because this is a logic test that doesn't // attempt to expose UB. @@ -5366,7 +5429,7 @@ mod tests { assert_impls!(Wrapping: KnownLayout, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); assert_impls!(Unalign: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned); - assert_impls!(Unalign: KnownLayout, Unaligned, !FromZeroes, !FromBytes, !AsBytes); + assert_impls!(Unalign: Unaligned, !FromZeroes, !FromBytes, !AsBytes); assert_impls!([u8]: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned); assert_impls!([NotZerocopy]: !KnownLayout, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); diff --git a/src/macro_util.rs b/src/macro_util.rs index 574cb2b17a2..ac058fe8b83 100644 --- a/src/macro_util.rs +++ b/src/macro_util.rs @@ -66,7 +66,7 @@ impl MaxAlignsOf { } } -const _64K: usize = 1 << 16; +pub const _64K: usize = 1 << 16; // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this // `cfg` when `size_of_val_raw` is stabilized. @@ -115,7 +115,7 @@ pub const ALIGNED_64K_ALLOCATION: NonNull<[u8]> = { #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. #[macro_export] macro_rules! trailing_field_offset { - ($ty:ty, $trailing_field_name:tt) => {{ + ($ty:ty, $trailing_field_name:tt $(, $oversized_message:literal)?) => {{ let min_size = { let zero_elems: *const [()] = $crate::macro_util::core_reexport::ptr::slice_from_raw_parts( @@ -146,10 +146,10 @@ macro_rules! trailing_field_offset { } }; - assert!(min_size <= _64K); + assert!(min_size <= $crate::macro_util::_64K $(, $oversized_message:literal)?); #[allow(clippy::as_conversions)] - let ptr = ALIGNED_64K_ALLOCATION.as_ptr() as *const $ty; + let ptr = $crate::macro_util::ALIGNED_64K_ALLOCATION.as_ptr() as *const $ty; // SAFETY: // - Thanks to the preceding `assert!`, we know that the value with zero @@ -173,7 +173,7 @@ macro_rules! trailing_field_offset { // The size of a value is always a multiple of its alignment. // // [2] https://github.com/rust-lang/reference/pull/1387 - let field = unsafe { + let field: *const _ = unsafe { $crate::macro_util::core_reexport::ptr::addr_of!((*ptr).$trailing_field_name) }; // SAFETY: @@ -196,12 +196,10 @@ macro_rules! trailing_field_offset { // Guaranteed not to be lossy: `field` comes after `ptr`, so the offset // from `ptr` to `field` is guaranteed to be positive. assert!(offset >= 0); - Some( - #[allow(clippy::as_conversions)] - { - offset as usize - }, - ) + #[allow(clippy::as_conversions)] + { + offset as usize + } }}; } @@ -214,7 +212,7 @@ macro_rules! trailing_field_offset { #[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. #[macro_export] macro_rules! align_of { - ($ty:ty) => {{ + ($ty:ty $(, $oversized_message:literal)?) => {{ // SAFETY: `OffsetOfTrailingIsAlignment` is `repr(C)`, and its layout is // guaranteed [1] to begin with the single-byte layout for `_byte`, // followed by the padding needed to align `_trailing`, then the layout @@ -236,15 +234,60 @@ macro_rules! align_of { // [1]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprc #[repr(C)] - struct OffsetOfTrailingIsAlignment { + struct OffsetOfTrailingIsAlignment { _byte: u8, - _trailing: $ty, + _trailing: T, } - trailing_field_offset!(OffsetOfTrailingIsAlignment, _trailing) + $crate::trailing_field_offset!( + OffsetOfTrailingIsAlignment<$ty>, + _trailing + $(, $oversized_message:literal)? + ) }}; } +/// Constructs the `DstLayout` of a given `$ty` that (optionally) has a +/// `$trailing_field_name`. If `$ty` is a struct and `$trailing_field_name` is +/// provided, this macro will compile successfully even if `$ty` is unsized +/// (otherwise it will not). If `$ty` is sized, then `$trailing_field_name` may +/// be omitted. +// TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this +// `cfg` and the subsequent fallback when `size_of_val_raw` is stabilized. +#[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] +#[macro_export] +macro_rules! dst_layout { + ($ty:ty) => { + $crate::DstLayout::for_type::<$ty>() + }; + ($ty:ty, $trailing_field_name:tt: $trailing_field_ty:ty) => {{ + let align = $crate::align_of!($ty); + let align = match $crate::macro_util::core_reexport::num::NonZeroUsize::new(align) { + Some(align) => align, + None => unreachable!(), + }; + let maybe_layout = $crate::DstLayout::for_dst( + align, + $crate::trailing_field_offset!($ty, $trailing_field_name), + <$trailing_field_ty as $crate::KnownLayout>::LAYOUT, + ); + match maybe_layout { + Some(layout) => layout, + None => panic!(concat!("Could not compute layout of `", stringify!($ty), "`.")), + } + }}; +} +#[cfg(not(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS))] +#[macro_export] +macro_rules! dst_layout { + ($ty:ty) => { + $crate::DstLayout::for_type::<$ty>() + }; + ($ty:ty, $_trailing_field_name:tt: $_trailing_field_ty:ty) => { + $crate::dst_layout!($ty) + }; +} + /// Does the struct type `$t` have padding? /// /// `$ts` is the list of the type of every field in `$t`. `$t` must be a @@ -402,6 +445,17 @@ pub mod core_reexport { pub mod mem { pub use core::mem::*; + + // TODO(#29), TODO(https://github.com/rust-lang/rust/issues/69835): Remove this + // `cfg` and the subsequent fallback when `size_of_val_raw` is stabilized. + #[cfg(__INTERNAL_USE_ONLY_NIGHLTY_FEATURES_IN_TESTS)] + #[inline(always)] + pub const unsafe fn size_of_val_raw(val: *const T) -> usize + where + T: ?Sized, + { + unsafe { core::mem::size_of_val_raw(val) } + } } } @@ -484,83 +538,83 @@ mod tests { (@offset $_t:ty ; $_trailing:ty) => { trailing_field_offset!(Test, 1) }; } - test!(#[repr(C)] #[repr(transparent)] #[repr(packed)](; u8) => Some(0)); - test!(#[repr(C)] #[repr(transparent)] #[repr(packed)](; [u8]) => Some(0)); - test!(#[repr(C)] #[repr(packed)] (u8; u8) => Some(1)); - test!(#[repr(C)] (; AU64) => Some(0)); - test!(#[repr(C)] (; [AU64]) => Some(0)); - test!(#[repr(C)] (u8; AU64) => Some(8)); - test!(#[repr(C)] (u8; [AU64]) => Some(8)); - test!(#[repr(C)] (; Nested) => Some(0)); - test!(#[repr(C)] (; Nested) => Some(0)); - test!(#[repr(C)] (u8; Nested) => Some(8)); - test!(#[repr(C)] (u8; Nested) => Some(8)); + test!(#[repr(C)] #[repr(transparent)] #[repr(packed)](; u8) => 0); + test!(#[repr(C)] #[repr(transparent)] #[repr(packed)](; [u8]) => 0); + test!(#[repr(C)] #[repr(packed)] (u8; u8) => 1); + test!(#[repr(C)] (; AU64) => 0); + test!(#[repr(C)] (; [AU64]) => 0); + test!(#[repr(C)] (u8; AU64) => 8); + test!(#[repr(C)] (u8; [AU64]) => 8); + test!(#[repr(C)] (; Nested) => 0); + test!(#[repr(C)] (; Nested) =>0); + test!(#[repr(C)] (u8; Nested) => 8); + test!(#[repr(C)] (u8; Nested) => 8); // Test that `packed(N)` limits the offset of the trailing field. - test!(#[repr(C, packed( 1))] (u8; elain::Align< 2>) => Some( 1)); - test!(#[repr(C, packed( 2))] (u8; elain::Align< 4>) => Some( 2)); - test!(#[repr(C, packed( 4))] (u8; elain::Align< 8>) => Some( 4)); - test!(#[repr(C, packed( 8))] (u8; elain::Align< 16>) => Some( 8)); - test!(#[repr(C, packed( 16))] (u8; elain::Align< 32>) => Some( 16)); - test!(#[repr(C, packed( 32))] (u8; elain::Align< 64>) => Some( 32)); - test!(#[repr(C, packed( 64))] (u8; elain::Align< 128>) => Some( 64)); - test!(#[repr(C, packed( 128))] (u8; elain::Align< 256>) => Some( 128)); - test!(#[repr(C, packed( 256))] (u8; elain::Align< 512>) => Some( 256)); - test!(#[repr(C, packed( 512))] (u8; elain::Align< 1024>) => Some( 512)); - test!(#[repr(C, packed( 1024))] (u8; elain::Align< 2048>) => Some( 1024)); - test!(#[repr(C, packed( 2048))] (u8; elain::Align< 4096>) => Some( 2048)); - test!(#[repr(C, packed( 4096))] (u8; elain::Align< 8192>) => Some( 4096)); - test!(#[repr(C, packed( 8192))] (u8; elain::Align< 16384>) => Some( 8192)); - test!(#[repr(C, packed( 16384))] (u8; elain::Align< 32768>) => Some( 16384)); - test!(#[repr(C, packed( 32768))] (u8; elain::Align< 65536>) => Some( 32768)); - test!(#[repr(C, packed( 65536))] (u8; elain::Align< 131072>) => Some( 65536)); + test!(#[repr(C, packed( 1))] (u8; elain::Align< 2>) => 1); + test!(#[repr(C, packed( 2))] (u8; elain::Align< 4>) => 2); + test!(#[repr(C, packed( 4))] (u8; elain::Align< 8>) => 4); + test!(#[repr(C, packed( 8))] (u8; elain::Align< 16>) => 8); + test!(#[repr(C, packed( 16))] (u8; elain::Align< 32>) => 16); + test!(#[repr(C, packed( 32))] (u8; elain::Align< 64>) => 32); + test!(#[repr(C, packed( 64))] (u8; elain::Align< 128>) => 64); + test!(#[repr(C, packed( 128))] (u8; elain::Align< 256>) => 128); + test!(#[repr(C, packed( 256))] (u8; elain::Align< 512>) => 256); + test!(#[repr(C, packed( 512))] (u8; elain::Align< 1024>) => 512); + test!(#[repr(C, packed( 1024))] (u8; elain::Align< 2048>) => 1024); + test!(#[repr(C, packed( 2048))] (u8; elain::Align< 4096>) => 2048); + test!(#[repr(C, packed( 4096))] (u8; elain::Align< 8192>) => 4096); + test!(#[repr(C, packed( 8192))] (u8; elain::Align< 16384>) => 8192); + test!(#[repr(C, packed( 16384))] (u8; elain::Align< 32768>) => 16384); + test!(#[repr(C, packed( 32768))] (u8; elain::Align< 65536>) => 32768); + test!(#[repr(C, packed( 65536))] (u8; elain::Align< 131072>) => 65536); /* Alignments above 65536 are not yet supported. - test!(#[repr(C, packed( 131072))] (u8; elain::Align< 262144>) => Some( 131072)); - test!(#[repr(C, packed( 262144))] (u8; elain::Align< 524288>) => Some( 262144)); - test!(#[repr(C, packed( 524288))] (u8; elain::Align< 1048576>) => Some( 524288)); - test!(#[repr(C, packed( 1048576))] (u8; elain::Align< 2097152>) => Some( 1048576)); - test!(#[repr(C, packed( 2097152))] (u8; elain::Align< 4194304>) => Some( 2097152)); - test!(#[repr(C, packed( 4194304))] (u8; elain::Align< 8388608>) => Some( 4194304)); - test!(#[repr(C, packed( 8388608))] (u8; elain::Align< 16777216>) => Some( 8388608)); - test!(#[repr(C, packed( 16777216))] (u8; elain::Align< 33554432>) => Some( 16777216)); - test!(#[repr(C, packed( 33554432))] (u8; elain::Align< 67108864>) => Some( 33554432)); - test!(#[repr(C, packed( 67108864))] (u8; elain::Align< 33554432>) => Some( 67108864)); - test!(#[repr(C, packed( 33554432))] (u8; elain::Align<134217728>) => Some( 33554432)); - test!(#[repr(C, packed(134217728))] (u8; elain::Align<268435456>) => Some(134217728)); - test!(#[repr(C, packed(268435456))] (u8; elain::Align<268435456>) => Some(268435456)); + test!(#[repr(C, packed( 131072))] (u8; elain::Align< 262144>) => 131072); + test!(#[repr(C, packed( 262144))] (u8; elain::Align< 524288>) => 262144); + test!(#[repr(C, packed( 524288))] (u8; elain::Align< 1048576>) => 524288); + test!(#[repr(C, packed( 1048576))] (u8; elain::Align< 2097152>) => 1048576); + test!(#[repr(C, packed( 2097152))] (u8; elain::Align< 4194304>) => 2097152); + test!(#[repr(C, packed( 4194304))] (u8; elain::Align< 8388608>) => 4194304); + test!(#[repr(C, packed( 8388608))] (u8; elain::Align< 16777216>) => 8388608); + test!(#[repr(C, packed( 16777216))] (u8; elain::Align< 33554432>) => 16777216); + test!(#[repr(C, packed( 33554432))] (u8; elain::Align< 67108864>) => 33554432); + test!(#[repr(C, packed( 67108864))] (u8; elain::Align< 33554432>) => 67108864); + test!(#[repr(C, packed( 33554432))] (u8; elain::Align<134217728>) => 33554432); + test!(#[repr(C, packed(134217728))] (u8; elain::Align<268435456>) => 134217728); + test!(#[repr(C, packed(268435456))] (u8; elain::Align<268435456>) => 268435456); */ // Test that `align(N)` does not limit the offset of the trailing field. - test!(#[repr(C, align( 1))] (u8; elain::Align< 2>) => Some( 2)); - test!(#[repr(C, align( 2))] (u8; elain::Align< 4>) => Some( 4)); - test!(#[repr(C, align( 4))] (u8; elain::Align< 8>) => Some( 8)); - test!(#[repr(C, align( 8))] (u8; elain::Align< 16>) => Some( 16)); - test!(#[repr(C, align( 16))] (u8; elain::Align< 32>) => Some( 32)); - test!(#[repr(C, align( 32))] (u8; elain::Align< 64>) => Some( 64)); - test!(#[repr(C, align( 64))] (u8; elain::Align< 128>) => Some( 128)); - test!(#[repr(C, align( 128))] (u8; elain::Align< 256>) => Some( 256)); - test!(#[repr(C, align( 256))] (u8; elain::Align< 512>) => Some( 512)); - test!(#[repr(C, align( 512))] (u8; elain::Align< 1024>) => Some( 1024)); - test!(#[repr(C, align( 1024))] (u8; elain::Align< 2048>) => Some( 2048)); - test!(#[repr(C, align( 2048))] (u8; elain::Align< 4096>) => Some( 4096)); - test!(#[repr(C, align( 4096))] (u8; elain::Align< 8192>) => Some( 8192)); - test!(#[repr(C, align( 8192))] (u8; elain::Align< 16384>) => Some( 16384)); - test!(#[repr(C, align( 16384))] (u8; elain::Align< 32768>) => Some( 32768)); - test!(#[repr(C, align( 32768))] (u8; elain::Align< 65536>) => Some( 65536)); + test!(#[repr(C, align( 1))] (u8; elain::Align< 2>) => 2); + test!(#[repr(C, align( 2))] (u8; elain::Align< 4>) => 4); + test!(#[repr(C, align( 4))] (u8; elain::Align< 8>) => 8); + test!(#[repr(C, align( 8))] (u8; elain::Align< 16>) => 16); + test!(#[repr(C, align( 16))] (u8; elain::Align< 32>) => 32); + test!(#[repr(C, align( 32))] (u8; elain::Align< 64>) => 64); + test!(#[repr(C, align( 64))] (u8; elain::Align< 128>) => 128); + test!(#[repr(C, align( 128))] (u8; elain::Align< 256>) => 256); + test!(#[repr(C, align( 256))] (u8; elain::Align< 512>) => 512); + test!(#[repr(C, align( 512))] (u8; elain::Align< 1024>) => 1024); + test!(#[repr(C, align( 1024))] (u8; elain::Align< 2048>) => 2048); + test!(#[repr(C, align( 2048))] (u8; elain::Align< 4096>) => 4096); + test!(#[repr(C, align( 4096))] (u8; elain::Align< 8192>) => 8192); + test!(#[repr(C, align( 8192))] (u8; elain::Align< 16384>) => 16384); + test!(#[repr(C, align( 16384))] (u8; elain::Align< 32768>) => 32768); + test!(#[repr(C, align( 32768))] (u8; elain::Align< 65536>) => 65536); /* Alignments above 65536 are not yet supported. - test!(#[repr(C, align( 65536))] (u8; elain::Align< 131072>) => Some( 131072)); - test!(#[repr(C, align( 131072))] (u8; elain::Align< 262144>) => Some( 262144)); - test!(#[repr(C, align( 262144))] (u8; elain::Align< 524288>) => Some( 524288)); - test!(#[repr(C, align( 524288))] (u8; elain::Align< 1048576>) => Some( 1048576)); - test!(#[repr(C, align( 1048576))] (u8; elain::Align< 2097152>) => Some( 2097152)); - test!(#[repr(C, align( 2097152))] (u8; elain::Align< 4194304>) => Some( 4194304)); - test!(#[repr(C, align( 4194304))] (u8; elain::Align< 8388608>) => Some( 8388608)); - test!(#[repr(C, align( 8388608))] (u8; elain::Align< 16777216>) => Some( 16777216)); - test!(#[repr(C, align( 16777216))] (u8; elain::Align< 33554432>) => Some( 33554432)); - test!(#[repr(C, align( 33554432))] (u8; elain::Align< 67108864>) => Some( 67108864)); - test!(#[repr(C, align( 67108864))] (u8; elain::Align< 33554432>) => Some( 33554432)); - test!(#[repr(C, align( 33554432))] (u8; elain::Align<134217728>) => Some(134217728)); - test!(#[repr(C, align(134217728))] (u8; elain::Align<268435456>) => Some(268435456)); + test!(#[repr(C, align( 65536))] (u8; elain::Align< 131072>) => 131072); + test!(#[repr(C, align( 131072))] (u8; elain::Align< 262144>) => 262144); + test!(#[repr(C, align( 262144))] (u8; elain::Align< 524288>) => 524288); + test!(#[repr(C, align( 524288))] (u8; elain::Align< 1048576>) => 1048576); + test!(#[repr(C, align( 1048576))] (u8; elain::Align< 2097152>) => 2097152); + test!(#[repr(C, align( 2097152))] (u8; elain::Align< 4194304>) => 4194304); + test!(#[repr(C, align( 4194304))] (u8; elain::Align< 8388608>) => 8388608); + test!(#[repr(C, align( 8388608))] (u8; elain::Align< 16777216>) => 16777216); + test!(#[repr(C, align( 16777216))] (u8; elain::Align< 33554432>) => 33554432); + test!(#[repr(C, align( 33554432))] (u8; elain::Align< 67108864>) => 67108864); + test!(#[repr(C, align( 67108864))] (u8; elain::Align< 33554432>) => 33554432); + test!(#[repr(C, align( 33554432))] (u8; elain::Align<134217728>) => 134217728); + test!(#[repr(C, align(134217728))] (u8; elain::Align<268435456>) => 268435456); */ } @@ -571,37 +625,37 @@ mod tests { #[test] fn test_align_of_dst() { // Test that `align_of!` correctly computes the alignment of DSTs. - assert_eq!(align_of!([elain::Align<1>]), Some(1)); - assert_eq!(align_of!([elain::Align<2>]), Some(2)); - assert_eq!(align_of!([elain::Align<4>]), Some(4)); - assert_eq!(align_of!([elain::Align<8>]), Some(8)); - assert_eq!(align_of!([elain::Align<16>]), Some(16)); - assert_eq!(align_of!([elain::Align<32>]), Some(32)); - assert_eq!(align_of!([elain::Align<64>]), Some(64)); - assert_eq!(align_of!([elain::Align<128>]), Some(128)); - assert_eq!(align_of!([elain::Align<256>]), Some(256)); - assert_eq!(align_of!([elain::Align<512>]), Some(512)); - assert_eq!(align_of!([elain::Align<1024>]), Some(1024)); - assert_eq!(align_of!([elain::Align<2048>]), Some(2048)); - assert_eq!(align_of!([elain::Align<4096>]), Some(4096)); - assert_eq!(align_of!([elain::Align<8192>]), Some(8192)); - assert_eq!(align_of!([elain::Align<16384>]), Some(16384)); - assert_eq!(align_of!([elain::Align<32768>]), Some(32768)); - assert_eq!(align_of!([elain::Align<65536>]), Some(65536)); + assert_eq!(align_of!([elain::Align<1>]), 1); + assert_eq!(align_of!([elain::Align<2>]), 2); + assert_eq!(align_of!([elain::Align<4>]), 4); + assert_eq!(align_of!([elain::Align<8>]), 8); + assert_eq!(align_of!([elain::Align<16>]), 16); + assert_eq!(align_of!([elain::Align<32>]), 32); + assert_eq!(align_of!([elain::Align<64>]), 64); + assert_eq!(align_of!([elain::Align<128>]), 128); + assert_eq!(align_of!([elain::Align<256>]), 256); + assert_eq!(align_of!([elain::Align<512>]), 512); + assert_eq!(align_of!([elain::Align<1024>]), 1024); + assert_eq!(align_of!([elain::Align<2048>]), 2048); + assert_eq!(align_of!([elain::Align<4096>]), 4096); + assert_eq!(align_of!([elain::Align<8192>]), 8192); + assert_eq!(align_of!([elain::Align<16384>]), 16384); + assert_eq!(align_of!([elain::Align<32768>]), 32768); + assert_eq!(align_of!([elain::Align<65536>]), 65536); /* Alignments above 65536 are not yet supported. - assert_eq!(align_of!([elain::Align<131072>]), Some(131072)); - assert_eq!(align_of!([elain::Align<262144>]), Some(262144)); - assert_eq!(align_of!([elain::Align<524288>]), Some(524288)); - assert_eq!(align_of!([elain::Align<1048576>]), Some(1048576)); - assert_eq!(align_of!([elain::Align<2097152>]), Some(2097152)); - assert_eq!(align_of!([elain::Align<4194304>]), Some(4194304)); - assert_eq!(align_of!([elain::Align<8388608>]), Some(8388608)); - assert_eq!(align_of!([elain::Align<16777216>]), Some(16777216)); - assert_eq!(align_of!([elain::Align<33554432>]), Some(33554432)); - assert_eq!(align_of!([elain::Align<67108864>]), Some(67108864)); - assert_eq!(align_of!([elain::Align<33554432>]), Some(33554432)); - assert_eq!(align_of!([elain::Align<134217728>]), Some(134217728)); - assert_eq!(align_of!([elain::Align<268435456>]), Some(268435456)); + assert_eq!(align_of!([elain::Align<131072>]), 131072); + assert_eq!(align_of!([elain::Align<262144>]), 262144); + assert_eq!(align_of!([elain::Align<524288>]), 524288); + assert_eq!(align_of!([elain::Align<1048576>]), 1048576); + assert_eq!(align_of!([elain::Align<2097152>]), 2097152); + assert_eq!(align_of!([elain::Align<4194304>]), 4194304); + assert_eq!(align_of!([elain::Align<8388608>]), 8388608); + assert_eq!(align_of!([elain::Align<16777216>]), 16777216); + assert_eq!(align_of!([elain::Align<33554432>]), 33554432); + assert_eq!(align_of!([elain::Align<67108864>]), 67108864); + assert_eq!(align_of!([elain::Align<33554432>]), 33554432); + assert_eq!(align_of!([elain::Align<134217728>]), 134217728); + assert_eq!(align_of!([elain::Align<268435456>]), 268435456); */ } diff --git a/src/wrappers.rs b/src/wrappers.rs index 532d8729789..9e2179faf3f 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -17,6 +17,9 @@ use core::{ use super::*; +// Allows the proc-macro derives to operate in the context of this file. +use crate::zerocopy; + /// A type with no alignment requirement. /// /// An `Unalign` wraps a `T`, removing any alignment requirement. `Unalign` diff --git a/zerocopy-derive/src/ext.rs b/zerocopy-derive/src/ext.rs index 87cf838f883..b9c04333937 100644 --- a/zerocopy-derive/src/ext.rs +++ b/zerocopy-derive/src/ext.rs @@ -6,12 +6,18 @@ // This file may not be copied, modified, or distributed except according to // those terms. -use syn::{Data, DataEnum, DataStruct, DataUnion, Type}; +use proc_macro2::Span; +use syn::{Data, DataEnum, DataStruct, DataUnion, Member, Type}; pub trait DataExt { /// Extract the types of all fields. For enums, extract the types of fields /// from each variant. fn field_types(&self) -> Vec<&Type>; + + /// Extract the single trailing field of a struct, if any. + fn trailing_field(&self) -> Option<(syn::Member, &Type)> { + None + } } impl DataExt for Data { @@ -22,12 +28,30 @@ impl DataExt for Data { Data::Union(un) => un.field_types(), } } + + fn trailing_field(&self) -> Option<(syn::Member, &Type)> { + match self { + Data::Struct(strc) => strc.trailing_field(), + Data::Enum(enm) => enm.trailing_field(), + Data::Union(un) => un.trailing_field(), + } + } } impl DataExt for DataStruct { fn field_types(&self) -> Vec<&Type> { self.fields.iter().map(|f| &f.ty).collect() } + + fn trailing_field(&self) -> Option<(Member, &Type)> { + let (index, trailing_field) = self.fields.iter().enumerate().last()?; + let member = if let Some(ident) = &trailing_field.ident { + Member::Named(ident.to_owned()) + } else { + Member::Unnamed(syn::Index { index: index as u32, span: Span::mixed_site() }) + }; + Some((member, &trailing_field.ty)) + } } impl DataExt for DataEnum { diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index 9bdc997472f..9f591653df9 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -63,9 +63,23 @@ use {crate::ext::*, crate::repr::*}; pub fn derive_known_layout(ts: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = syn::parse_macro_input!(ts as DeriveInput); match &ast.data { - Data::Struct(strct) => impl_block(&ast, strct, Trait::KnownLayout, false, None), - Data::Enum(enm) => impl_block(&ast, enm, Trait::KnownLayout, false, None), - Data::Union(unn) => impl_block(&ast, unn, Trait::KnownLayout, false, None), + Data::Struct(strct) => { + // A bound on the trailing field is required, since structs are + // unsized if their trailing field is unsized. Reflecting the layout + // of an usized trailing field requires that the field is + // `KnownLayout`. + impl_block(&ast, strct, Trait::KnownLayout, RequireBoundedFields::Trailing, None) + } + Data::Enum(enm) => { + // A bound on the trailing field is not required, since enums cannot + // currently be unsized. + impl_block(&ast, enm, Trait::KnownLayout, RequireBoundedFields::No, None) + } + Data::Union(unn) => { + // A bound on the trailing field is not required, since unions + // cannot currently be unsized. + impl_block(&ast, unn, Trait::KnownLayout, RequireBoundedFields::No, None) + } } .into() } @@ -136,7 +150,7 @@ const STRUCT_UNION_ALLOWED_REPR_COMBINATIONS: &[&[StructRepr]] = &[ // - all fields are `FromZeroes` fn derive_from_zeroes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream { - impl_block(ast, strct, Trait::FromZeroes, true, None) + impl_block(ast, strct, Trait::FromZeroes, RequireBoundedFields::Yes, None) } // An enum is `FromZeroes` if: @@ -170,21 +184,21 @@ fn derive_from_zeroes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::To .to_compile_error(); } - impl_block(ast, enm, Trait::FromZeroes, true, None) + impl_block(ast, enm, Trait::FromZeroes, RequireBoundedFields::Yes, None) } // Like structs, unions are `FromZeroes` if // - all fields are `FromZeroes` fn derive_from_zeroes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream { - impl_block(ast, unn, Trait::FromZeroes, true, None) + impl_block(ast, unn, Trait::FromZeroes, RequireBoundedFields::Yes, None) } // A struct is `FromBytes` if: // - all fields are `FromBytes` fn derive_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream { - impl_block(ast, strct, Trait::FromBytes, true, None) + impl_block(ast, strct, Trait::FromBytes, RequireBoundedFields::Yes, None) } // An enum is `FromBytes` if: @@ -227,7 +241,7 @@ fn derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::Tok .to_compile_error(); } - impl_block(ast, enm, Trait::FromBytes, true, None) + impl_block(ast, enm, Trait::FromBytes, RequireBoundedFields::Yes, None) } #[rustfmt::skip] @@ -258,7 +272,7 @@ const ENUM_FROM_BYTES_CFG: Config = { // - all fields are `FromBytes` fn derive_from_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream { - impl_block(ast, unn, Trait::FromBytes, true, None) + impl_block(ast, unn, Trait::FromBytes, RequireBoundedFields::Yes, None) } // A struct is `AsBytes` if: @@ -292,7 +306,7 @@ fn derive_as_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2: // any padding bytes would need to come from the fields, all of which // we require to be `AsBytes` (meaning they don't have any padding). let padding_check = if is_transparent || is_packed { None } else { Some(PaddingCheck::Struct) }; - impl_block(ast, strct, Trait::AsBytes, true, padding_check) + impl_block(ast, strct, Trait::AsBytes, RequireBoundedFields::Yes, padding_check) } const STRUCT_UNION_AS_BYTES_CFG: Config = Config { @@ -315,7 +329,7 @@ fn derive_as_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::Token // We don't care what the repr is; we only care that it is one of the // allowed ones. let _: Vec = try_or_print!(ENUM_AS_BYTES_CFG.validate_reprs(ast)); - impl_block(ast, enm, Trait::AsBytes, false, None) + impl_block(ast, enm, Trait::AsBytes, RequireBoundedFields::No, None) } #[rustfmt::skip] @@ -357,7 +371,7 @@ fn derive_as_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::Tok try_or_print!(STRUCT_UNION_AS_BYTES_CFG.validate_reprs(ast)); - impl_block(ast, unn, Trait::AsBytes, true, Some(PaddingCheck::Union)) + impl_block(ast, unn, Trait::AsBytes, RequireBoundedFields::Yes, Some(PaddingCheck::Union)) } // A struct is `Unaligned` if: @@ -368,7 +382,7 @@ fn derive_as_bytes_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::Tok fn derive_unaligned_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro2::TokenStream { let reprs = try_or_print!(STRUCT_UNION_UNALIGNED_CFG.validate_reprs(ast)); - let require_trait_bounds_on_field_types = !reprs.contains(&StructRepr::Packed); + let require_trait_bounds_on_field_types = (!reprs.contains(&StructRepr::Packed)).into(); impl_block(ast, strct, Trait::Unaligned, require_trait_bounds_on_field_types, None) } @@ -401,7 +415,7 @@ fn derive_unaligned_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::Toke // for `require_trait_bound_on_field_types` doesn't really do anything. But // it's marginally more future-proof in case that restriction is lifted in // the future. - impl_block(ast, enm, Trait::Unaligned, true, None) + impl_block(ast, enm, Trait::Unaligned, RequireBoundedFields::Yes, None) } #[rustfmt::skip] @@ -437,7 +451,7 @@ const ENUM_UNALIGNED_CFG: Config = { fn derive_unaligned_union(ast: &DeriveInput, unn: &DataUnion) -> proc_macro2::TokenStream { let reprs = try_or_print!(STRUCT_UNION_UNALIGNED_CFG.validate_reprs(ast)); - let require_trait_bound_on_field_types = !reprs.contains(&StructRepr::Packed); + let require_trait_bound_on_field_types = (!reprs.contains(&StructRepr::Packed)).into(); impl_block(ast, unn, Trait::Unaligned, require_trait_bound_on_field_types, None) } @@ -479,11 +493,27 @@ impl Trait { } } +#[derive(Debug, Eq, PartialEq)] +enum RequireBoundedFields { + No, + Yes, + Trailing, +} + +impl From for RequireBoundedFields { + fn from(do_require: bool) -> Self { + match do_require { + true => Self::Yes, + false => Self::No, + } + } +} + fn impl_block( input: &DeriveInput, data: &D, trt: Trait, - require_trait_bound_on_field_types: bool, + require_trait_bound_on_field_types: RequireBoundedFields, padding_check: Option, ) -> proc_macro2::TokenStream { // In this documentation, we will refer to this hypothetical struct: @@ -548,11 +578,19 @@ fn impl_block( let trait_ident = trt.ident(); let field_types = data.field_types(); - let field_type_bounds = require_trait_bound_on_field_types - .then(|| field_types.iter().map(|ty| parse_quote!(#ty: zerocopy::#trait_ident))) - .into_iter() - .flatten() - .collect::>(); + let field_type_bounds: Vec<_> = match require_trait_bound_on_field_types { + RequireBoundedFields::No => vec![], + RequireBoundedFields::Yes => { + field_types.iter().map(|ty| parse_quote!(#ty: zerocopy::#trait_ident)).collect() + } + RequireBoundedFields::Trailing => { + if let Some(trailing) = field_types.last() { + vec![parse_quote!(#trailing: zerocopy::#trait_ident)] + } else { + vec![] + } + } + }; // Don't bother emitting a padding check if there are no fields. #[allow(unstable_name_collisions)] // See `BoolExt` below @@ -576,10 +614,17 @@ fn impl_block( .chain(padding_check_bound.iter()); let layout_extras = if trt == Trait::KnownLayout { - // We currently only support deriving for sized types; this code will - // fail to compile for unsized types. + let layout = if let Some((name, ty)) = input.data.trailing_field() { + quote!(zerocopy::dst_layout!( + Self, + #name: #ty + )) + } else { + quote!(zerocopy::DstLayout::for_type::()) + }; + Some(quote!( - const LAYOUT: zerocopy::DstLayout = zerocopy::DstLayout::for_type::(); + const LAYOUT: zerocopy::DstLayout = #layout; // SAFETY: `.cast` preserves address and provenance. //