|
6 | 6 | // This file may not be copied, modified, or distributed except according to
|
7 | 7 | // those terms.
|
8 | 8 |
|
9 |
| -use core::hash::Hash; |
| 9 | +use core::{fmt, hash::Hash}; |
10 | 10 |
|
11 | 11 | use super::*;
|
12 | 12 |
|
@@ -464,6 +464,126 @@ impl<T: Unaligned + Display> Display for Unalign<T> {
|
464 | 464 | }
|
465 | 465 | }
|
466 | 466 |
|
| 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 | + |
467 | 587 | #[cfg(test)]
|
468 | 588 | mod tests {
|
469 | 589 | use core::panic::AssertUnwindSafe;
|
|
0 commit comments