diff --git a/library/alloc/src/boxed/thin.rs b/library/alloc/src/boxed/thin.rs index 0421a12b3a952..19621fa980ddd 100644 --- a/library/alloc/src/boxed/thin.rs +++ b/library/alloc/src/boxed/thin.rs @@ -6,8 +6,10 @@ use core::error::Error; use core::fmt::{self, Debug, Display, Formatter}; use core::marker::PhantomData; #[cfg(not(no_global_oom_handling))] -use core::marker::Unsize; -use core::mem::{self, SizedTypeProperties}; +use core::marker::{Freeze, Unsize}; +use core::mem; +#[cfg(not(no_global_oom_handling))] +use core::mem::{MaybeUninit, SizedTypeProperties}; use core::ops::{Deref, DerefMut}; use core::ptr::Pointee; use core::ptr::{self, NonNull}; @@ -91,6 +93,93 @@ impl<T> ThinBox<T> { #[unstable(feature = "thin_box", issue = "92791")] impl<Dyn: ?Sized> ThinBox<Dyn> { + #[cfg(not(no_global_oom_handling))] + fn new_unsize_zst<T>(value: T) -> Self + where + T: Unsize<Dyn>, + { + assert!(mem::size_of::<T>() == 0); + + #[repr(C)] + struct ReprC<A, B> { + a: A, + b: B, + } + + struct EmptyArray<T> { + _array: [T; 0], + } + + // SAFETY: this is a zero-sized type, there can't be any mutable memory here. + // It's a private type so this can never leak to the user either. + // If the user tried to write through the shared reference to this type, + // they would be causing library UB as that's a write outside the memory + // inhabited by their type (which is zero-sized). Therefore making this + // language UB is justified. + unsafe impl<T> Freeze for EmptyArray<T> {} + + // Allocate header with padding in the beginning: + // ``` + // [ padding | header ] + // ``` + // where the struct is aligned to both header and value. + #[repr(C)] + struct AlignedHeader<H: Copy, T> { + header_data: MaybeUninit<ReprC<H, EmptyArray<T>>>, + } + + impl<H: Copy + Freeze, T> AlignedHeader<H, T> { + const fn make(header: H) -> Self { + let mut header_data = MaybeUninit::<ReprC<H, EmptyArray<T>>>::zeroed(); + unsafe { + header_data.as_mut_ptr().add(1).cast::<H>().sub(1).write(header); + } + AlignedHeader { header_data } + } + } + + #[repr(C)] + struct DynZstAlloc<T, Dyn: ?Sized> { + header: AlignedHeader<<Dyn as Pointee>::Metadata, T>, + value: EmptyArray<T>, + } + + impl<T, Dyn: ?Sized> DynZstAlloc<T, Dyn> + where + T: Unsize<Dyn>, + { + const ALLOCATION: DynZstAlloc<T, Dyn> = DynZstAlloc { + header: AlignedHeader::make(ptr::metadata::<Dyn>( + ptr::dangling::<T>() as *const Dyn + )), + value: EmptyArray { _array: [] }, + }; + + fn static_alloc<'a>() -> &'a DynZstAlloc<T, Dyn> { + &Self::ALLOCATION + } + } + + let alloc: &DynZstAlloc<T, Dyn> = DynZstAlloc::<T, Dyn>::static_alloc(); + + let value_offset = mem::offset_of!(DynZstAlloc<T, Dyn>, value); + assert_eq!(value_offset, mem::size_of::<AlignedHeader<<Dyn as Pointee>::Metadata, T>>()); + + let ptr = WithOpaqueHeader( + NonNull::new( + // SAFETY: there's no overflow here because we add field offset. + unsafe { + (alloc as *const DynZstAlloc<T, Dyn> as *mut u8).add(value_offset) as *mut _ + }, + ) + .unwrap(), + ); + let thin_box = ThinBox::<Dyn> { ptr, _marker: PhantomData }; + // Forget the value to avoid double drop. + mem::forget(value); + thin_box + } + /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on /// the stack. /// @@ -109,9 +198,13 @@ impl<Dyn: ?Sized> ThinBox<Dyn> { where T: Unsize<Dyn>, { - let meta = ptr::metadata(&value as &Dyn); - let ptr = WithOpaqueHeader::new(meta, value); - ThinBox { ptr, _marker: PhantomData } + if mem::size_of::<T>() == 0 { + Self::new_unsize_zst(value) + } else { + let meta = ptr::metadata(&value as &Dyn); + let ptr = WithOpaqueHeader::new(meta, value); + ThinBox { ptr, _marker: PhantomData } + } } } @@ -300,20 +393,19 @@ impl<H> WithHeader<H> { impl<H> Drop for DropGuard<H> { fn drop(&mut self) { + // All ZST are allocated statically. + if self.value_layout.size() == 0 { + return; + } + unsafe { // SAFETY: Layout must have been computable if we're in drop let (layout, value_offset) = WithHeader::<H>::alloc_layout(self.value_layout).unwrap_unchecked(); - // Note: Don't deallocate if the layout size is zero, because the pointer - // didn't come from the allocator. - if layout.size() != 0 { - alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout); - } else { - debug_assert!( - value_offset == 0 && H::IS_ZST && self.value_layout.size() == 0 - ); - } + // Since we only allocate for non-ZSTs, the layout size cannot be zero. + debug_assert!(layout.size() != 0); + alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout); } } } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 26d238154a3ba..9ead984cc90b5 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -130,6 +130,8 @@ #![feature(extend_one)] #![feature(fmt_internals)] #![feature(fn_traits)] +#![feature(freeze)] +#![feature(freeze_impls)] #![feature(generic_nonzero)] #![feature(hasher_prefixfree_extras)] #![feature(hint_assert_unchecked)] diff --git a/library/core/src/ptr/metadata.rs b/library/core/src/ptr/metadata.rs index fe19f66a31ac4..80092c08ce892 100644 --- a/library/core/src/ptr/metadata.rs +++ b/library/core/src/ptr/metadata.rs @@ -2,6 +2,7 @@ use crate::fmt; use crate::hash::{Hash, Hasher}; +use crate::marker::Freeze; /// Provides the pointer metadata type of any pointed-to type. /// @@ -57,7 +58,7 @@ pub trait Pointee { // NOTE: Keep trait bounds in `static_assert_expected_bounds_for_metadata` // in `library/core/src/ptr/metadata.rs` // in sync with those here: - type Metadata: Copy + Send + Sync + Ord + Hash + Unpin; + type Metadata: Copy + Send + Sync + Ord + Hash + Unpin + Freeze; } /// Pointers to types implementing this trait alias are “thin”. diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 421062f5873cd..b52e1031a0b82 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -36,6 +36,7 @@ #![feature(duration_constructors)] #![feature(exact_size_is_empty)] #![feature(extern_types)] +#![feature(freeze)] #![feature(flt2dec)] #![feature(fmt_internals)] #![feature(float_minimum_maximum)] diff --git a/library/core/tests/ptr.rs b/library/core/tests/ptr.rs index 5c518e2d59340..b13921b434b11 100644 --- a/library/core/tests/ptr.rs +++ b/library/core/tests/ptr.rs @@ -1,4 +1,5 @@ use core::cell::RefCell; +use core::marker::Freeze; use core::mem::{self, MaybeUninit}; use core::num::NonZero; use core::ptr; @@ -841,7 +842,7 @@ fn ptr_metadata_bounds() { fn static_assert_expected_bounds_for_metadata<Meta>() where // Keep this in sync with the associated type in `library/core/src/ptr/metadata.rs` - Meta: Copy + Send + Sync + Ord + std::hash::Hash + Unpin, + Meta: Copy + Send + Sync + Ord + std::hash::Hash + Unpin + Freeze, { } }