Skip to content

Commit

Permalink
Add KnownLayout::MaybeUninit type
Browse files Browse the repository at this point in the history
Closes #1797
  • Loading branch information
joshlf authored and jswrenn committed Oct 11, 2024
1 parent fa7d56d commit 55749cb
Show file tree
Hide file tree
Showing 9 changed files with 1,415 additions and 1,019 deletions.
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ fn main() {
println!(
"cargo:rustc-check-cfg=cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)"
);
println!("cargo:rustc-check-cfg=cfg(zerocopy_unstable)");
println!("cargo:rustc-check-cfg=cfg(coverage_nightly)");
}

Expand Down
30 changes: 16 additions & 14 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down Expand Up @@ -630,14 +632,14 @@ safety_comment! {
/// SAFETY:
/// `TryFromBytes` (with no validator), `FromZeros`, `FromBytes`:
/// `MaybeUninit<T>` has no restrictions on its contents.
unsafe_impl!(T => TryFromBytes for MaybeUninit<T>);
unsafe_impl!(T => FromZeros for MaybeUninit<T>);
unsafe_impl!(T => FromBytes for MaybeUninit<T>);
unsafe_impl!(T => TryFromBytes for CoreMaybeUninit<T>);
unsafe_impl!(T => FromZeros for CoreMaybeUninit<T>);
unsafe_impl!(T => FromBytes for CoreMaybeUninit<T>);
}

impl_for_transparent_wrapper!(T: Immutable => Immutable for MaybeUninit<T>);
impl_for_transparent_wrapper!(T: Unaligned => Unaligned for MaybeUninit<T>);
assert_unaligned!(MaybeUninit<()>, MaybeUninit<u8>);
impl_for_transparent_wrapper!(T: Immutable => Immutable for CoreMaybeUninit<T>);
impl_for_transparent_wrapper!(T: Unaligned => Unaligned for CoreMaybeUninit<T>);
assert_unaligned!(CoreMaybeUninit<()>, CoreMaybeUninit<u8>);

impl_for_transparent_wrapper!(T: ?Sized + Immutable => Immutable for ManuallyDrop<T>);
impl_for_transparent_wrapper!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop<T>);
Expand Down Expand Up @@ -1254,8 +1256,8 @@ mod tests {
ManuallyDrop<UnsafeCell<()>>,
ManuallyDrop<[UnsafeCell<u8>]>,
ManuallyDrop<[UnsafeCell<bool>]>,
MaybeUninit<NotZerocopy>,
MaybeUninit<UnsafeCell<()>>,
CoreMaybeUninit<NotZerocopy>,
CoreMaybeUninit<UnsafeCell<()>>,
Wrapping<UnsafeCell<()>>
);

Expand Down Expand Up @@ -1297,9 +1299,9 @@ mod tests {
Option<FnManyArgs>,
Option<extern "C" fn()>,
Option<ECFnManyArgs>,
MaybeUninit<u8>,
MaybeUninit<NotZerocopy>,
MaybeUninit<UnsafeCell<()>>,
CoreMaybeUninit<u8>,
CoreMaybeUninit<NotZerocopy>,
CoreMaybeUninit<UnsafeCell<()>>,
ManuallyDrop<UnsafeCell<()>>,
ManuallyDrop<[UnsafeCell<u8>]>,
ManuallyDrop<[UnsafeCell<bool>]>,
Expand Down Expand Up @@ -1761,9 +1763,9 @@ mod tests {
assert_impls!(ManuallyDrop<[UnsafeCell<u8>]>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !Immutable);
assert_impls!(ManuallyDrop<[UnsafeCell<bool>]>: KnownLayout, TryFromBytes, FromZeros, IntoBytes, Unaligned, !Immutable, !FromBytes);

assert_impls!(MaybeUninit<u8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, Unaligned, !IntoBytes);
assert_impls!(MaybeUninit<NotZerocopy>: KnownLayout, TryFromBytes, FromZeros, FromBytes, !Immutable, !IntoBytes, !Unaligned);
assert_impls!(MaybeUninit<UnsafeCell<()>>: KnownLayout, TryFromBytes, FromZeros, FromBytes, Unaligned, !Immutable, !IntoBytes);
assert_impls!(CoreMaybeUninit<u8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, Unaligned, !IntoBytes);
assert_impls!(CoreMaybeUninit<NotZerocopy>: KnownLayout, TryFromBytes, FromZeros, FromBytes, !Immutable, !IntoBytes, !Unaligned);
assert_impls!(CoreMaybeUninit<UnsafeCell<()>>: KnownLayout, TryFromBytes, FromZeros, FromBytes, Unaligned, !Immutable, !IntoBytes);

assert_impls!(Wrapping<u8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
// This test is important because it allows us to test our hand-rolled
Expand Down
52 changes: 46 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,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,
Expand Down Expand Up @@ -724,6 +724,22 @@ pub unsafe trait KnownLayout {
/// This is `()` for sized types and `usize` for slice DSTs.
type PointerMetadata: PointerMetadata;

/// A maybe-uninitialized analog of `Self`
///
/// # Safety
///
/// Users may assume that:
/// - `Self` and `Self::MaybeUninit` have the same:
/// - Fixed prefix size
/// - Alignment
/// - (For DSTs) trailing slice element size
/// - It is valid to perform an `as` cast from `*mut Self` and `*mut
/// Self::MaybeUninit`, and this operation preserves referent size (ie,
/// `size_of_val_raw`).
#[cfg(zerocopy_unstable)]
#[doc(hidden)]
type MaybeUninit: ?Sized + KnownLayout<PointerMetadata = Self::PointerMetadata>;

/// The layout of `Self`.
///
/// # Safety
Expand Down Expand Up @@ -856,6 +872,17 @@ unsafe impl<T> KnownLayout for [T] {

type PointerMetadata = usize;

// SAFETY: `CoreMaybeUninit<T>` has the same size and alignment as `T`.
// Consequently, `[CoreMaybeUninit<T>]` has the same size and alignment as
// `[T]`, becuase `CoreMaybeUninit<T>` has the same size and alignment as
// `T` [1].
//
// [1] Per https://doc.rust-lang.org/1.81.0/std/mem/union.MaybeUninit.html#layout-1:
//
// `MaybeUninit<T>` is guaranteed to have the same size, alignment, and ABI as
// `T`
type MaybeUninit = [CoreMaybeUninit<T>];

const LAYOUT: DstLayout = DstLayout::for_slice::<T>();

// SAFETY: `.cast` preserves address and provenance. The returned pointer
Expand Down Expand Up @@ -908,7 +935,7 @@ impl_known_layout!(
T => Option<T>,
T: ?Sized => PhantomData<T>,
T => Wrapping<T>,
T => MaybeUninit<T>,
T => CoreMaybeUninit<T>,
T: ?Sized => *const T,
T: ?Sized => *mut T
);
Expand Down Expand Up @@ -941,6 +968,19 @@ safety_comment! {
unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] UnsafeCell<T>);
}

safety_comment! {
/// SAFETY:
/// - By invariant on `KnownLayout::MaybeUninit`, `T` and `T::MaybeUninit`
/// have the same:
/// - Fixed prefix size
/// - Alignment
/// - (For DSTs) trailing slice element size
/// - By invariant on `KnownLayout`, 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<T>);
}

/// Analyzes whether a type is [`FromZeros`].
///
/// This derive analyzes, at compile time, whether the annotated type satisfies
Expand Down Expand Up @@ -2539,7 +2579,7 @@ pub unsafe trait TryFromBytes {
where
Self: Sized,
{
let candidate = match MaybeUninit::<Self>::read_from_bytes(source) {
let candidate = match CoreMaybeUninit::<Self>::read_from_bytes(source) {
Ok(candidate) => candidate,
Err(e) => {
return Err(TryReadError::Size(e.with_dst()));
Expand Down Expand Up @@ -2600,7 +2640,7 @@ pub unsafe trait TryFromBytes {
where
Self: Sized,
{
let (candidate, suffix) = match MaybeUninit::<Self>::read_from_prefix(source) {
let (candidate, suffix) = match CoreMaybeUninit::<Self>::read_from_prefix(source) {
Ok(candidate) => candidate,
Err(e) => {
return Err(TryReadError::Size(e.with_dst()));
Expand Down Expand Up @@ -2662,7 +2702,7 @@ pub unsafe trait TryFromBytes {
where
Self: Sized,
{
let (prefix, candidate) = match MaybeUninit::<Self>::read_from_suffix(source) {
let (prefix, candidate) = match CoreMaybeUninit::<Self>::read_from_suffix(source) {
Ok(candidate) => candidate,
Err(e) => {
return Err(TryReadError::Size(e.with_dst()));
Expand Down Expand Up @@ -2735,7 +2775,7 @@ fn swap<T, U>((t, u): (T, U)) -> (U, T) {
#[inline(always)]
unsafe fn try_read_from<S, T: TryFromBytes>(
source: S,
mut candidate: MaybeUninit<T>,
mut candidate: CoreMaybeUninit<T>,
) -> Result<T, TryReadError<S, T>> {
// We use `from_mut` despite not mutating via `c_ptr` so that we don't need
// to add a `T: Immutable` bound.
Expand Down
3 changes: 3 additions & 0 deletions src/util/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,8 @@ macro_rules! impl_known_layout {

type PointerMetadata = ();

type MaybeUninit = core::mem::MaybeUninit<Self>;

const LAYOUT: crate::DstLayout = crate::DstLayout::for_type::<$ty>();

// SAFETY: `.cast` preserves address and provenance.
Expand Down Expand Up @@ -572,6 +574,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;

Expand Down
16 changes: 16 additions & 0 deletions src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,22 @@ pub(crate) unsafe fn copy_unchecked(src: &[u8], dst: &mut [u8]) {
};
}

/// Unsafely transmutes the given `src` into a type `Dst`.
///
/// # Safety
///
/// TODO. Same as it ever was.
#[inline(always)]
pub(crate) const unsafe fn transmute_unchecked<Src, Dst>(src: Src) -> Dst {
static_assert!(Src, Dst => core::mem::size_of::<Src>() >= core::mem::size_of::<Dst>());
#[repr(C)]
union Transmute<Src, Dst> {
src: ManuallyDrop<Src>,
dst: ManuallyDrop<Dst>,
}
unsafe { ManuallyDrop::into_inner(Transmute { src: ManuallyDrop::new(src) }.dst) }
}

/// 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
Expand Down
Loading

0 comments on commit 55749cb

Please sign in to comment.