From 60f0a430ec2fc79f28a10ef1c03ec2b899ead98e Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Fri, 4 Oct 2024 18:44:37 -0700 Subject: [PATCH] Add initial support for unsized `MaybeUninit` wrapper type This is achieved by adding a `MaybeUninit` associated type to `KnownLayout`, whose layout is identical to `Self` except that it admits uninitialized bytes in all positions. For sized types, this is bound to `mem::MaybeUninit`. For potentially unsized structs, we synthesize a doppelganger with the same `repr`, whose leading fields are wrapped in `mem::MaybeUninit` and whose trailing field is the `MaybeUninit` associated type of struct's original trailing field type. This type-level recursion bottoms out at `[T]`, whose `MaybeUninit` associated type is bound to `[mem::MaybeUninit]`. Makes progress towards #1797 SKIP_CARGO_SEMVER_CHECKS=1 --- .github/workflows/ci.yml | 4 +- src/impls.rs | 30 +- src/lib.rs | 142 +++++----- src/util/macros.rs | 12 + src/util/mod.rs | 139 ++++++++++ src/wrappers.rs | 190 +++++++++++-- ...nostic-not-implemented-known-layout.stderr | 4 +- ...nostic-not-implemented-known-layout.stderr | 4 +- zerocopy-derive/src/lib.rs | 261 ++++++++++++++---- zerocopy-derive/src/output_tests.rs | 113 +++++++- .../tests/ui-msrv/mid_compile_pass.stderr | 9 +- .../tests/ui-nightly/mid_compile_pass.stderr | 13 +- .../tests/ui-nightly/struct.stderr | 8 +- .../tests/ui-stable/mid_compile_pass.stderr | 13 +- zerocopy-derive/tests/ui-stable/struct.stderr | 8 +- 15 files changed, 759 insertions(+), 191 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3fcf736eca..1d0f7ccaf7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -244,6 +244,8 @@ jobs: steps: - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1 + with: + fetch-depth: 2 - name: Populate cache uses: ./.github/actions/cache @@ -490,7 +492,7 @@ jobs: if [ "${{ github.event_name }}" == "pull_request" ]; then # Invoked from a PR - get the PR body directly - MESSAGE="${{ github.event.pull_request.body }}" + MESSAGE="$(git log -1 --pretty=%B ${{ github.event.pull_request.head.sha }})" else # Invoked from the merge queue - get the commit message MESSAGE="$(git log -1 --pretty=%B ${{ github.sha }})" diff --git a/src/impls.rs b/src/impls.rs index 1b00e22cee..79a3ae07d5 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -7,6 +7,8 @@ // This file may not be copied, modified, or distributed except according to // those terms. +use core::mem::MaybeUninit as CoreMaybeUninit; + use super::*; safety_comment! { @@ -632,14 +634,14 @@ safety_comment! { /// SAFETY: /// `TryFromBytes` (with no validator), `FromZeros`, `FromBytes`: /// `MaybeUninit` has no restrictions on its contents. - unsafe_impl!(T => TryFromBytes for MaybeUninit); - unsafe_impl!(T => FromZeros for MaybeUninit); - unsafe_impl!(T => FromBytes for MaybeUninit); + unsafe_impl!(T => TryFromBytes for CoreMaybeUninit); + unsafe_impl!(T => FromZeros for CoreMaybeUninit); + unsafe_impl!(T => FromBytes for CoreMaybeUninit); } -impl_for_transparent_wrapper!(T: Immutable => Immutable for MaybeUninit); -impl_for_transparent_wrapper!(T: Unaligned => Unaligned for MaybeUninit); -assert_unaligned!(MaybeUninit<()>, MaybeUninit); +impl_for_transparent_wrapper!(T: Immutable => Immutable for CoreMaybeUninit); +impl_for_transparent_wrapper!(T: Unaligned => Unaligned for CoreMaybeUninit); +assert_unaligned!(CoreMaybeUninit<()>, CoreMaybeUninit); impl_for_transparent_wrapper!(T: ?Sized + Immutable => Immutable for ManuallyDrop); impl_for_transparent_wrapper!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop); @@ -1257,8 +1259,8 @@ mod tests { ManuallyDrop>, ManuallyDrop<[UnsafeCell]>, ManuallyDrop<[UnsafeCell]>, - MaybeUninit, - MaybeUninit>, + CoreMaybeUninit, + CoreMaybeUninit>, Wrapping> ); @@ -1300,9 +1302,9 @@ mod tests { Option, Option, Option, - MaybeUninit, - MaybeUninit, - MaybeUninit>, + CoreMaybeUninit, + CoreMaybeUninit, + CoreMaybeUninit>, ManuallyDrop>, ManuallyDrop<[UnsafeCell]>, ManuallyDrop<[UnsafeCell]>, @@ -1764,9 +1766,9 @@ mod tests { assert_impls!(ManuallyDrop<[UnsafeCell]>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !Immutable); assert_impls!(ManuallyDrop<[UnsafeCell]>: KnownLayout, TryFromBytes, FromZeros, IntoBytes, Unaligned, !Immutable, !FromBytes); - assert_impls!(MaybeUninit: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, Unaligned, !IntoBytes); - assert_impls!(MaybeUninit: KnownLayout, TryFromBytes, FromZeros, FromBytes, !Immutable, !IntoBytes, !Unaligned); - assert_impls!(MaybeUninit>: KnownLayout, TryFromBytes, FromZeros, FromBytes, Unaligned, !Immutable, !IntoBytes); + 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); // This test is important because it allows us to test our hand-rolled diff --git a/src/lib.rs b/src/lib.rs index c48223e7dc..a45c49c8fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -354,7 +354,7 @@ use core::{ fmt::{self, Debug, Display, Formatter}, hash::Hasher, marker::PhantomData, - mem::{self, ManuallyDrop, MaybeUninit}, + mem::{self, ManuallyDrop, MaybeUninit as CoreMaybeUninit}, num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping, @@ -732,6 +732,15 @@ pub unsafe trait KnownLayout { /// This is `()` for sized types and `usize` for slice DSTs. type PointerMetadata: PointerMetadata; + /// A maybe-uninitialized analog of `Self` + /// + /// # Safety + /// + /// `Self::LAYOUT` and `Self::MaybeUninit::LAYOUT` are identical. + /// `Self::MaybeUninit` admits uninitialized bytes in all positions. + #[doc(hidden)] + type MaybeUninit: ?Sized + KnownLayout; + /// The layout of `Self`. /// /// # Safety @@ -864,6 +873,35 @@ unsafe impl KnownLayout for [T] { type PointerMetadata = usize; + // SAFETY: `CoreMaybeUninit::LAYOUT` and `T::LAYOUT` are identical + // because `CoreMaybeUninit` has the same size and alignment as `T` [1]. + // Consequently, `[CoreMaybeUninit]::LAYOUT` and `[T]::LAYOUT` are + // identical, because they both lack a fixed-sized prefix and because they + // inherit the alignments of their inner element type (which are identical) + // [2][3]. + // + // `[CoreMaybeUninit]` admits uninitialized bytes at all positions + // because `CoreMaybeUninit` admits uninitialized bytes at all positions + // and because the inner elements of `[CoreMaybeUninit]` are laid out + // back-to-back [2][3]. + // + // [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` + // + // [2] Per https://doc.rust-lang.org/1.82.0/reference/type-layout.html#slice-layout: + // + // Slices have the same layout as the section of the array they slice. + // + // [3] Per https://doc.rust-lang.org/1.82.0/reference/type-layout.html#array-layout: + // + // An array of `[T; N]` has a size of `size_of::() * N` and the same + // alignment of `T`. Arrays are laid out so that the zero-based `nth` + // element of the array is offset from the start of the array by `n * + // size_of::()` bytes. + type MaybeUninit = [CoreMaybeUninit]; + const LAYOUT: DstLayout = DstLayout::for_slice::(); // SAFETY: `.cast` preserves address and provenance. The returned pointer @@ -916,9 +954,11 @@ impl_known_layout!( T => Option, T: ?Sized => PhantomData, T => Wrapping, - T => MaybeUninit, + T => CoreMaybeUninit, T: ?Sized => *const T, - T: ?Sized => *mut T + T: ?Sized => *mut T, + T: ?Sized => &'_ T, + T: ?Sized => &'_ mut T, ); impl_known_layout!(const N: usize, T => [T; N]); @@ -949,6 +989,21 @@ safety_comment! { unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] UnsafeCell); } +safety_comment! { + /// SAFETY: + /// - By consequence of the invariant on `T::MaybeUninit` that `T::LAYOUT` + /// and `T::MaybeUninit::LAYOUT` are equal, `T` and `T::MaybeUninit` + /// have the same: + /// - Fixed prefix size + /// - Alignment + /// - (For DSTs) trailing slice element size + /// - By consequence of the above, referents `T::MaybeUninit` and `T` have + /// the require the same kind of pointer metadata, and thus it is valid to + /// perform an `as` cast from `*mut T` and `*mut T::MaybeUninit`, and this + /// operation preserves referent size (ie, `size_of_val_raw`). + unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T::MaybeUninit)] MaybeUninit); +} + /// Analyzes whether a type is [`FromZeros`]. /// /// This derive analyzes, at compile time, whether the annotated type satisfies @@ -2550,7 +2605,7 @@ pub unsafe trait TryFromBytes { where Self: Sized, { - let candidate = match MaybeUninit::::read_from_bytes(source) { + let candidate = match CoreMaybeUninit::::read_from_bytes(source) { Ok(candidate) => candidate, Err(e) => { return Err(TryReadError::Size(e.with_dst())); @@ -2611,7 +2666,7 @@ pub unsafe trait TryFromBytes { where Self: Sized, { - let (candidate, suffix) = match MaybeUninit::::read_from_prefix(source) { + let (candidate, suffix) = match CoreMaybeUninit::::read_from_prefix(source) { Ok(candidate) => candidate, Err(e) => { return Err(TryReadError::Size(e.with_dst())); @@ -2673,7 +2728,7 @@ pub unsafe trait TryFromBytes { where Self: Sized, { - let (prefix, candidate) = match MaybeUninit::::read_from_suffix(source) { + let (prefix, candidate) = match CoreMaybeUninit::::read_from_suffix(source) { Ok(candidate) => candidate, Err(e) => { return Err(TryReadError::Size(e.with_dst())); @@ -2746,7 +2801,7 @@ fn swap((t, u): (T, U)) -> (U, T) { #[inline(always)] unsafe fn try_read_from( source: S, - mut candidate: MaybeUninit, + mut candidate: CoreMaybeUninit, ) -> Result> { // We use `from_mut` despite not mutating via `c_ptr` so that we don't need // to add a `T: Immutable` bound. @@ -3035,72 +3090,11 @@ pub unsafe trait FromZeros: TryFromBytes { where Self: KnownLayout, { - let size = match count.size_for_metadata(Self::LAYOUT) { - Some(size) => size, - None => return Err(AllocError), - }; - - let align = Self::LAYOUT.align.get(); - // On stable Rust versions <= 1.64.0, `Layout::from_size_align` has a - // bug in which sufficiently-large allocations (those which, when - // rounded up to the alignment, overflow `isize`) are not rejected, - // which can cause undefined behavior. See #64 for details. - // - // TODO(#67): Once our MSRV is > 1.64.0, remove this assertion. - #[allow(clippy::as_conversions)] - let max_alloc = (isize::MAX as usize).saturating_sub(align); - if size > max_alloc { - return Err(AllocError); - } - - // TODO(https://github.com/rust-lang/rust/issues/55724): Use - // `Layout::repeat` once it's stabilized. - let layout = Layout::from_size_align(size, align).or(Err(AllocError))?; - - let ptr = if layout.size() != 0 { - // TODO(#429): Add a "SAFETY" comment and remove this `allow`. - #[allow(clippy::undocumented_unsafe_blocks)] - let ptr = unsafe { alloc::alloc::alloc_zeroed(layout) }; - match NonNull::new(ptr) { - Some(ptr) => ptr, - None => return Err(AllocError), - } - } else { - let align = Self::LAYOUT.align.get(); - // We use `transmute` instead of an `as` cast since Miri (with - // strict provenance enabled) notices and complains that an `as` - // cast creates a pointer with no provenance. Miri isn't smart - // enough to realize that we're only executing this branch when - // we're constructing a zero-sized `Box`, which doesn't require - // provenance. - // - // SAFETY: any initialized bit sequence is a bit-valid `*mut u8`. - // All bits of a `usize` are initialized. - #[allow(clippy::useless_transmute)] - let dangling = unsafe { mem::transmute::(align) }; - // SAFETY: `dangling` is constructed from `Self::LAYOUT.align`, - // which is a `NonZeroUsize`, which is guaranteed to be non-zero. - // - // `Box<[T]>` does not allocate when `T` is zero-sized or when `len` - // is zero, but it does require a non-null dangling pointer for its - // allocation. - // - // TODO(https://github.com/rust-lang/rust/issues/95228): Use - // `std::ptr::without_provenance` once it's stable. That may - // optimize better. As written, Rust may assume that this consumes - // "exposed" provenance, and thus Rust may have to assume that this - // may consume provenance from any pointer whose provenance has been - // exposed. - unsafe { NonNull::new_unchecked(dangling) } - }; - - let ptr = Self::raw_from_ptr_len(ptr, count); - - // TODO(#429): Add a "SAFETY" comment and remove this `allow`. Make sure - // to include a justification that `ptr.as_ptr()` is validly-aligned in - // the ZST case (in which we manually construct a dangling pointer). - #[allow(clippy::undocumented_unsafe_blocks)] - Ok(unsafe { Box::from_raw(ptr.as_ptr()) }) + // SAFETY: `alloc::alloc::alloc_zeroed` is a valid argument of + // `new_box`. The referent of the pointer returned by `alloc_zeroed` + // (and, consequently, the `Box` derived from it) is a valid instance of + // `Self`, because `Self` is `FromZeros`. + unsafe { crate::util::new_box(count, alloc::alloc::alloc_zeroed) } } #[deprecated(since = "0.8.0", note = "renamed to `FromZeros::new_box_zeroed_with_elems`")] @@ -4562,7 +4556,7 @@ pub unsafe trait FromBytes: FromZeros { Self: Sized, R: io::Read, { - let mut buf = MaybeUninit::::zeroed(); + let mut buf = CoreMaybeUninit::::zeroed(); let ptr = Ptr::from_mut(&mut buf); // SAFETY: `buf` consists entirely of initialized, zeroed bytes. let ptr = unsafe { ptr.assume_validity::() }; diff --git a/src/util/macros.rs b/src/util/macros.rs index 582df1a8d1..d155d142e5 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -557,6 +557,17 @@ macro_rules! impl_known_layout { type PointerMetadata = (); + // SAFETY: `CoreMaybeUninit::LAYOUT` and `T::LAYOUT` are + // identical because `CoreMaybeUninit` has the same size and + // alignment as `T` [1], and `CoreMaybeUninit` admits + // uninitialized bytes in all positions. + // + // [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` + type MaybeUninit = core::mem::MaybeUninit; + const LAYOUT: crate::DstLayout = crate::DstLayout::for_type::<$ty>(); // SAFETY: `.cast` preserves address and provenance. @@ -599,6 +610,7 @@ macro_rules! unsafe_impl_known_layout { fn only_derive_is_allowed_to_implement_this_trait() {} type PointerMetadata = <$repr as KnownLayout>::PointerMetadata; + type MaybeUninit = <$repr as KnownLayout>::MaybeUninit; const LAYOUT: DstLayout = <$repr as KnownLayout>::LAYOUT; diff --git a/src/util/mod.rs b/src/util/mod.rs index d7d1710158..0c4dd788ce 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -683,6 +683,145 @@ pub(crate) unsafe fn copy_unchecked(src: &[u8], dst: &mut [u8]) { }; } +/// Unsafely transmutes the given `src` into a type `Dst`. +/// +/// # Safety +/// +/// The value `src` must be a valid instance of `Dst`. +#[inline(always)] +pub(crate) const unsafe fn transmute_unchecked(src: Src) -> Dst { + static_assert!(Src, Dst => core::mem::size_of::() == core::mem::size_of::()); + + #[repr(C)] + union Transmute { + src: ManuallyDrop, + dst: ManuallyDrop, + } + + // SAFETY: Since `Transmute` is `#[repr(C)]`, its `src` and `dst` + // fields both start at the same offset and the types of those fields are + // transparent wrappers around `Src` and `Dst` [1]. Consequently, + // initializng `Transmute` with with `src` and then reading out `dst` is + // equivalent to transmuting from `Src` to `Dst` [2]. Transmuting from `src` + // to `Dst` is valid because — by contract on the caller — `src` is a valid + // instance of `Dst`. + // + // [1] Per https://doc.rust-lang.org/1.82.0/std/mem/struct.ManuallyDrop.html: + // + // `ManuallyDrop` is guaranteed to have the same layout and bit + // validity as `T`, and is subject to the same layout optimizations as + // `T`. + // + // [2] Per https://doc.rust-lang.org/1.82.0/reference/items/unions.html#reading-and-writing-union-fields: + // + // Effectively, writing to and then reading from a union with the C + // representation is analogous to a transmute from the type used for + // writing to the type used for reading. + unsafe { ManuallyDrop::into_inner(Transmute { src: ManuallyDrop::new(src) }.dst) } +} + +/// Uses `allocate` to create a `Box`. +/// +/// # Errors +/// +/// Returns an error on allocation failure. Allocation failure is guaranteed +/// never to cause a panic or an abort. +/// +/// # Safety +/// +/// `allocate` must be either `alloc::alloc::alloc` or +/// `alloc::alloc::alloc_zeroed`. The referent of the box returned by `new_box` +/// has the same bit-validity as the referent of the pointer returned by the +/// given `allocate` and sufficient size to store `T` with `meta`. +#[must_use = "has no side effects (other than allocation)"] +#[cfg(feature = "alloc")] +#[inline] +pub(crate) unsafe fn new_box( + meta: T::PointerMetadata, + allocate: unsafe fn(core::alloc::Layout) -> *mut u8, +) -> Result, crate::error::AllocError> +where + T: ?Sized + crate::KnownLayout, +{ + use crate::error::AllocError; + use crate::PointerMetadata; + use core::alloc::Layout; + + let size = match meta.size_for_metadata(T::LAYOUT) { + Some(size) => size, + None => return Err(AllocError), + }; + + let align = T::LAYOUT.align.get(); + // On stable Rust versions <= 1.64.0, `Layout::from_size_align` has a bug in + // which sufficiently-large allocations (those which, when rounded up to the + // alignment, overflow `isize`) are not rejected, which can cause undefined + // behavior. See #64 for details. + // + // TODO(#67): Once our MSRV is > 1.64.0, remove this assertion. + #[allow(clippy::as_conversions)] + let max_alloc = (isize::MAX as usize).saturating_sub(align); + if size > max_alloc { + return Err(AllocError); + } + + // TODO(https://github.com/rust-lang/rust/issues/55724): Use + // `Layout::repeat` once it's stabilized. + let layout = Layout::from_size_align(size, align).or(Err(AllocError))?; + + let ptr = if layout.size() != 0 { + // SAFETY: By contract on the caller, `allocate` is either + // `alloc::alloc::alloc` or `alloc::alloc::alloc_zeroed`. The above + // check ensures their shared safety precondition: that the supplied + // layout is not zero-sized type [1]. + // + // [1] Per https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html#tymethod.alloc: + // + // This function is unsafe because undefined behavior can result if + // the caller does not ensure that layout has non-zero size. + let ptr = unsafe { allocate(layout) }; + match NonNull::new(ptr) { + Some(ptr) => ptr, + None => return Err(AllocError), + } + } else { + let align = T::LAYOUT.align.get(); + // We use `transmute` instead of an `as` cast since Miri (with strict + // provenance enabled) notices and complains that an `as` cast creates a + // pointer with no provenance. Miri isn't smart enough to realize that + // we're only executing this branch when we're constructing a zero-sized + // `Box`, which doesn't require provenance. + // + // SAFETY: any initialized bit sequence is a bit-valid `*mut u8`. All + // bits of a `usize` are initialized. + #[allow(clippy::useless_transmute)] + let dangling = unsafe { mem::transmute::(align) }; + // SAFETY: `dangling` is constructed from `T::LAYOUT.align`, which is a + // `NonZeroUsize`, which is guaranteed to be non-zero. + // + // `Box<[T]>` does not allocate when `T` is zero-sized or when `len` is + // zero, but it does require a non-null dangling pointer for its + // allocation. + // + // TODO(https://github.com/rust-lang/rust/issues/95228): Use + // `std::ptr::without_provenance` once it's stable. That may optimize + // better. As written, Rust may assume that this consumes "exposed" + // provenance, and thus Rust may have to assume that this may consume + // provenance from any pointer whose provenance has been exposed. + unsafe { NonNull::new_unchecked(dangling) } + }; + + let ptr = T::raw_from_ptr_len(ptr, meta); + + // TODO(#429): Add a "SAFETY" comment and remove this `allow`. Make sure to + // include a justification that `ptr.as_ptr()` is validly-aligned in the ZST + // case (in which we manually construct a dangling pointer) and to justify + // why `Box` is safe to drop (it's because `allocate` uses the system + // allocator). + #[allow(clippy::undocumented_unsafe_blocks)] + Ok(unsafe { alloc::boxed::Box::from_raw(ptr.as_ptr()) }) +} + /// 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 0637d76025..fd48236ae4 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -6,7 +6,7 @@ // This file may not be copied, modified, or distributed except according to // those terms. -use core::hash::Hash; +use core::{fmt, hash::Hash}; use super::*; @@ -166,20 +166,8 @@ impl Unalign { /// Consumes `self`, returning the inner `T`. #[inline(always)] pub const fn into_inner(self) -> T { - // Use this instead of `mem::transmute` since the latter can't tell - // that `Unalign` and `T` have the same size. - #[repr(C)] - union Transmute { - u: ManuallyDrop>, - t: ManuallyDrop, - } - - // SAFETY: Since `Unalign` is `#[repr(C, packed)]`, it has the same - // layout as `T`. `ManuallyDrop` is guaranteed to have the same - // layout as `U`, and so `ManuallyDrop>` has the same layout - // as `ManuallyDrop`. Since `Transmute` is `#[repr(C)]`, its `t` - // and `u` fields both start at the same offset (namely, 0) within the - // union. + // SAFETY: Since `Unalign` is `#[repr(C, packed)]`, it has the same size + // and bit validity as `T`. // // We do this instead of just destructuring in order to prevent // `Unalign`'s `Drop::drop` from being run, since dropping is not @@ -187,7 +175,7 @@ impl Unalign { // // TODO(https://github.com/rust-lang/rust/issues/73255): Destructure // instead of using unsafe. - unsafe { ManuallyDrop::into_inner(Transmute { u: ManuallyDrop::new(self) }.t) } + unsafe { crate::util::transmute_unchecked(self) } } /// Attempts to return a reference to the wrapped `T`, failing if `self` is @@ -464,6 +452,139 @@ impl Display for Unalign { } } +/// A wrapper type to construct uninitialized instances of `T`. +/// +/// `MaybeUninit` is identical to the [standard library +/// `MaybeUninit`][core-maybe-uninit] type except that it supports unsized +/// types. +/// +/// # Layout +/// +/// The same layout guarantees and caveats apply to `MaybeUninit` as apply to +/// the [standard library `MaybeUninit`][core-maybe-uninit] with one exception: +/// for `T: !Sized`, there is no single value for `T`'s size. Instead, for such +/// types, the following are guaranteed: +/// - Every [valid size][valid-size] for `T` is a valid size for +/// `MaybeUninit` and vice versa +/// - Given `t: *const T` and `m: *const MaybeUninit` with identical fat +/// pointer metadata, `t` and `m` address the same number of bytes (and +/// likewise for `*mut`) +/// +/// [core-maybe-uninit]: core::mem::MaybeUninit +/// [valid-size]: crate::KnownLayout#what-is-a-valid-size +#[repr(transparent)] +#[doc(hidden)] +pub struct MaybeUninit( + // SAFETY: `MaybeUninit` has the same size as `T`, because (by invariant + // on `T::MaybeUninit`) `T::MaybeUninit` has `T::LAYOUT` identical to `T`, + // and because (invariant on `T::LAYOUT`) we can trust that `LAYOUT` + // accurately reflects the layout of `T`. By invariant on `T::MaybeUninit`, + // it admits uninitialized bytes in all positions. Because `MabyeUninit` is + // marked `repr(transparent)`, these properties additionally hold true for + // `Self`. + T::MaybeUninit, +); + +#[doc(hidden)] +impl MaybeUninit { + /// Constructs a `MaybeUninit` initialized with the given value. + #[inline(always)] + pub fn new(val: T) -> Self + where + T: Sized, + Self: Sized, + { + // SAFETY: It is valid to transmute `val` to `MaybeUninit` because it + // is both valid to transmute `val` to `T::MaybeUninit`, and it is valid + // to transmute from `T::MaybeUninit` to `MaybeUninit`. + // + // First, it is valid to transmute `val` to `T::MaybeUninit` because, by + // invariant on `T::MaybeUninit`: + // - For `T: Sized`, `T` and `T::MaybeUninit` have the same size. + // - All byte sequences of the correct size are valid values of + // `T::MaybeUninit`. + // + // Second, it is additionally valid to transmute from `T::MaybeUninit` + // to `MaybeUninit`, because `MaybeUninit` is a + // `repr(transparent)` wrapper around `T::MaybeUninit`. + // + // These two transmutes are collapsed into one so we don't need to add a + // `T::MaybeUninit: Sized` bound to this function's `where` clause. + unsafe { crate::util::transmute_unchecked(val) } + } + + /// Constructs an uninitialized `MaybeUninit`. + #[must_use] + #[inline(always)] + pub fn uninit() -> Self + where + T: Sized, + Self: Sized, + { + let uninit = CoreMaybeUninit::::uninit(); + // SAFETY: It is valid to transmute from `CoreMaybeUninit` to + // `MaybeUninit` since they both admit uninitialized bytes in all + // positions, and they have the same size (i.e., that of `T`). + // + // `MaybeUninit` has the same size as `T`, because (by invariant on + // `T::MaybeUninit`) `T::MaybeUninit` has `T::LAYOUT` identical to `T`, + // and because (invariant on `T::LAYOUT`) we can trust that `LAYOUT` + // accurately reflects the layout of `T`. + // + // `CoreMaybeUninit` has the same size as `T` [1] and admits + // uninitialized bytes in all positions. + // + // [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 { crate::util::transmute_unchecked(uninit) } + } + + /// Creates a `Box>`. + /// + /// This function is useful for allocating large, uninit values on the heap + /// without ever creating a temporary instance of `Self` on the stack. + /// + /// # Errors + /// + /// Returns an error on allocation failure. Allocation failure is guaranteed + /// never to cause a panic or an abort. + #[cfg(feature = "alloc")] + #[inline] + pub fn new_boxed_uninit(meta: T::PointerMetadata) -> Result, AllocError> { + // SAFETY: `alloc::alloc::alloc_zeroed` is a valid argument of + // `new_box`. The referent of the pointer returned by `alloc` (and, + // consequently, the `Box` derived from it) is a valid instance of + // `Self`, because `Self` is `MaybeUninit` and thus admits arbitrary + // (un)initialized bytes. + unsafe { crate::util::new_box(meta, alloc::alloc::alloc) } + } + + /// Extracts the value from the `MaybeUninit` container. + /// + /// # Safety + /// + /// The caller must ensure that `self` is in an bit-valid state. Depending + /// on subsequent use, it may also need to be in a library-valid state. + #[inline(always)] + pub unsafe fn assume_init(self) -> T + where + T: Sized, + Self: Sized, + { + // SAFETY: The caller guarantees that `self` is in an bit-valid state. + unsafe { crate::util::transmute_unchecked(self) } + } +} + +impl fmt::Debug for MaybeUninit { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad(core::any::type_name::()) + } +} + #[cfg(test)] mod tests { use core::panic::AssertUnwindSafe; @@ -558,7 +679,7 @@ mod tests { } #[test] - fn test_copy_clone() { + fn test_unalign_copy_clone() { // Test that `Copy` and `Clone` do not cause soundness issues. This test // is mainly meant to exercise UB that would be caught by Miri. @@ -573,7 +694,7 @@ mod tests { } #[test] - fn test_trait_impls() { + fn test_unalign_trait_impls() { let zero = Unalign::new(0u8); let one = Unalign::new(1u8); @@ -600,4 +721,37 @@ mod tests { assert_eq!(format!("{}", zero), format!("{}", 0u8)); assert_eq!(format!("{}", one), format!("{}", 1u8)); } + + #[test] + #[allow(clippy::as_conversions)] + fn test_maybe_uninit() { + // int + { + let input = 42; + let uninit = MaybeUninit::new(input); + // SAFETY: `uninit` is in an initialized state + let output = unsafe { uninit.assume_init() }; + assert_eq!(input, output); + } + + // thin ref + { + let input = 42; + let uninit = MaybeUninit::new(&input); + // SAFETY: `uninit` is in an initialized state + let output = unsafe { uninit.assume_init() }; + assert_eq!(&input as *const _, output as *const _); + assert_eq!(input, *output); + } + + // wide ref + { + let input = [1, 2, 3, 4]; + let uninit = MaybeUninit::new(&input[..]); + // SAFETY: `uninit` is in an initialized state + let output = unsafe { uninit.assume_init() }; + assert_eq!(&input[..] as *const _, output as *const _); + assert_eq!(input, *output); + } + } } diff --git a/tests/ui-nightly/diagnostic-not-implemented-known-layout.stderr b/tests/ui-nightly/diagnostic-not-implemented-known-layout.stderr index 2509defb7b..812547839d 100644 --- a/tests/ui-nightly/diagnostic-not-implemented-known-layout.stderr +++ b/tests/ui-nightly/diagnostic-not-implemented-known-layout.stderr @@ -6,14 +6,14 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::KnownLayout` is not satisf | = note: Consider adding `#[derive(KnownLayout)]` to `NotZerocopy` = help: the following other types implement trait `zerocopy::KnownLayout`: + &T + &mut T () *const T *mut T AU16 AtomicBool AtomicI16 - AtomicI32 - AtomicI64 and $N others note: required by a bound in `takes_known_layout` --> tests/ui-nightly/diagnostic-not-implemented-known-layout.rs:21:26 diff --git a/tests/ui-stable/diagnostic-not-implemented-known-layout.stderr b/tests/ui-stable/diagnostic-not-implemented-known-layout.stderr index bfeec2263d..af5564c89f 100644 --- a/tests/ui-stable/diagnostic-not-implemented-known-layout.stderr +++ b/tests/ui-stable/diagnostic-not-implemented-known-layout.stderr @@ -6,14 +6,14 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::KnownLayout` is not satisf | = note: Consider adding `#[derive(KnownLayout)]` to `NotZerocopy` = help: the following other types implement trait `zerocopy::KnownLayout`: + &T + &mut T () *const T *mut T AU16 AtomicBool AtomicI16 - AtomicI32 - AtomicI64 and $N others note: required by a bound in `takes_known_layout` --> tests/ui-stable/diagnostic-not-implemented-known-layout.rs:21:26 diff --git a/zerocopy-derive/src/lib.rs b/zerocopy-derive/src/lib.rs index 776bf9efc3..a94ba719be 100644 --- a/zerocopy-derive/src/lib.rs +++ b/zerocopy-derive/src/lib.rs @@ -139,8 +139,10 @@ fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result Result::PointerMetadata; - - // SAFETY: `LAYOUT` accurately describes the layout of `Self`. - // The layout of `Self` is reflected using a sequence of - // invocations of `DstLayout::{new_zst,extend,pad_to_align}`. - // The documentation of these items vows that invocations in - // this manner will acurately describe a type, so long as: - // - // - that type is `repr(C)`, - // - its fields are enumerated in the order they appear, - // - the presence of `repr_align` and `repr_packed` are correctly accounted for. - // - // We respect all three of these preconditions here. This - // expansion is only used if `is_repr_c_struct`, we enumerate - // the fields in order, and we extract the values of `align(N)` - // and `packed(N)`. - const LAYOUT: ::zerocopy::DstLayout = { - use ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize; - use ::zerocopy::{DstLayout, KnownLayout}; - - let repr_align = #repr_align; - let repr_packed = #repr_packed; - - DstLayout::new_zst(repr_align) - #(.extend(DstLayout::for_type::<#leading_fields_tys>(), repr_packed))* - .extend(<#trailing_field_ty as KnownLayout>::LAYOUT, repr_packed) - .pad_to_align() - }; - + let make_methods = |trailing_field_ty| { + quote! { // SAFETY: // - The returned pointer has the same address and provenance as // `bytes`: @@ -238,8 +210,112 @@ fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result Self::PointerMetadata { <#trailing_field_ty>::pointer_to_metadata(ptr as *mut _) } - ), - ) + } + }; + + let inner_extras = { + let leading_fields_tys = leading_fields_tys.clone(); + let methods = make_methods(*trailing_field_ty); + let (_, ty_generics, _) = ast.generics.split_for_impl(); + + quote!( + type PointerMetadata = <#trailing_field_ty as ::zerocopy::KnownLayout>::PointerMetadata; + + type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit #ty_generics; + + // SAFETY: `LAYOUT` accurately describes the layout of `Self`. + // The layout of `Self` is reflected using a sequence of + // invocations of `DstLayout::{new_zst,extend,pad_to_align}`. + // The documentation of these items vows that invocations in + // this manner will acurately describe a type, so long as: + // + // - that type is `repr(C)`, + // - its fields are enumerated in the order they appear, + // - the presence of `repr_align` and `repr_packed` are correctly accounted for. + // + // We respect all three of these preconditions here. This + // expansion is only used if `is_repr_c_struct`, we enumerate + // the fields in order, and we extract the values of `align(N)` + // and `packed(N)`. + const LAYOUT: ::zerocopy::DstLayout = { + use ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize; + use ::zerocopy::{DstLayout, KnownLayout}; + + let repr_align = #repr_align; + let repr_packed = #repr_packed; + + DstLayout::new_zst(repr_align) + #(.extend(DstLayout::for_type::<#leading_fields_tys>(), repr_packed))* + .extend(<#trailing_field_ty as KnownLayout>::LAYOUT, repr_packed) + .pad_to_align() + }; + + #methods + ) + }; + + let outer_extras = { + let ident = &ast.ident; + let vis = &ast.vis; + let params = &ast.generics.params; + let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); + + let predicates = if let Some(where_clause) = where_clause { + where_clause.predicates.clone() + } else { + Default::default() + }; + + let methods = make_methods(&parse_quote! { + <#trailing_field_ty as ::zerocopy::KnownLayout>::MaybeUninit + }); + + quote! { + // SAFETY: This has the same layout as the derive target type, + // except that it admits uninit bytes. This is ensured by using the + // same repr as the target type, and by using field types which have + // the same layout as the target type's fields, except that they + // admit uninit bytes. + #repr + #[doc(hidden)] + #vis struct __ZerocopyKnownLayoutMaybeUninit<#params> ( + #(::zerocopy::util::macro_util::core_reexport::mem::MaybeUninit<#leading_fields_tys>,)* + <#trailing_field_ty as ::zerocopy::KnownLayout>::MaybeUninit + ) + where + #trailing_field_ty: ::zerocopy::KnownLayout, + #predicates; + + // SAFETY: We largely defer to the `KnownLayout` implementation on + // the derive target type (both by using the same tokens, and by + // deferring to impl via type-level indirection). This is sound, + // since `__ZerocopyKnownLayoutMaybeUninit` is guaranteed to + // have the same layout as the derive target type, except that + // `__ZerocopyKnownLayoutMaybeUninit` admits uninit bytes. + unsafe impl #impl_generics ::zerocopy::KnownLayout for __ZerocopyKnownLayoutMaybeUninit #ty_generics + where + #trailing_field_ty: ::zerocopy::KnownLayout, + // This bound may appear to be superfluous, but is required + // on our MSRV (1.55) to avoid an ICE. + <#trailing_field_ty as ::zerocopy::KnownLayout>::MaybeUninit: ::zerocopy::KnownLayout, + #predicates + { + #[allow(clippy::missing_inline_in_public_items)] + #[cfg_attr(coverage_nightly, coverage(off))] + fn only_derive_is_allowed_to_implement_this_trait() {} + + type PointerMetadata = <#ident #ty_generics as ::zerocopy::KnownLayout>::PointerMetadata; + + type MaybeUninit = Self; + + const LAYOUT: ::zerocopy::DstLayout = <#ident #ty_generics as ::zerocopy::KnownLayout>::LAYOUT; + + #methods + } + } + }; + + (SelfBounds::None, inner_extras, Some(outer_extras)) } else { // For enums, unions, and non-`repr(C)` structs, we require that // `Self` is sized, and as a result don't need to reason about the @@ -248,6 +324,8 @@ fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result; // SAFETY: `LAYOUT` is guaranteed to accurately describe the // layout of `Self`, because that is the documented safety @@ -270,6 +348,7 @@ fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result () {} ), + None, ) }; @@ -292,7 +371,8 @@ fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result { @@ -305,7 +385,8 @@ fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result { @@ -318,7 +399,8 @@ fn derive_known_layout_inner(ast: &DeriveInput, _top_level: Trait) -> Result TokenStream { SelfBounds::None, None, None, + None, ), Data::Enum(enm) => impl_block( ast, @@ -343,6 +426,7 @@ fn derive_no_cell_inner(ast: &DeriveInput, _top_level: Trait) -> TokenStream { SelfBounds::None, None, None, + None, ), Data::Union(unn) => impl_block( ast, @@ -352,6 +436,7 @@ fn derive_no_cell_inner(ast: &DeriveInput, _top_level: Trait) -> TokenStream { SelfBounds::None, None, None, + None, ), } } @@ -453,6 +538,7 @@ fn derive_try_from_bytes_struct( SelfBounds::None, None, Some(extras), + None, )) } @@ -511,6 +597,7 @@ fn derive_try_from_bytes_union( SelfBounds::None, None, Some(extras), + None, ) } @@ -547,6 +634,7 @@ fn derive_try_from_bytes_enum( SelfBounds::None, None, Some(extra), + None, )) } @@ -629,7 +717,16 @@ unsafe fn gen_trivial_is_bit_valid_unchecked() -> proc_macro2::TokenStream { /// A struct is `FromZeros` if: /// - all fields are `FromZeros` fn derive_from_zeros_struct(ast: &DeriveInput, strct: &DataStruct) -> TokenStream { - impl_block(ast, strct, Trait::FromZeros, FieldBounds::ALL_SELF, SelfBounds::None, None, None) + impl_block( + ast, + strct, + Trait::FromZeros, + FieldBounds::ALL_SELF, + SelfBounds::None, + None, + None, + None, + ) } /// Returns `Ok(index)` if variant `index` of the enum has a discriminant of @@ -765,6 +862,7 @@ fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> Result TokenStream { // compatibility with `derive(TryFromBytes)` on unions; not for soundness. let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]); - impl_block(ast, unn, Trait::FromZeros, field_type_trait_bounds, SelfBounds::None, None, None) + impl_block( + ast, + unn, + Trait::FromZeros, + field_type_trait_bounds, + SelfBounds::None, + None, + None, + None, + ) } /// A struct is `FromBytes` if: /// - all fields are `FromBytes` fn derive_from_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> TokenStream { - impl_block(ast, strct, Trait::FromBytes, FieldBounds::ALL_SELF, SelfBounds::None, None, None) + impl_block( + ast, + strct, + Trait::FromBytes, + FieldBounds::ALL_SELF, + SelfBounds::None, + None, + None, + None, + ) } /// An enum is `FromBytes` if: @@ -813,7 +929,16 @@ fn derive_from_bytes_enum(ast: &DeriveInput, enm: &DataEnum) -> Result TokenStream { // compatibility with `derive(TryFromBytes)` on unions; not for soundness. let field_type_trait_bounds = FieldBounds::All(&[TraitBound::Slf, TraitBound::Other(Trait::Immutable)]); - impl_block(ast, unn, Trait::FromBytes, field_type_trait_bounds, SelfBounds::None, None, None) + impl_block( + ast, + unn, + Trait::FromBytes, + field_type_trait_bounds, + SelfBounds::None, + None, + None, + None, + ) } fn derive_into_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> Result { @@ -913,6 +1047,7 @@ fn derive_into_bytes_struct(ast: &DeriveInput, strct: &DataStruct) -> Result Result Result Result Result impl '_ + Iterator( input: &DeriveInput, data: &D, @@ -1188,7 +1336,8 @@ fn impl_block( field_type_trait_bounds: FieldBounds, self_type_trait_bounds: SelfBounds, padding_check: Option, - extras: Option, + inner_extras: Option, + outer_extras: Option, ) -> TokenStream { // In this documentation, we will refer to this hypothetical struct: // @@ -1335,7 +1484,7 @@ fn impl_block( } }); - quote! { + let impl_tokens = quote! { // TODO(#553): Add a test that generates a warning when // `#[allow(deprecated)]` isn't present. #[allow(deprecated)] @@ -1348,8 +1497,22 @@ fn impl_block( { fn only_derive_is_allowed_to_implement_this_trait() {} - #extras + #inner_extras + } + }; + + if let Some(outer_extras) = outer_extras { + // So that any items defined in `#outer_extras` don't conflict with + // existing names defined in this scope. + quote! { + const _: () = { + #impl_tokens + + #outer_extras + }; } + } else { + impl_tokens } } diff --git a/zerocopy-derive/src/output_tests.rs b/zerocopy-derive/src/output_tests.rs index ba27213799..6b1931f80f 100644 --- a/zerocopy-derive/src/output_tests.rs +++ b/zerocopy-derive/src/output_tests.rs @@ -111,6 +111,8 @@ fn test_known_layout() { type PointerMetadata = (); + type MaybeUninit = ::zerocopy::util::macro_util::core_reexport::mem::MaybeUninit; + const LAYOUT: ::zerocopy::DstLayout = ::zerocopy::DstLayout::for_type::(); #[inline(always)] @@ -126,6 +128,105 @@ fn test_known_layout() { } } no_build } + + test! { + KnownLayout { + #[repr(C, align(2))] + struct Foo(T, U); + } + expands to { + const _: () = { + #[allow(deprecated)] + #[automatically_derived] + unsafe impl ::zerocopy::KnownLayout for Foo + where + U: ::zerocopy::KnownLayout, + { + fn only_derive_is_allowed_to_implement_this_trait() {} + type PointerMetadata = ::PointerMetadata; + type MaybeUninit = __ZerocopyKnownLayoutMaybeUninit; + const LAYOUT: ::zerocopy::DstLayout = { + use ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize; + use ::zerocopy::{DstLayout, KnownLayout}; + let repr_align = ::zerocopy::util::macro_util::core_reexport::num::NonZeroUsize::new( + 2u32 as usize, + ); + let repr_packed = ::zerocopy::util::macro_util::core_reexport::option::Option::None; + DstLayout::new_zst(repr_align) + .extend(DstLayout::for_type::(), repr_packed) + .extend(::LAYOUT, repr_packed) + .pad_to_align() + }; + #[inline(always)] + fn raw_from_ptr_len( + bytes: ::zerocopy::util::macro_util::core_reexport::ptr::NonNull, + meta: Self::PointerMetadata, + ) -> ::zerocopy::util::macro_util::core_reexport::ptr::NonNull { + use ::zerocopy::KnownLayout; + let trailing = ::raw_from_ptr_len(bytes, meta); + let slf = trailing.as_ptr() as *mut Self; + unsafe { + ::zerocopy::util::macro_util::core_reexport::ptr::NonNull::new_unchecked( + slf, + ) + } + } + #[inline(always)] + fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata { + ::pointer_to_metadata(ptr as *mut _) + } + } + #[repr(C)] + #[repr(align(2))] + #[doc(hidden)] + struct __ZerocopyKnownLayoutMaybeUninit( + ::zerocopy::util::macro_util::core_reexport::mem::MaybeUninit, + ::MaybeUninit, + ) + where + U: ::zerocopy::KnownLayout; + unsafe impl ::zerocopy::KnownLayout + for __ZerocopyKnownLayoutMaybeUninit + where + U: ::zerocopy::KnownLayout, + ::MaybeUninit: ::zerocopy::KnownLayout, + { + #[allow(clippy::missing_inline_in_public_items)] + #[cfg_attr(coverage_nightly, coverage(off))] + fn only_derive_is_allowed_to_implement_this_trait() {} + type PointerMetadata = as ::zerocopy::KnownLayout>::PointerMetadata; + type MaybeUninit = Self; + const LAYOUT: ::zerocopy::DstLayout = as ::zerocopy::KnownLayout>::LAYOUT; + #[inline(always)] + fn raw_from_ptr_len( + bytes: ::zerocopy::util::macro_util::core_reexport::ptr::NonNull, + meta: Self::PointerMetadata, + ) -> ::zerocopy::util::macro_util::core_reexport::ptr::NonNull { + use ::zerocopy::KnownLayout; + let trailing = <::MaybeUninit as KnownLayout>::raw_from_ptr_len( + bytes, + meta, + ); + let slf = trailing.as_ptr() as *mut Self; + unsafe { + ::zerocopy::util::macro_util::core_reexport::ptr::NonNull::new_unchecked( + slf, + ) + } + } + #[inline(always)] + fn pointer_to_metadata(ptr: *mut Self) -> Self::PointerMetadata { + <::MaybeUninit>::pointer_to_metadata( + ptr as *mut _, + ) + } + } + }; + } no_build + } } #[test] @@ -637,7 +738,7 @@ fn test_try_from_bytes_enum() { }) }; let variant = unsafe { variant.assume_initialized() }; - <___ZerocopyVariantStruct_StructLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( + <___ZerocopyVariantStruct_StructLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } ___ZEROCOPY_TAG_TupleLike => { @@ -647,7 +748,7 @@ fn test_try_from_bytes_enum() { }) }; let variant = unsafe { variant.assume_initialized() }; - <___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( + <___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } _ => false, @@ -934,7 +1035,7 @@ fn test_try_from_bytes_enum() { }) }; let variant = unsafe { variant.assume_initialized() }; - <___ZerocopyVariantStruct_StructLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( + <___ZerocopyVariantStruct_StructLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } ___ZEROCOPY_TAG_TupleLike => { @@ -944,7 +1045,7 @@ fn test_try_from_bytes_enum() { }) }; let variant = unsafe { variant.assume_initialized() }; - <___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( + <___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } _ => false, @@ -1231,7 +1332,7 @@ fn test_try_from_bytes_enum() { }) }; let variant = unsafe { variant.assume_initialized() }; - <___ZerocopyVariantStruct_StructLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( + <___ZerocopyVariantStruct_StructLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } ___ZEROCOPY_TAG_TupleLike => { @@ -1241,7 +1342,7 @@ fn test_try_from_bytes_enum() { }) }; let variant = unsafe { variant.assume_initialized() }; - <___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( + <___ZerocopyVariantStruct_TupleLike<'a, N, X, Y> as ::zerocopy ::TryFromBytes>::is_bit_valid ( variant) } _ => false, diff --git a/zerocopy-derive/tests/ui-msrv/mid_compile_pass.stderr b/zerocopy-derive/tests/ui-msrv/mid_compile_pass.stderr index 1d6d22df3f..9648856ad6 100644 --- a/zerocopy-derive/tests/ui-msrv/mid_compile_pass.stderr +++ b/zerocopy-derive/tests/ui-msrv/mid_compile_pass.stderr @@ -79,7 +79,10 @@ error[E0277]: the trait bound `T: KnownLayout` is not satisfied --> tests/ui-msrv/mid_compile_pass.rs:50:15 | 50 | assert_kl(kl) - | ^^ the trait `KnownLayout` is not implemented for `T` + | ^^ + | | + | expected an implementor of trait `KnownLayout` + | help: consider borrowing here: `&kl` | note: required because of the requirements on the impl of `KnownLayout` for `KL12` --> tests/ui-msrv/mid_compile_pass.rs:45:10 @@ -92,7 +95,3 @@ note: required by a bound in `assert_kl` 23 | fn assert_kl(_: &T) {} | ^^^^^^^^^^^ required by this bound in `assert_kl` = note: this error originates in the derive macro `KnownLayout` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider further restricting this bound - | -49 | fn test_kl12(kl: &KL12) { - | +++++++++++++++++++++++ diff --git a/zerocopy-derive/tests/ui-nightly/mid_compile_pass.stderr b/zerocopy-derive/tests/ui-nightly/mid_compile_pass.stderr index c80f1beee7..7793e88745 100644 --- a/zerocopy-derive/tests/ui-nightly/mid_compile_pass.stderr +++ b/zerocopy-derive/tests/ui-nightly/mid_compile_pass.stderr @@ -82,15 +82,14 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` 39 + fn test_kl06(kl: &KL06) { | -error[E0277]: the trait bound `T: KnownLayout` is not satisfied +error[E0277]: the trait bound `KL12: KnownLayout` is not satisfied --> tests/ui-nightly/mid_compile_pass.rs:50:15 | 50 | assert_kl(kl) - | --------- ^^ the trait `KnownLayout` is not implemented for `T` + | --------- ^^ the trait `KnownLayout` is not implemented for `KL12` | | | required by a bound introduced by this call | - = note: Consider adding `#[derive(KnownLayout)]` to `T` note: required for `KL12` to implement `KnownLayout` --> tests/ui-nightly/mid_compile_pass.rs:45:10 | @@ -102,7 +101,9 @@ note: required by a bound in `assert_kl` 23 | fn assert_kl(_: &T) {} | ^^^^^^^^^^^ required by this bound in `assert_kl` = note: this error originates in the derive macro `KnownLayout` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider further restricting this bound +help: consider borrowing here | -49 | fn test_kl12(kl: &KL12) { - | +++++++++++++++++++++++ +50 | assert_kl(&kl) + | + +50 | assert_kl(&mut kl) + | ++++ diff --git a/zerocopy-derive/tests/ui-nightly/struct.stderr b/zerocopy-derive/tests/ui-nightly/struct.stderr index 200a12683b..8952da8ddd 100644 --- a/zerocopy-derive/tests/ui-nightly/struct.stderr +++ b/zerocopy-derive/tests/ui-nightly/struct.stderr @@ -135,14 +135,14 @@ error[E0277]: the trait bound `NotKnownLayoutDst: zerocopy::KnownLayout` is not | = note: Consider adding `#[derive(KnownLayout)]` to `NotKnownLayoutDst` = help: the following other types implement trait `zerocopy::KnownLayout`: + &T + &mut T () *const T *mut T AU16 AtomicBool AtomicI16 - AtomicI32 - AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `KnownLayout` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -159,14 +159,14 @@ error[E0277]: the trait bound `NotKnownLayout: zerocopy::KnownLayout` is not sat | = note: Consider adding `#[derive(KnownLayout)]` to `NotKnownLayout` = help: the following other types implement trait `zerocopy::KnownLayout`: + &T + &mut T () *const T *mut T AU16 AtomicBool AtomicI16 - AtomicI32 - AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `KnownLayout` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/zerocopy-derive/tests/ui-stable/mid_compile_pass.stderr b/zerocopy-derive/tests/ui-stable/mid_compile_pass.stderr index 6b1875b036..476d1c672d 100644 --- a/zerocopy-derive/tests/ui-stable/mid_compile_pass.stderr +++ b/zerocopy-derive/tests/ui-stable/mid_compile_pass.stderr @@ -80,15 +80,14 @@ help: consider removing the `?Sized` bound to make the type parameter `Sized` 39 + fn test_kl06(kl: &KL06) { | -error[E0277]: the trait bound `T: KnownLayout` is not satisfied +error[E0277]: the trait bound `KL12: KnownLayout` is not satisfied --> tests/ui-stable/mid_compile_pass.rs:50:15 | 50 | assert_kl(kl) - | --------- ^^ the trait `KnownLayout` is not implemented for `T`, which is required by `KL12: KnownLayout` + | --------- ^^ the trait `KnownLayout` is not implemented for `KL12` | | | required by a bound introduced by this call | - = note: Consider adding `#[derive(KnownLayout)]` to `T` note: required for `KL12` to implement `KnownLayout` --> tests/ui-stable/mid_compile_pass.rs:45:10 | @@ -100,7 +99,9 @@ note: required by a bound in `assert_kl` 23 | fn assert_kl(_: &T) {} | ^^^^^^^^^^^ required by this bound in `assert_kl` = note: this error originates in the derive macro `KnownLayout` (in Nightly builds, run with -Z macro-backtrace for more info) -help: consider further restricting this bound +help: consider borrowing here | -49 | fn test_kl12(kl: &KL12) { - | +++++++++++++++++++++++ +50 | assert_kl(&kl) + | + +50 | assert_kl(&mut kl) + | ++++ diff --git a/zerocopy-derive/tests/ui-stable/struct.stderr b/zerocopy-derive/tests/ui-stable/struct.stderr index 789d291606..6e789457c4 100644 --- a/zerocopy-derive/tests/ui-stable/struct.stderr +++ b/zerocopy-derive/tests/ui-stable/struct.stderr @@ -124,14 +124,14 @@ error[E0277]: the trait bound `NotKnownLayoutDst: zerocopy::KnownLayout` is not | = note: Consider adding `#[derive(KnownLayout)]` to `NotKnownLayoutDst` = help: the following other types implement trait `zerocopy::KnownLayout`: + &T + &mut T () *const T *mut T AU16 AtomicBool AtomicI16 - AtomicI32 - AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `KnownLayout` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -144,14 +144,14 @@ error[E0277]: the trait bound `NotKnownLayout: zerocopy::KnownLayout` is not sat | = note: Consider adding `#[derive(KnownLayout)]` to `NotKnownLayout` = help: the following other types implement trait `zerocopy::KnownLayout`: + &T + &mut T () *const T *mut T AU16 AtomicBool AtomicI16 - AtomicI32 - AtomicI64 and $N others = help: see issue #48214 = note: this error originates in the derive macro `KnownLayout` (in Nightly builds, run with -Z macro-backtrace for more info)