diff --git a/src/lib.rs b/src/lib.rs index 351da7b98e..d8133fa06f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,7 +177,7 @@ use core::{ fmt::{self, Debug, Display, Formatter}, hash::Hasher, marker::PhantomData, - mem::{self, ManuallyDrop, MaybeUninit}, + mem::{self, ManuallyDrop}, num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping, @@ -229,11 +229,54 @@ pub unsafe trait KnownLayout: sealed::KnownLayoutSealed { #[doc(hidden)] const TRAILING_SLICE_ELEM_SIZE: Option; + /// A type which has the same layout as `Self`, but which has no validity + /// constraints. + /// + /// Roughly speaking, this type is equivalent to what the standard library's + /// [`MaybeUninit`] would be if it supported unsized types. + /// + /// # Safety + /// + /// For `T: KnownLayout`, the following must hold: + /// - Given `m: T::MaybeUninit`, it is sound to write any byte value, + /// including an uninitialized byte, at any byte offset in `m` + /// - `T` and `T::MaybeUninit` have the same alignment requirement + /// - It is valid to use an `as` cast to convert a `t: *const T` to a `m: + /// *const T::MaybeUninit` and vice-versa (and likewise for `*mut T`/`*mut + /// T::MaybeUninit`). Regardless of which direction the conversion was + /// performed, the sizes of the pointers' referents are always equal (in + /// terms of an API which is not yet stable, `size_of_val_raw(t) == + /// size_of_val_raw(m)`). + /// - `T::MaybeUninit` contains [`UnsafeCell`]s at exactly the same byte + /// ranges that `T` does. + /// + /// [`MaybeUninit`]: core::mem::MaybeUninit + /// [`UnsafeCell`]: core::cell::UnsafeCell + type MaybeUninit: ?Sized + KnownLayout; + /// SAFETY: The returned pointer has the same address and provenance as /// `bytes`. If `Self` is a DST, the returned pointer's referent has `elems` /// elements in its trailing slice. #[doc(hidden)] fn raw_from_ptr_len(bytes: NonNull, elems: usize) -> NonNull; + + /// Converts a pointer at the type level. + /// + /// # Safety + /// + /// Callers may assume that the memory region addressed by the return value + /// is the same as that addressed by the argument, and that both the return + /// value and the argument have the same provenance. + fn cast_from_maybe_uninit(maybe_uninit: NonNull) -> NonNull; + + /// Converts a pointer at the type level. + /// + /// # Safety + /// + /// Callers may assume that the memory region addressed by the return value + /// is the same as that addressed by the argument, and that both the return + /// value and the argument have the same provenance. + fn cast_to_maybe_uninit(slf: NonNull) -> NonNull; } impl sealed::KnownLayoutSealed for [T] {} @@ -253,6 +296,22 @@ unsafe impl KnownLayout for [T] { }; const TRAILING_SLICE_ELEM_SIZE: Option = Some(mem::size_of::()); + // SAFETY: + // - `MaybeUninit` has no bit validity requirements and `[U]` has the same + // bit validity requirements as `U`, so `[MaybeUninit]` has no bit + // validity requirements. Thus, it is sound to write any byte value, + // including an uninitialized byte, at any byte offset. + // - Since `MaybeUninit` has the same layout as `T`, and `[U]` has the + // same alignment as `U`, `[MaybeUninit]` has the same alignment as + // `[T]`. + // - `[T]` and `[MaybeUninit]` are both slice types, and so pointers can + // be converted using an `as` cast. Since `T` and `MaybeUninit` have + // the same size, and since such a cast preserves the number of elements + // in the slice, the referent slices themselves will have the same size. + // - `MaybeUninit` has the same field offsets as `[T]`, and so it + // contains `UnsafeCell`s at exactly the same byte ranges as `[T]`. + type MaybeUninit = [mem::MaybeUninit]; + // SAFETY: `.cast` preserves address and provenance. The returned pointer // refers to an object with `elems` elements by construction. #[inline(always)] @@ -261,6 +320,20 @@ unsafe impl KnownLayout for [T] { #[allow(unstable_name_collisions)] NonNull::slice_from_raw_parts(data.cast::(), elems) } + + fn cast_from_maybe_uninit(maybe_uninit: NonNull<[mem::MaybeUninit]>) -> NonNull<[T]> { + let (ptr, len) = (maybe_uninit.cast::(), maybe_uninit.len()); + // TODO(#67): Remove this allow. See NonNullExt for more details. + #[allow(unstable_name_collisions)] + NonNull::slice_from_raw_parts(ptr, len) + } + + fn cast_to_maybe_uninit(slf: NonNull<[T]>) -> NonNull<[mem::MaybeUninit]> { + let (ptr, len) = (slf.cast::>(), slf.len()); + // TODO(#67): Remove this allow. See NonNullExt for more details. + #[allow(unstable_name_collisions)] + NonNull::slice_from_raw_parts(ptr, len) + } } /// Implements `KnownLayout` for a sized type. @@ -297,11 +370,36 @@ macro_rules! impl_known_layout { // `T` is sized so it has no trailing slice. const TRAILING_SLICE_ELEM_SIZE: Option = None; + // SAFETY: + // - `MaybeUninit` has no validity requirements, so it is sound to + // write any byte value, including an uninitialized byte, at any + // offset. + // - `MaybeUninit` has the same layout as `T`, so they have the + // same alignment requirement. For the same reason, their sizes + // are equal. + // - Since their sizes are equal, raw pointers to both types are + // thin pointers, and thus can be converted using as casts. For + // the same reason, the sizes of these pointers' referents are + // always equal. + // - `MaybeUninit` has the same field offsets as `T`, and so it + // contains `UnsafeCell`s at exactly the same byte ranges as `T`. + type MaybeUninit = mem::MaybeUninit<$ty>; + // SAFETY: `.cast` preserves address and provenance. #[inline(always)] fn raw_from_ptr_len(bytes: NonNull, _elems: usize) -> NonNull { bytes.cast::() } + + // SAFETY: `.cast` preserves pointer address and provenance. + fn cast_from_maybe_uninit(maybe_uninit: NonNull) -> NonNull { + maybe_uninit.cast::() + } + + // SAFETY: `.cast` preserves pointer address and provenance. + fn cast_to_maybe_uninit(slf: NonNull) -> NonNull { + slf.cast::() + } } }; } @@ -317,16 +415,85 @@ impl_known_layout!( impl_known_layout!(T => Option); impl_known_layout!(T: ?Sized => PhantomData); impl_known_layout!(T => Wrapping); -impl_known_layout!(T => MaybeUninit); +impl_known_layout!(T => mem::MaybeUninit); impl_known_layout!(const N: usize, T => [T; N]); safety_comment! { /// SAFETY: /// `str` and `ManuallyDrop<[T]>` have the same representations as `[u8]` - /// and `[T]` repsectively. `str` has different bit validity than `[u8]`, - /// but that doesn't affect the soundness of this impl. + /// and `[T]` repsectively, including with respect to the locations of + /// `UnsafeCell`s. `str` has different bit validity than `[u8]`, but that + /// doesn't affect the soundness of this impl. unsafe_impl_known_layout!(#[repr([u8])] str); unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] ManuallyDrop); + /// SAFETY: + /// `Cell` and `UnsafeCell` have the same representations, including + /// (trivially) with respect to the locations of `UnsafeCell`s. + unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(cell::UnsafeCell)] cell::Cell); +} + +impl sealed::KnownLayoutSealed for cell::UnsafeCell {} +// SAFETY: See inline comments. +unsafe impl KnownLayout for cell::UnsafeCell { + // SAFETY: `UnsafeCell` and `T` have the same size, alignment, and + // trailing element size. + const FIXED_PREFIX_SIZE: usize = ::FIXED_PREFIX_SIZE; + const ALIGN: NonZeroUsize = ::ALIGN; + const TRAILING_SLICE_ELEM_SIZE: Option = ::TRAILING_SLICE_ELEM_SIZE; + + // SAFETY: + // - By `MaybeUninit` invariant, it is sound to write any byte - including + // an uninitialized byte - at any byte offset in + // `UnsafeCell`. + // - `UnsafeCell` and `T` have the same size, alignment, and trailing + // element size. Also, by `MaybeUninit` invariants: + // - `T` and `T::MaybeUninit` have the same alignment. + // - It is valid to cast `*const T` to `*const T::MaybeUninit` and + // vice-versa (and likewise for `*mut`), and these operations preserve + // pointer referent size. + // + // Thus, these properties hold between `UnsafeCell` and + // `UnsafeCell`. + // - `UnsafeCell` and `UnsafeCell` trivially have + // `UnsafeCell`s in exactly the same locations. + type MaybeUninit = cell::UnsafeCell<::MaybeUninit>; + + // SAFETY: All operations preserve address and provenance. Caller + // has promised that the `as` cast preserves size. + #[inline(always)] + fn raw_from_ptr_len(bytes: NonNull, elems: usize) -> NonNull { + let slf = T::raw_from_ptr_len(bytes, elems).as_ptr(); + #[allow(clippy::as_conversions)] + let slf = slf as *mut cell::UnsafeCell; + // SAFETY: `.as_ptr()` called on a non-null pointer. + unsafe { NonNull::new_unchecked(slf) } + } + + // SAFETY: All operations preserve pointer address and provenance. + fn cast_from_maybe_uninit(maybe_uninit: NonNull) -> NonNull { + #[allow(clippy::as_conversions)] + let maybe_uninit = maybe_uninit.as_ptr() as *mut ::MaybeUninit; + // SAFETY: `.as_ptr()` called on a non-null pointer. + let maybe_uninit = unsafe { NonNull::new_unchecked(maybe_uninit) }; + let repr = ::cast_from_maybe_uninit(maybe_uninit).as_ptr(); + #[allow(clippy::as_conversions)] + let slf = repr as *mut Self; + // SAFETY: `.as_ptr()` called on non-null pointer. + unsafe { NonNull::new_unchecked(slf) } + } + + // SAFETY: `.cast` preserves pointer address and provenance. + fn cast_to_maybe_uninit(slf: NonNull) -> NonNull { + #[allow(clippy::as_conversions)] + let repr = slf.as_ptr() as *mut T; + // SAFETY: `.as_ptr()` called on non-null pointer. + let repr = unsafe { NonNull::new_unchecked(repr) }; + let maybe_uninit = ::cast_to_maybe_uninit(repr).as_ptr(); + #[allow(clippy::as_conversions)] + let maybe_uninit = maybe_uninit as *mut cell::UnsafeCell; + // SAFETY: `.as_ptr()` called on non-null pointer. + unsafe { NonNull::new_unchecked(maybe_uninit) } + } } /// Types for which a sequence of bytes all set to zero represents a valid @@ -1133,17 +1300,16 @@ safety_comment! { /// - `Unaligned`: `MaybeUninit` is guaranteed by its documentation [1] /// to have the same alignment as `T`. /// - /// [1] - /// https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1 + /// [1] https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1 /// /// TODO(https://github.com/google/zerocopy/issues/251): If we split /// `FromBytes` and `RefFromBytes`, or if we introduce a separate /// `NoCell`/`Freeze` trait, we can relax the trait bounds for `FromZeroes` /// and `FromBytes`. - unsafe_impl!(T: FromZeroes => FromZeroes for MaybeUninit); - unsafe_impl!(T: FromBytes => FromBytes for MaybeUninit); - unsafe_impl!(T: Unaligned => Unaligned for MaybeUninit); - assert_unaligned!(MaybeUninit<()>, MaybeUninit); + unsafe_impl!(T: FromZeroes => FromZeroes for mem::MaybeUninit); + unsafe_impl!(T: FromBytes => FromBytes for mem::MaybeUninit); + unsafe_impl!(T: Unaligned => Unaligned for mem::MaybeUninit); + assert_unaligned!(mem::MaybeUninit<()>, mem::MaybeUninit); } safety_comment! { /// SAFETY: @@ -3648,8 +3814,11 @@ mod tests { assert_impls!(Option: FromZeroes, FromBytes, AsBytes, !Unaligned); assert_impls!(Option: FromZeroes, FromBytes, AsBytes, !Unaligned); - // Implements none of the ZC traits. + // Implements none of the ZC traits, but implements `KnownLayout` so + // that types like `MaybeUninit` can at least be written + // down without causing errors so that we can test them. struct NotZerocopy; + impl_known_layout!(NotZerocopy); assert_impls!(PhantomData: FromZeroes, FromBytes, AsBytes, Unaligned); assert_impls!(PhantomData<[u8]>: FromZeroes, FromBytes, AsBytes, Unaligned); @@ -3659,8 +3828,15 @@ mod tests { assert_impls!(ManuallyDrop: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); assert_impls!(ManuallyDrop<[NotZerocopy]>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(mem::MaybeUninit: FromZeroes, FromBytes, Unaligned, !AsBytes); + assert_impls!(mem::MaybeUninit: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(MaybeUninit: FromZeroes, FromBytes, Unaligned, !AsBytes); + assert_impls!(MaybeUninit>: FromZeroes, FromBytes, Unaligned, !AsBytes); + assert_impls!(MaybeUninit<[u8]>: FromZeroes, FromBytes, Unaligned, !AsBytes); + assert_impls!(MaybeUninit>: FromZeroes, FromBytes, Unaligned, !AsBytes); assert_impls!(MaybeUninit: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); + assert_impls!(MaybeUninit>: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); assert_impls!(Wrapping: FromZeroes, FromBytes, AsBytes, Unaligned); assert_impls!(Wrapping: !FromZeroes, !FromBytes, !AsBytes, !Unaligned); diff --git a/src/macros.rs b/src/macros.rs index 1c67ee6d90..dbca44d869 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -19,7 +19,7 @@ /// The macro invocations are emitted, each decorated with the following /// attribute: `#[allow(clippy::undocumented_unsafe_blocks)]`. macro_rules! safety_comment { - (#[doc = r" SAFETY:"] $(#[doc = $_doc:literal])* $($macro:ident!$args:tt;)*) => { + (#[doc = r" SAFETY:"] $(#[doc = $_doc:literal])* $($macro:ident!$args:tt; $(#[doc = r" SAFETY:"] $(#[doc = $__doc:literal])*)?)*) => { #[allow(clippy::undocumented_unsafe_blocks)] const _: () = { $($macro!$args;)* }; } @@ -199,6 +199,7 @@ macro_rules! impl_or_verify { /// - Fixed prefix size /// - Alignment /// - (For DSTs) trailing slice element size +/// - `UnsafeCell`s covering exactly the same byte ranges /// - It must be valid to perform an `as` cast from `*mut $repr` to `*mut $ty`, /// and this operation must preserve referent size (ie, `size_of_val_raw`). macro_rules! unsafe_impl_known_layout { @@ -211,14 +212,47 @@ macro_rules! unsafe_impl_known_layout { const ALIGN: NonZeroUsize = <$repr as KnownLayout>::ALIGN; const TRAILING_SLICE_ELEM_SIZE: Option = <$repr as KnownLayout>::TRAILING_SLICE_ELEM_SIZE; + // SAFETY: + // - By `MaybeUninit` invariant, it is sound to write any byte - + // including an uninitialized byte - at any byte offset in + // `$repr::MaybeUninit`. + // - Caller has promised that `$ty` and `$repr` have the same + // alignment, size, trailing element size, and `UnsafeCell` + // locations. Also, by `MaybeUninit` invariants: + // - `$repr` and `$repr::MaybeUninit` have the same alignment. + // - It is valid to cast `*const $repr` to `*const + // $repr::MaybeUninit` and vice-versa (and likewise for `*mut`), + // and these operations preserve pointer referent size. + // - `$repr` and `$repr::MaybeUninit` contain `UnsafeCell`s at + // exactly the same byte ranges. + // + // Thus, all of the same properties hold between `$ty` and + // `$repr::MaybeUninit`. + type MaybeUninit = <$repr as KnownLayout>::MaybeUninit; + // SAFETY: All operations preserve address and provenance. Caller // has promised that the `as` cast preserves size. #[inline(always)] fn raw_from_ptr_len(bytes: NonNull, elems: usize) -> NonNull { + Self::cast_from_maybe_uninit(Self::MaybeUninit::raw_from_ptr_len(bytes, elems)) + } + + // SAFETY: All operations preserve pointer address and provenance. + fn cast_from_maybe_uninit(maybe_uninit: NonNull) -> NonNull { + let repr = <$repr as KnownLayout>::cast_from_maybe_uninit(maybe_uninit).as_ptr(); + #[allow(clippy::as_conversions)] + let slf = repr as *mut Self; + // SAFETY: `.as_ptr()` called on non-null pointer. + unsafe { NonNull::new_unchecked(slf) } + } + + // SAFETY: `.cast` preserves pointer address and provenance. + fn cast_to_maybe_uninit(slf: NonNull) -> NonNull { #[allow(clippy::as_conversions)] - let ptr = <$repr>::raw_from_ptr_len(bytes, elems).as_ptr() as *mut Self; - // SAFETY: `ptr` was converted from `bytes`, which is non-null. - unsafe { NonNull::new_unchecked(ptr) } + let repr = slf.as_ptr() as *mut $repr; + // SAFETY: `.as_ptr()` called on non-null pointer. + let repr = unsafe { NonNull::new_unchecked(repr) }; + <$repr as KnownLayout>::cast_to_maybe_uninit(repr) } } }; diff --git a/src/wrappers.rs b/src/wrappers.rs index fb9b1d553a..ce7fcbea67 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -13,6 +13,191 @@ use core::{ use super::*; +/// An alternative to the standard library's [`MaybeUninit`] that supports +/// unsized types. +/// +/// `MaybeUninit` is identical to the standard library's `MaybeUninit` type +/// with the exception that it supports wrapping unsized types. Namely, +/// `MaybeUninit` has the same layout as `T`, but it has no bit validity +/// constraints - any byte of a `MaybeUninit` may have any value, including +/// uninitialized. +/// +/// [`MaybeUninit`]: core::mem::MaybeUninit +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct MaybeUninit { + inner: T::MaybeUninit, +} + +safety_comment! { + /// SAFETY: + /// Since `MaybeUninit` is a `repr(transparent)` wrapper around + /// `T::MaybeUninit`: + /// - They have the same prefix size, alignment, and trailing slice element + /// size + /// - It is valid to perform an `as` cast in either direction, and this + /// operation preserves referent size + unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T::MaybeUninit)] MaybeUninit); +} + +impl Debug for MaybeUninit { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.pad(core::any::type_name::()) + } +} + +impl MaybeUninit { + /// Gets a shared reference to the contained value. + /// + /// # Safety + /// + /// `self` must contain a validly-initialized `T`. + pub unsafe fn assume_init_ref(&self) -> &T { + let ptr = T::cast_from_maybe_uninit(NonNull::from(&self.inner)); + // SAFETY: The caller has promised that `self` contains an initialized + // `T`. Since `Self` is `repr(transparent)`, it has the same layout as + // `T::MaybeUninit`, which in turn is guaranteed (by safety invariant) + // to have the same layout as `T`. Thus, it is sound to treat `ptr` as + // pointing to a valid `T` of the correct size and alignment. + // + // Further, thanks to the safety requirements on `T::MaybeUninit`, we + // know that there are `UnsafeCell`s at the same byte ranges in both + // types. See [1] for a discussion of why this is a required safety + // condition. + // + // [1] https://github.com/rust-lang/unsafe-code-guidelines/issues/455 + unsafe { &*ptr.as_ptr() } + } + + /// Gets a mutable reference to the contained value. + /// + /// # Safety + /// + /// `self` must contain a validly-initialized `T`. + pub unsafe fn assume_init_mut(&mut self) -> &mut T { + let ptr = T::cast_from_maybe_uninit(NonNull::from(&mut self.inner)); + // SAFETY: The caller has promised that `self` contains an initialized + // `T`. Since `Self` is `repr(transparent)`, it has the same layout as + // `T::MaybeUninit`, which in turn is guaranteed (by safety invariant) + // to have the same layout as `T`. Thus, it is sound to treat `ptr` as + // pointing to a valid `T` of the correct size and alignment. + // + // Further, thanks to the safety requirements on `T::MaybeUninit`, we + // know that there are `UnsafeCell`s at the same byte ranges in both + // types. See [1] for a discussion of why this is a required safety + // condition. + // + // [1] https://github.com/rust-lang/unsafe-code-guidelines/issues/455 + unsafe { &mut *ptr.as_ptr() } + } + + /// Gets a pointer to the contained value. + /// + /// Reading from this pointer or turning it into a reference is undefined + /// behavior unless `self` is initialized. Writing to memory that this + /// pointer (non-transitively) points to is undefined behavior (except + /// inside an `UnsafeCell`). + pub fn as_ptr(&self) -> *const T { + T::cast_from_maybe_uninit(NonNull::from(&self.inner)).as_ptr().cast_const() + } + + /// Gets a mutable pointer to the contained value. + /// + /// Reading from this pointer or turning it into a reference is undefined + /// behavior unless `self` is initialized. + pub fn as_mut_ptr(&mut self) -> *mut T { + T::cast_from_maybe_uninit(NonNull::from(&mut self.inner)).as_ptr() + } + + /// Gets a view of this `&T` as a `&MaybeUninit`. + /// + /// There is no mutable equivalent to this function, as producing a `&mut + /// MaybeUninit` from a `&mut T` would allow safe code to write invalid + /// values which would be accessible through `&mut T`. + pub fn from_ref(r: &T) -> &MaybeUninit { + let ptr = T::cast_to_maybe_uninit(NonNull::from(r)); + #[allow(clippy::as_conversions)] + let ptr = ptr.as_ptr() as *const MaybeUninit; + // SAFETY: Since `Self` is `repr(transparent)`, it has the same layout + // as `T::MaybeUninit`, so the size and alignment here are valid. + // + // `MaybeUninit` has no bit validity constraints, so this is + // guaranteed not to produce in invalid `MaybeUninit`. If it were + // possible to write a different value for `MaybeUninit` through the + // returned reference, it could result in an invalid value being exposed + // via the `&T`. Luckily, the only way for mutation to happen is if `T` + // contains an `UnsafeCell` and the caller uses it to perform interior + // mutation. Importantly, `T` containing an `UnsafeCell` does not permit + // interior mutation through `MaybeUninit`, so it doesn't permit + // writing uninitialized or otherwise invalid values which would be + // visible through the original `&T`. + unsafe { &*ptr } + } +} + +impl> + Sized> MaybeUninit { + /// Creates a new `MaybeUninit` with the given value. + pub const fn new(val: T) -> MaybeUninit { + MaybeUninit { inner: mem::MaybeUninit::new(val) } + } + + /// Creates a new `MaybeUninit` in an uninitialized state. + pub const fn uninit() -> MaybeUninit { + MaybeUninit { inner: mem::MaybeUninit::uninit() } + } + + /// Creates a new `MaybeUninit` whose bytes are initialized to 0. + // TODO(https://github.com/rust-lang/rust/issues/91850): Make this const + // once `MaybeUninit::zeroed` is const. + pub fn zeroed() -> MaybeUninit { + MaybeUninit { inner: mem::MaybeUninit::zeroed() } + } + + /// Extracts the value from the `MaybeUninit` container. + /// + /// # Safety + /// + /// `assume_init` has the same safety requirements and guarantees as the + /// standard library's [`MaybeUninit::assume_init`] method. + /// + /// [`MaybeUninit::assume_init`]: mem::MaybeUninit::assume_init + pub const unsafe fn assume_init(self) -> T { + // SAFETY: The caller has promised to uphold the safety invariants of + // the exact function we're calling here. Since, for `T: Sized`, + // `MaybeUninit` is a `repr(transparent)` wrapper around + // `mem::MaybeUninit`, it is sound to treat `Self` as equivalent to a + // `mem::MaybeUninit` for the purposes of + // `mem::MaybeUninit::assume_init`'s safety invariants. + unsafe { self.inner.assume_init() } + } +} + +safety_comment! { + // `MaybeUninit` is `FromZeroes` and `FromBytes`, but never `AsBytes` + // since it may contain uninitialized bytes. + // + /// SAFETY: + /// - `FromZeroes`, `FromBytes`: `MaybeUninit` has no restrictions on its + /// contents. Unfortunately, in addition to bit validity, `FromZeroes` and + /// `FromBytes` also require that implementers contain no `UnsafeCell`s. + /// Thus, we require `T: FromZeroes` and `T: FromBytes` in order to ensure + /// that `T` - and thus `MaybeUninit` - contains to `UnsafeCell`s. + /// Thus, requiring that `T` implement each of these traits is sufficient + /// - `Unaligned`: `MaybeUninit` is guaranteed by its documentation [1] + /// to have the same alignment as `T`. + /// + /// [1] https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1 + /// + /// TODO(https://github.com/google/zerocopy/issues/251): If we split + /// `FromBytes` and `RefFromBytes`, or if we introduce a separate + /// `NoCell`/`Freeze` trait, we can relax the trait bounds for `FromZeroes` + /// and `FromBytes`. + unsafe_impl!(T: ?Sized + KnownLayout + FromZeroes => FromZeroes for MaybeUninit); + unsafe_impl!(T: ?Sized + KnownLayout + FromBytes => FromBytes for MaybeUninit); + unsafe_impl!(T: ?Sized + KnownLayout + Unaligned => Unaligned for MaybeUninit); + assert_unaligned!(mem::MaybeUninit<()>, MaybeUninit); +} + /// A type with no alignment requirement. /// /// An `Unalign` wraps a `T`, removing any alignment requirement. `Unalign` @@ -394,6 +579,78 @@ mod tests { } } + #[test] + fn test_maybe_uninit() { + let m = MaybeUninit::new(1usize); + // SAFETY: `m` was initialized with a valid `usize`. + assert_eq!(unsafe { m.assume_init() }, 1); + + let mut m = MaybeUninit::::uninit(); + // SAFETY: Writing a valid `usize`. + unsafe { core::ptr::write(m.as_mut_ptr(), 1) }; + // SAFETY: We just initialized `m`. + assert_eq!(unsafe { m.assume_init_ref() }, &1); + // SAFETY: We just initialized `m`. + assert_eq!(unsafe { m.assume_init_mut() }, &mut 1); + // SAFETY: We just initialized `m`. + assert_eq!(unsafe { m.assume_init() }, 1); + + let mut bytes = [0u8, 1, 2]; + let bytes_mut = &mut bytes[..]; + + // SAFETY: `MaybeUninit<[u8]>` has the same layout as `[u8]`. + let m = unsafe { + // Assign to `m` rather than leaving as a trailing expression + // because annotations on expressions are unstable. + #[allow(clippy::as_conversions)] + let m = &mut *(bytes_mut as *mut [u8] as *mut MaybeUninit<[u8]>); + m + }; + + // // SAFETY: `m` was created from an initialized value. + // let r = unsafe { m.assume_init_ref() }; + // assert_eq!(r.len(), 3); + // assert_eq!(r, [0, 1, 2]); + + // SAFETY: `m` was created from an initialized value. + let r = unsafe { m.assume_init_mut() }; + assert_eq!(r.len(), 3); + assert_eq!(r, [0, 1, 2]); + + r[0] = 1; + assert_eq!(bytes, [1, 1, 2]); + } + + #[test] + fn test_maybe_uninit_zeroed() { + let m = MaybeUninit::::zeroed(); + // SAFETY: `m` was initialized with zeroes, which constitute a valid + // instance of `usize`. + assert_eq!(unsafe { m.assume_init() }, 0); + } + + #[test] + fn test_maybe_uninit_from_ref() { + use core::cell::Cell; + + let u = 1usize; + let m = MaybeUninit::from_ref(&u); + // SAFETY: `m` was constructed from a valid `&usize`. + assert_eq!(unsafe { m.assume_init_ref() }, &1usize); + + // Test that interior mutability doesn't affect correctness or + // soundness. + + let c = Cell::new(1usize); + let m = MaybeUninit::from_ref(&c); + // SAFETY: `m` was constructed from a valid `&usize`. + assert_eq!(unsafe { m.assume_init_ref() }, &Cell::new(1)); + + c.set(2); + // SAFETY: `m` was constructed from a valid `&usize`. + assert_eq!(unsafe { m.assume_init_ref() }, &Cell::new(2)); + } + #[test] fn test_unalign() { // Test methods that don't depend on alignment.