44use crate :: alloc:: { self , Layout , LayoutError } ;
55use core:: error:: Error ;
66use core:: fmt:: { self , Debug , Display , Formatter } ;
7+ use core:: intrinsics:: const_allocate;
78use core:: marker:: PhantomData ;
89#[ cfg( not( no_global_oom_handling) ) ]
910use core:: marker:: Unsize ;
10- use core:: mem:: { self , SizedTypeProperties } ;
11+ use core:: mem;
12+ #[ cfg( not( no_global_oom_handling) ) ]
13+ use core:: mem:: { MaybeUninit , SizedTypeProperties } ;
1114use core:: ops:: { Deref , DerefMut } ;
1215use core:: ptr:: Pointee ;
1316use core:: ptr:: { self , NonNull } ;
@@ -91,6 +94,79 @@ impl<T> ThinBox<T> {
9194
9295#[ unstable( feature = "thin_box" , issue = "92791" ) ]
9396impl < Dyn : ?Sized > ThinBox < Dyn > {
97+ #[ cfg( not( no_global_oom_handling) ) ]
98+ fn new_unsize_zst < T > ( value : T ) -> Self
99+ where
100+ T : Unsize < Dyn > ,
101+ {
102+ assert ! ( mem:: size_of:: <T >( ) == 0 ) ;
103+
104+ // This is a helper struct to allocate the header with proper alignment:
105+ // Header **end** must be aligned to the value.
106+ // We insert the padding not after the `header` field, but before the `header` field.
107+ // Allocation layout is be:
108+ // ```
109+ // [ padding | metadata | ZST value ]
110+ // ```
111+ #[ repr( C ) ]
112+ struct DynZstAlloc < T , Dyn : ?Sized > {
113+ header : MaybeUninit < <Dyn as Pointee >:: Metadata > ,
114+ value : MaybeUninit < T > ,
115+ }
116+
117+ impl < ' a , T : ' a , Dyn : ?Sized + ' a > DynZstAlloc < T , Dyn >
118+ where
119+ T : Unsize < Dyn > ,
120+ {
121+ const fn value_offset ( ) -> usize {
122+ mem:: offset_of!( DynZstAlloc <T , Dyn >, value)
123+ }
124+
125+ const ALLOC : & ' a DynZstAlloc < T , Dyn > = {
126+ // SAFETY: this is compile time evaluation.
127+ unsafe {
128+ let size = mem:: size_of :: < DynZstAlloc < T , Dyn > > ( ) ;
129+ let alloc: * mut u8 =
130+ const_allocate ( size, mem:: align_of :: < DynZstAlloc < T , Dyn > > ( ) ) ;
131+
132+ // Zerofill to be safe.
133+ ptr:: write_bytes ( alloc, 0 , size) ;
134+
135+ // Here we pick proper placement of the metadata,
136+ // which is not where the metadata field is declared.
137+ let metadata_ptr = alloc. add (
138+ Self :: value_offset ( )
139+ . checked_sub ( mem:: size_of :: < <Dyn as Pointee >:: Metadata > ( ) )
140+ . unwrap ( ) ,
141+ ) ;
142+ ptr:: write (
143+ metadata_ptr as * mut <Dyn as Pointee >:: Metadata ,
144+ ptr:: metadata :: < Dyn > ( ptr:: dangling :: < T > ( ) as * const Dyn ) ,
145+ ) ;
146+
147+ & * ( alloc as * const DynZstAlloc < T , Dyn > )
148+ }
149+ } ;
150+ }
151+
152+ let alloc: & DynZstAlloc < T , Dyn > = DynZstAlloc :: < T , Dyn > :: ALLOC ;
153+
154+ let value_offset = DynZstAlloc :: < T , Dyn > :: value_offset ( ) ;
155+
156+ let ptr = WithOpaqueHeader (
157+ NonNull :: new (
158+ // SAFETY: there's no overflow here because we add field offset.
159+ unsafe {
160+ ( alloc as * const DynZstAlloc < T , Dyn > as * mut u8 ) . add ( value_offset) as * mut _
161+ } ,
162+ )
163+ . unwrap ( ) ,
164+ ) ;
165+ // Forget the value to avoid double drop.
166+ mem:: forget ( value) ;
167+ ThinBox :: < Dyn > { ptr, _marker : PhantomData }
168+ }
169+
94170 /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
95171 /// the stack.
96172 ///
@@ -109,9 +185,13 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
109185 where
110186 T : Unsize < Dyn > ,
111187 {
112- let meta = ptr:: metadata ( & value as & Dyn ) ;
113- let ptr = WithOpaqueHeader :: new ( meta, value) ;
114- ThinBox { ptr, _marker : PhantomData }
188+ if mem:: size_of :: < T > ( ) == 0 {
189+ Self :: new_unsize_zst ( value)
190+ } else {
191+ let meta = ptr:: metadata ( & value as & Dyn ) ;
192+ let ptr = WithOpaqueHeader :: new ( meta, value) ;
193+ ThinBox { ptr, _marker : PhantomData }
194+ }
115195 }
116196}
117197
@@ -300,20 +380,19 @@ impl<H> WithHeader<H> {
300380
301381 impl < H > Drop for DropGuard < H > {
302382 fn drop ( & mut self ) {
383+ // All ZST are allocated statically.
384+ if self . value_layout . size ( ) == 0 {
385+ return ;
386+ }
387+
303388 unsafe {
304389 // SAFETY: Layout must have been computable if we're in drop
305390 let ( layout, value_offset) =
306391 WithHeader :: < H > :: alloc_layout ( self . value_layout ) . unwrap_unchecked ( ) ;
307392
308- // Note: Don't deallocate if the layout size is zero, because the pointer
309- // didn't come from the allocator.
310- if layout. size ( ) != 0 {
311- alloc:: dealloc ( self . ptr . as_ptr ( ) . sub ( value_offset) , layout) ;
312- } else {
313- debug_assert ! (
314- value_offset == 0 && H :: IS_ZST && self . value_layout. size( ) == 0
315- ) ;
316- }
393+ // Since we only allocate for non-ZSTs, the layout size cannot be zero.
394+ debug_assert ! ( layout. size( ) != 0 ) ;
395+ alloc:: dealloc ( self . ptr . as_ptr ( ) . sub ( value_offset) , layout) ;
317396 }
318397 }
319398 }
0 commit comments