Skip to content

Commit aad95aa

Browse files
joshlfjswrenn
authored andcommitted
Add KnownLayout::MaybeUninit type
Closes #1797
1 parent 7f2d45d commit aad95aa

File tree

8 files changed

+1335
-1021
lines changed

8 files changed

+1335
-1021
lines changed

build.rs

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ fn main() {
8383
println!(
8484
"cargo:rustc-check-cfg=cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)"
8585
);
86+
println!("cargo:rustc-check-cfg=cfg(zerocopy_unstable)");
8687
println!("cargo:rustc-check-cfg=cfg(coverage_nightly)");
8788
}
8889

src/impls.rs

+16-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
// This file may not be copied, modified, or distributed except according to
88
// those terms.
99

10+
use core::mem::MaybeUninit as CoreMaybeUninit;
11+
1012
use super::*;
1113

1214
safety_comment! {
@@ -626,14 +628,14 @@ safety_comment! {
626628
/// SAFETY:
627629
/// `TryFromBytes` (with no validator), `FromZeros`, `FromBytes`:
628630
/// `MaybeUninit<T>` has no restrictions on its contents.
629-
unsafe_impl!(T => TryFromBytes for MaybeUninit<T>);
630-
unsafe_impl!(T => FromZeros for MaybeUninit<T>);
631-
unsafe_impl!(T => FromBytes for MaybeUninit<T>);
631+
unsafe_impl!(T => TryFromBytes for CoreMaybeUninit<T>);
632+
unsafe_impl!(T => FromZeros for CoreMaybeUninit<T>);
633+
unsafe_impl!(T => FromBytes for CoreMaybeUninit<T>);
632634
}
633635

634-
impl_for_transparent_wrapper!(T: Immutable => Immutable for MaybeUninit<T>);
635-
impl_for_transparent_wrapper!(T: Unaligned => Unaligned for MaybeUninit<T>);
636-
assert_unaligned!(MaybeUninit<()>, MaybeUninit<u8>);
636+
impl_for_transparent_wrapper!(T: Immutable => Immutable for CoreMaybeUninit<T>);
637+
impl_for_transparent_wrapper!(T: Unaligned => Unaligned for CoreMaybeUninit<T>);
638+
assert_unaligned!(CoreMaybeUninit<()>, CoreMaybeUninit<u8>);
637639

638640
impl_for_transparent_wrapper!(T: ?Sized + Immutable => Immutable for ManuallyDrop<T>);
639641
impl_for_transparent_wrapper!(T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop<T>);
@@ -1250,8 +1252,8 @@ mod tests {
12501252
ManuallyDrop<UnsafeCell<()>>,
12511253
ManuallyDrop<[UnsafeCell<u8>]>,
12521254
ManuallyDrop<[UnsafeCell<bool>]>,
1253-
MaybeUninit<NotZerocopy>,
1254-
MaybeUninit<UnsafeCell<()>>,
1255+
CoreMaybeUninit<NotZerocopy>,
1256+
CoreMaybeUninit<UnsafeCell<()>>,
12551257
Wrapping<UnsafeCell<()>>
12561258
);
12571259

@@ -1293,9 +1295,9 @@ mod tests {
12931295
Option<FnManyArgs>,
12941296
Option<extern "C" fn()>,
12951297
Option<ECFnManyArgs>,
1296-
MaybeUninit<u8>,
1297-
MaybeUninit<NotZerocopy>,
1298-
MaybeUninit<UnsafeCell<()>>,
1298+
CoreMaybeUninit<u8>,
1299+
CoreMaybeUninit<NotZerocopy>,
1300+
CoreMaybeUninit<UnsafeCell<()>>,
12991301
ManuallyDrop<UnsafeCell<()>>,
13001302
ManuallyDrop<[UnsafeCell<u8>]>,
13011303
ManuallyDrop<[UnsafeCell<bool>]>,
@@ -1757,9 +1759,9 @@ mod tests {
17571759
assert_impls!(ManuallyDrop<[UnsafeCell<u8>]>: KnownLayout, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned, !Immutable);
17581760
assert_impls!(ManuallyDrop<[UnsafeCell<bool>]>: KnownLayout, TryFromBytes, FromZeros, IntoBytes, Unaligned, !Immutable, !FromBytes);
17591761

1760-
assert_impls!(MaybeUninit<u8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, Unaligned, !IntoBytes);
1761-
assert_impls!(MaybeUninit<NotZerocopy>: KnownLayout, TryFromBytes, FromZeros, FromBytes, !Immutable, !IntoBytes, !Unaligned);
1762-
assert_impls!(MaybeUninit<UnsafeCell<()>>: KnownLayout, TryFromBytes, FromZeros, FromBytes, Unaligned, !Immutable, !IntoBytes);
1762+
assert_impls!(CoreMaybeUninit<u8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, Unaligned, !IntoBytes);
1763+
assert_impls!(CoreMaybeUninit<NotZerocopy>: KnownLayout, TryFromBytes, FromZeros, FromBytes, !Immutable, !IntoBytes, !Unaligned);
1764+
assert_impls!(CoreMaybeUninit<UnsafeCell<()>>: KnownLayout, TryFromBytes, FromZeros, FromBytes, Unaligned, !Immutable, !IntoBytes);
17631765

17641766
assert_impls!(Wrapping<u8>: KnownLayout, Immutable, TryFromBytes, FromZeros, FromBytes, IntoBytes, Unaligned);
17651767
// This test is important because it allows us to test our hand-rolled

src/lib.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ use core::{
349349
fmt::{self, Debug, Display, Formatter},
350350
hash::Hasher,
351351
marker::PhantomData,
352-
mem::{self, ManuallyDrop, MaybeUninit},
352+
mem::{self, ManuallyDrop, MaybeUninit as CoreMaybeUninit},
353353
num::{
354354
NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128,
355355
NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, Wrapping,
@@ -724,6 +724,9 @@ pub unsafe trait KnownLayout {
724724
/// This is `()` for sized types and `usize` for slice DSTs.
725725
type PointerMetadata: PointerMetadata;
726726

727+
#[doc(hidden)]
728+
type MaybeUninit: ?Sized + KnownLayout<PointerMetadata = Self::PointerMetadata>;
729+
727730
/// The layout of `Self`.
728731
///
729732
/// # Safety
@@ -856,6 +859,8 @@ unsafe impl<T> KnownLayout for [T] {
856859

857860
type PointerMetadata = usize;
858861

862+
type MaybeUninit = [CoreMaybeUninit<T>];
863+
859864
const LAYOUT: DstLayout = DstLayout::for_slice::<T>();
860865

861866
// SAFETY: `.cast` preserves address and provenance. The returned pointer
@@ -908,7 +913,7 @@ impl_known_layout!(
908913
T => Option<T>,
909914
T: ?Sized => PhantomData<T>,
910915
T => Wrapping<T>,
911-
T => MaybeUninit<T>,
916+
T => CoreMaybeUninit<T>,
912917
T: ?Sized => *const T,
913918
T: ?Sized => *mut T
914919
);
@@ -941,6 +946,12 @@ safety_comment! {
941946
unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] UnsafeCell<T>);
942947
}
943948

949+
safety_comment! {
950+
/// SAFETY:
951+
/// TODO
952+
unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T::MaybeUninit)] MaybeUninit<T>);
953+
}
954+
944955
/// Analyzes whether a type is [`FromZeros`].
945956
///
946957
/// This derive analyzes, at compile time, whether the annotated type satisfies
@@ -2539,7 +2550,7 @@ pub unsafe trait TryFromBytes {
25392550
where
25402551
Self: Sized,
25412552
{
2542-
let candidate = match MaybeUninit::<Self>::read_from_bytes(source) {
2553+
let candidate = match CoreMaybeUninit::<Self>::read_from_bytes(source) {
25432554
Ok(candidate) => candidate,
25442555
Err(e) => {
25452556
return Err(TryReadError::Size(e.with_dst()));
@@ -2600,7 +2611,7 @@ pub unsafe trait TryFromBytes {
26002611
where
26012612
Self: Sized,
26022613
{
2603-
let (candidate, suffix) = match MaybeUninit::<Self>::read_from_prefix(source) {
2614+
let (candidate, suffix) = match CoreMaybeUninit::<Self>::read_from_prefix(source) {
26042615
Ok(candidate) => candidate,
26052616
Err(e) => {
26062617
return Err(TryReadError::Size(e.with_dst()));
@@ -2662,7 +2673,7 @@ pub unsafe trait TryFromBytes {
26622673
where
26632674
Self: Sized,
26642675
{
2665-
let (prefix, candidate) = match MaybeUninit::<Self>::read_from_suffix(source) {
2676+
let (prefix, candidate) = match CoreMaybeUninit::<Self>::read_from_suffix(source) {
26662677
Ok(candidate) => candidate,
26672678
Err(e) => {
26682679
return Err(TryReadError::Size(e.with_dst()));
@@ -2735,7 +2746,7 @@ fn swap<T, U>((t, u): (T, U)) -> (U, T) {
27352746
#[inline(always)]
27362747
unsafe fn try_read_from<S, T: TryFromBytes>(
27372748
source: S,
2738-
mut candidate: MaybeUninit<T>,
2749+
mut candidate: CoreMaybeUninit<T>,
27392750
) -> Result<T, TryReadError<S, T>> {
27402751
// We use `from_mut` despite not mutating via `c_ptr` so that we don't need
27412752
// to add a `T: Immutable` bound.

src/util/macros.rs

+3
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,8 @@ macro_rules! impl_known_layout {
530530

531531
type PointerMetadata = ();
532532

533+
type MaybeUninit = core::mem::MaybeUninit<Self>;
534+
533535
const LAYOUT: crate::DstLayout = crate::DstLayout::for_type::<$ty>();
534536

535537
// SAFETY: `.cast` preserves address and provenance.
@@ -572,6 +574,7 @@ macro_rules! unsafe_impl_known_layout {
572574
fn only_derive_is_allowed_to_implement_this_trait() {}
573575

574576
type PointerMetadata = <$repr as KnownLayout>::PointerMetadata;
577+
type MaybeUninit = <$repr as KnownLayout>::MaybeUninit;
575578

576579
const LAYOUT: DstLayout = <$repr as KnownLayout>::LAYOUT;
577580

src/wrappers.rs

+121-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// This file may not be copied, modified, or distributed except according to
77
// those terms.
88

9-
use core::hash::Hash;
9+
use core::{fmt, hash::Hash};
1010

1111
use super::*;
1212

@@ -464,6 +464,126 @@ impl<T: Unaligned + Display> Display for Unalign<T> {
464464
}
465465
}
466466

467+
/// A wrapper type to construct uninitialized instances of `T`.
468+
///
469+
/// `MaybeUninit` is identical to the [standard library
470+
/// `MaybeUninit`][core-maybe-uninit] type except that it supports unsized
471+
/// types.
472+
///
473+
/// # Layout
474+
///
475+
/// The same layout guarantees and caveats apply to `MaybeUninit<T>` as apply to
476+
/// the [standard library `MaybeUninit`][core-maybe-uninit] with one exception:
477+
/// for `T: !Sized`, there is no single value for `T`'s size. Instead, for such
478+
/// types, the following are guaranteed:
479+
/// - Every [valid size][valid-size] for `T` is a valid size for
480+
/// `MaybeUninit<T>` and vice versa
481+
/// - Given `t: *const T` and `m: *const MaybeUninit<T>` with identical fat
482+
/// pointer metadata, `t` and `m` address the same number of bytes (and
483+
/// likewise for `*mut`)
484+
///
485+
/// [core-maybe-uninit]: core::mem::MaybeUninit
486+
/// [valid-size]: crate::KnownLayout#what-is-a-valid-size
487+
#[repr(transparent)]
488+
#[cfg(zerocopy_unstable)]
489+
pub struct MaybeUninit<T: ?Sized + KnownLayout>(T::MaybeUninit);
490+
491+
#[cfg(zerocopy_unstable)]
492+
impl<T: ?Sized + KnownLayout> MaybeUninit<T> {
493+
/// Creates a `Box<MaybeUninit<T>>`.
494+
///
495+
/// This function is useful for allocating large, uninit values on the heap,
496+
/// without ever creating a temporary instance of `Self` on the stack.
497+
///
498+
/// # Errors
499+
///
500+
/// Returns an error on allocation failure. Allocation failure is guaranteed
501+
/// never to cause a panic or an abort.
502+
#[cfg(feature = "alloc")]
503+
pub fn new_boxed_uninit(meta: T::PointerMetadata) -> Result<Box<Self>, AllocError> {
504+
let align = Self::LAYOUT.align.get();
505+
let size = match meta.size_for_metadata(Self::LAYOUT) {
506+
Some(size) => size,
507+
None => return Err(AllocError),
508+
};
509+
// On stable Rust versions <= 1.64.0, `Layout::from_size_align` has a
510+
// bug in which sufficiently-large allocations (those which, when
511+
// rounded up to the alignment, overflow `isize`) are not rejected,
512+
// which can cause undefined behavior. See #64 for details.
513+
//
514+
// TODO(#67): Once our MSRV is > 1.64.0, remove this assertion.
515+
#[allow(clippy::as_conversions)]
516+
let max_alloc = (isize::MAX as usize).saturating_sub(align);
517+
if size > max_alloc {
518+
return Err(AllocError);
519+
}
520+
521+
// TODO(https://github.com/rust-lang/rust/issues/55724): Use
522+
// `Layout::repeat` once it's stabilized.
523+
let layout = Layout::from_size_align(size, align).or(Err(AllocError))?;
524+
525+
let ptr = if layout.size() != 0 {
526+
// TODO(#429): Add a "SAFETY" comment and remove this `allow`.
527+
#[allow(clippy::undocumented_unsafe_blocks)]
528+
let ptr = unsafe { alloc::alloc::alloc(layout) };
529+
match NonNull::new(ptr) {
530+
Some(ptr) => ptr,
531+
None => return Err(AllocError),
532+
}
533+
} else {
534+
let align = Self::LAYOUT.align.get();
535+
// We use `transmute` instead of an `as` cast since Miri (with
536+
// strict provenance enabled) notices and complains that an `as`
537+
// cast creates a pointer with no provenance. Miri isn't smart
538+
// enough to realize that we're only executing this branch when
539+
// we're constructing a zero-sized `Box`, which doesn't require
540+
// provenance.
541+
//
542+
// SAFETY: any initialized bit sequence is a bit-valid `*mut u8`.
543+
// All bits of a `usize` are initialized.
544+
#[allow(clippy::useless_transmute)]
545+
let dangling = unsafe { mem::transmute::<usize, *mut u8>(align) };
546+
// SAFETY: `dangling` is constructed from `Self::LAYOUT.align`,
547+
// which is a `NonZeroUsize`, which is guaranteed to be non-zero.
548+
//
549+
// `Box<[T]>` does not allocate when `T` is zero-sized or when `len`
550+
// is zero, but it does require a non-null dangling pointer for its
551+
// allocation.
552+
//
553+
// TODO(https://github.com/rust-lang/rust/issues/95228): Use
554+
// `std::ptr::without_provenance` once it's stable. That may
555+
// optimize better. As written, Rust may assume that this consumes
556+
// "exposed" provenance, and thus Rust may have to assume that this
557+
// may consume provenance from any pointer whose provenance has been
558+
// exposed.
559+
#[allow(fuzzy_provenance_casts)]
560+
unsafe {
561+
NonNull::new_unchecked(dangling)
562+
}
563+
};
564+
565+
let ptr = Self::raw_from_ptr_len(ptr, meta);
566+
567+
// TODO(#429): Add a "SAFETY" comment and remove this `allow`. Make sure
568+
// to include a justification that `ptr.as_ptr()` is validly-aligned in
569+
// the ZST case (in which we manually construct a dangling pointer).
570+
#[allow(clippy::undocumented_unsafe_blocks)]
571+
Ok(unsafe { Box::from_raw(ptr.as_ptr()) })
572+
}
573+
574+
#[cfg(feature = "alloc")]
575+
unsafe fn assume_boxed_init(self: Box<Self>) -> Box<T> {
576+
todo!()
577+
}
578+
}
579+
580+
#[cfg(zerocopy_unstable)]
581+
impl<T: ?Sized + KnownLayout> fmt::Debug for MaybeUninit<T> {
582+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
583+
f.pad(core::any::type_name::<Self>())
584+
}
585+
}
586+
467587
#[cfg(test)]
468588
mod tests {
469589
use core::panic::AssertUnwindSafe;

tools/cargo-zerocopy/src/main.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,9 @@ fn install_toolchain_or_exit(versions: &Versions, name: &str) -> Result<(), Erro
166166
fn get_rustflags(name: &str) -> &'static str {
167167
// See #1792 for context on zerocopy_derive_union_into_bytes.
168168
if name == "nightly" {
169-
"--cfg __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS --cfg zerocopy_derive_union_into_bytes "
169+
"--cfg __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS --cfg zerocopy_derive_union_into_bytes --cfg zerocopy_unstable "
170170
} else {
171-
"--cfg zerocopy_derive_union_into_bytes "
171+
"--cfg zerocopy_derive_union_into_bytes --cfg zerocopy_unstable "
172172
}
173173
}
174174

0 commit comments

Comments
 (0)