// Copyright 2019 The Fuchsia Authors // // Licensed under a BSD-style license , Apache License, Version 2.0 // , or the MIT // license , at your option. // This file may not be copied, modified, or distributed except according to // those terms. // See comment in `include.rs` for why we disable the prelude. #![no_implicit_prelude] #![allow(warnings)] include!("include.rs"); // A struct can derive `IntoBytes` if all of its fields implement `IntoBytes` // and its representation lets the derive prove that the struct introduces no // uninitialized outer padding. The cases below are grouped by representation // and by the proof strategy used by the derive. // // Non-generic `repr(C)` // #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct CZst; util_assert_impl_all!(CZst: imp::IntoBytes); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, align(8))] struct ReprCAlignedZst; util_assert_impl_all!(ReprCAlignedZst: imp::IntoBytes); #[derive(imp::IntoBytes, Copy, Clone)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, align(8))] struct ReprCAligned8([u8; 8]); util_assert_impl_all!(ReprCAligned8: imp::IntoBytes); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct C { a: u8, b: u8, c: util::AU16, } util_assert_impl_all!(C: imp::IntoBytes); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCSyntacticDst { a: u8, b: u8, c: [util::AU16], } util_assert_impl_all!(ReprCSyntacticDst: imp::IntoBytes); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, align(2))] struct ReprCSyntacticDstAligned { a: [[u8; 2]], } util_assert_impl_all!(ReprCSyntacticDstAligned: imp::IntoBytes); // // Generic `repr(C)` // #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCGenericOneField { t: T, } // Even though `ReprCGenericOneField` has generic type arguments, its one-field // layout never requires an `Unaligned` bound. Sized, array, and slice // instantiations are asserted in `assert_repr_c_homogeneous` below. // Given `T: Sized + IntoBytes`, a plain `repr(C)` struct whose fields are all // `T`, `[T; N]`, or a final `[T]` introduces no outer padding. `T` and all of // its arrays and slices have the same alignment. The size of `T`, every array, // and every slice value is a multiple of that alignment; bytes within each `T` // are covered by `T`'s `IntoBytes` implementation. // // The following six declarations exhaustively enumerate the direct two-field // shape combinations in this grammar. A slice may only be the final field, so // the first field is either `T` or `[T; N]`, while the second is `T`, `[T; M]`, // or `[T]`. The one-field case is covered by `ReprCGenericOneField` above. #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCHomogeneousTThenT(T, T); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCHomogeneousTThenArray(T, [T; N]); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCHomogeneousArrayThenT([T; N], T); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCHomogeneousArrayThenArray([T; N], [T; M]); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCHomogeneousTThenSlice(T, [T]); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCHomogeneousArrayThenSlice([T; N], [T]); // Exercise longer named-field structs containing every allowed sized field // shape, both with and without a trailing slice. Together with the two-field // matrix above, these ensure that the derive handles repeated transitions and // both of its sized/DST paths rather than special-casing a particular arity. // The literal-length field also ensures that recognition does not depend on // the array length being a const parameter. #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCHomogeneousMixedSized { literal: [T; 0], array0: [T; N], t0: T, array1: [T; M], t1: T, } #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCHomogeneousMixedDst { t0: T, array0: [T; N], t1: T, array1: [T; M], tail: [T], } // `align(1)` cannot increase a type's alignment, so it preserves the same // no-padding argument as an unmodified `repr(C)`. Test both the sized and DST // paths. #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, align(1))] struct ReprCHomogeneousAlign1Sized(T, [T; N], T); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, align(1))] struct ReprCHomogeneousAlign1Dst(T, [T; N], [T]); // `packed(2)` caps every field's alignment at the same value. Since every // field's size is still a multiple of that capped alignment, it likewise // preserves the no-padding argument. A packed DST may only contain elements // which do not need drop, so the DST declaration additionally requires // `Copy`. #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, packed(2))] struct ReprCHomogeneousPacked2Sized(T, [T; N], T); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, packed(2))] struct ReprCHomogeneousPacked2Dst(T, [T; N], [T]); // This generic assertion is stronger than checking a finite set of concrete // element types and array lengths: its body only assumes `T: IntoBytes`, and // is type-checked for every `N` and `M`. fn assert_repr_c_homogeneous() { fn assert_into_bytes() {} assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); assert_into_bytes::>(); } fn assert_repr_c_homogeneous_packed_dst() { fn assert_into_bytes() {} assert_into_bytes::>(); } // `AU16` is guaranteed to have alignment 2 and not implement `Unaligned`, so // these assertions distinguish the homogeneous case from the existing // `Unaligned` fallback. The witnesses cover every field shape, zero and // nonzero arrays in both positions, and both the sized and DST paths. util_assert_impl_all!(ReprCHomogeneousTThenT: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousTThenArray: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousArrayThenT: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousArrayThenArray: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousTThenSlice: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousArrayThenSlice: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousArrayThenSlice: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousMixedSized: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousMixedDst: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousAlign1Sized: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousAlign1Dst: imp::IntoBytes); // This exercises the case in which `packed(2)` actually lowers the element // alignment rather than merely leaving it unchanged. util_assert_impl_all!(ReprCHomogeneousPacked2Sized: imp::IntoBytes); util_assert_impl_all!(ReprCHomogeneousPacked2Dst: imp::IntoBytes); // Zero-sized element types still carry alignment. In particular, a derive // must not assume that an aligned field occupies any bytes. util_assert_impl_all!(ReprCHomogeneousArrayThenSlice: imp::IntoBytes); // `IntoBytes` does not imply `Immutable`; interior-mutable element types are // therefore part of the supported set too. util_assert_impl_all!( ReprCHomogeneousArrayThenSlice, 3>: imp::IntoBytes ); // A zero-length array does not eliminate the `T: IntoBytes` requirement: the // trailing slice can still contain elements. `MaybeUninit` implements // `Unaligned` but not `IntoBytes`, isolating the bound under test. util_assert_not_impl_any!( ReprCHomogeneousArrayThenSlice, 0>: imp::IntoBytes ); // Generic `repr(C)` structs outside of the homogeneous family fall back to // requiring every field type to be `Unaligned`. #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCIndependentFields { t: T, u: U, } util_assert_impl_all!(ReprCIndependentFields: imp::IntoBytes); util_assert_impl_all!(ReprCIndependentFields: imp::IntoBytes); util_assert_not_impl_any!(ReprCIndependentFields: imp::IntoBytes); util_assert_not_impl_any!(ReprCIndependentFields: imp::IntoBytes); // The common element type is essential. Here, the zero-length `AU16` array // gives the struct alignment 2, while an odd-length `[u8]` tail requires a // trailing padding byte. #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C)] struct ReprCHeterogeneousArrayThenSlice([T; N], [U]); util_assert_not_impl_any!( ReprCHeterogeneousArrayThenSlice: imp::IntoBytes ); // // `repr(transparent)` // #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(transparent)] struct Transparent { a: u8, b: (), } util_assert_impl_all!(Transparent: imp::IntoBytes); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(transparent)] struct TransparentGeneric { a: (), b: T, } util_assert_impl_all!(TransparentGeneric: imp::IntoBytes); util_assert_impl_all!(TransparentGeneric<[u64]>: imp::IntoBytes); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(transparent)] struct Unsized { a: [u8], } util_assert_impl_all!(Unsized: imp::IntoBytes); // Deriving `IntoBytes` should work if the struct has bounded parameters. #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(transparent)] struct WithParams<'a: 'b, 'b: 'a, T: 'a + 'b + imp::IntoBytes, const N: usize>( [T; N], imp::PhantomData<&'a &'b ()>, ) where 'a: 'b, 'b: 'a, T: 'a + 'b + imp::IntoBytes; util_assert_impl_all!(WithParams<'static, 'static, u8, 42>: imp::IntoBytes); // // Packed representations // #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, packed)] struct CZstPacked; util_assert_impl_all!(CZstPacked: imp::IntoBytes); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, packed)] struct CPacked { a: u8, // NOTE: The `u16` type is not guaranteed to have alignment 2, although it // does on many platforms. However, to fix this would require a custom type // with a `#[repr(align(2))]` attribute, and `#[repr(packed)]` types are not // allowed to transitively contain `#[repr(align(...))]` types. Thus, we // have no choice but to use `u16` here. Luckily, these tests run in CI on // platforms on which `u16` has alignment 2, so this isn't that big of a // deal. b: u16, } util_assert_impl_all!(CPacked: imp::IntoBytes); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, packed(2))] // The same caveats as for CPacked apply - we're assuming u64 is at least // 4-byte aligned by default. Without packed(2), this should fail, as there // would be padding between a/b assuming u64 is 4+ byte aligned. struct CPacked2 { a: u16, b: u64, } util_assert_impl_all!(CPacked2: imp::IntoBytes); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, packed)] struct CPackedGeneric { t: T, // Unsized types stored in `repr(packed)` structs must not be dropped // because dropping them in-place might be unsound depending on the // alignment of the outer struct. Sized types can be dropped by first being // moved to an aligned stack variable, but this isn't possible with unsized // types. u: imp::ManuallyDrop, } util_assert_impl_all!(CPackedGeneric: imp::IntoBytes); util_assert_impl_all!(CPackedGeneric: imp::IntoBytes); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(packed)] struct PackedGeneric { t: T, // Unsized types stored in `repr(packed)` structs must not be dropped // because dropping them in-place might be unsound depending on the // alignment of the outer struct. Sized types can be dropped by first being // moved to an aligned stack variable, but this isn't possible with unsized // types. u: imp::ManuallyDrop, } util_assert_impl_all!(PackedGeneric: imp::IntoBytes); util_assert_impl_all!(PackedGeneric: imp::IntoBytes); // Test for the failure reported in #1182. #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, packed)] pub struct IndexEntryFlags(u8); #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] #[repr(C, packed)] pub struct IndexEntry { block_number: imp::native_endian::U64, flags: IndexEntryFlags, block_id: [u8; SIZE_BLOCK_ID], } util_assert_impl_all!(IndexEntry<0>: imp::IntoBytes); util_assert_impl_all!(IndexEntry<1>: imp::IntoBytes); // // Rust representation // // This test is non-portable, but works so long as Rust happens to lay this // struct out with no padding. #[derive(imp::IntoBytes)] #[zerocopy(crate = "zerocopy_renamed")] struct Unpacked { a: u8, b: u8, } util_assert_impl_all!(Unpacked: imp::IntoBytes);