44use crate :: alloc:: { self , Layout , LayoutError } ;
55use core:: error:: Error ;
66use core:: fmt:: { self , Debug , Display , Formatter } ;
7+ #[ cfg( not( no_global_oom_handling) ) ]
8+ use core:: intrinsics:: const_allocate;
79use core:: marker:: PhantomData ;
810#[ cfg( not( no_global_oom_handling) ) ]
911use core:: marker:: Unsize ;
10- use core:: mem:: { self , SizedTypeProperties } ;
12+ use core:: mem;
13+ #[ cfg( not( no_global_oom_handling) ) ]
14+ use core:: mem:: SizedTypeProperties ;
1115use core:: ops:: { Deref , DerefMut } ;
1216use core:: ptr:: Pointee ;
1317use core:: ptr:: { self , NonNull } ;
@@ -91,6 +95,58 @@ impl<T> ThinBox<T> {
9195
9296#[ unstable( feature = "thin_box" , issue = "92791" ) ]
9397impl < Dyn : ?Sized > ThinBox < Dyn > {
98+ #[ cfg( not( no_global_oom_handling) ) ]
99+ fn new_unsize_zst < T > ( value : T ) -> Self
100+ where
101+ T : Unsize < Dyn > ,
102+ {
103+ assert ! ( mem:: size_of:: <T >( ) == 0 ) ;
104+
105+ const fn max ( a : usize , b : usize ) -> usize {
106+ if a > b { a } else { b }
107+ }
108+
109+ // Compute a pointer to the right metadata. This will point to the beginning
110+ // of the header, past the padding, so the assigned type makes sense.
111+ // It also ensures that the address at the end of the header is sufficiently
112+ // aligned for T.
113+ let alloc: & <Dyn as Pointee >:: Metadata = const {
114+ // FIXME: just call `WithHeader::alloc_layout` with size reset to 0.
115+ // Currently that's blocked on `Layout::extend` not being `const fn`.
116+
117+ let alloc_align =
118+ max ( mem:: align_of :: < T > ( ) , mem:: align_of :: < <Dyn as Pointee >:: Metadata > ( ) ) ;
119+
120+ let alloc_size =
121+ max ( mem:: align_of :: < T > ( ) , mem:: size_of :: < <Dyn as Pointee >:: Metadata > ( ) ) ;
122+
123+ unsafe {
124+ // SAFETY: align is power of two because it is the maximum of two alignments.
125+ let alloc: * mut u8 = const_allocate ( alloc_size, alloc_align) ;
126+
127+ let metadata_offset =
128+ alloc_size. checked_sub ( mem:: size_of :: < <Dyn as Pointee >:: Metadata > ( ) ) . unwrap ( ) ;
129+ // SAFETY: adding offset within the allocation.
130+ let metadata_ptr: * mut <Dyn as Pointee >:: Metadata =
131+ alloc. add ( metadata_offset) . cast ( ) ;
132+ // SAFETY: `*metadata_ptr` is within the allocation.
133+ metadata_ptr. write ( ptr:: metadata :: < Dyn > ( ptr:: dangling :: < T > ( ) as * const Dyn ) ) ;
134+
135+ // SAFETY: we have just written the metadata.
136+ & * ( metadata_ptr)
137+ }
138+ } ;
139+
140+ // SAFETY: `alloc` points to `<Dyn as Pointee>::Metadata`, so addition stays in-bounds.
141+ let value_ptr =
142+ unsafe { ( alloc as * const <Dyn as Pointee >:: Metadata ) . add ( 1 ) } . cast :: < T > ( ) . cast_mut ( ) ;
143+ debug_assert ! ( value_ptr. is_aligned( ) ) ;
144+ let ptr = WithOpaqueHeader ( NonNull :: new ( value_ptr. cast ( ) ) . unwrap ( ) ) ;
145+ // Forget the value to avoid double drop.
146+ mem:: forget ( value) ;
147+ ThinBox :: < Dyn > { ptr, _marker : PhantomData }
148+ }
149+
94150 /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
95151 /// the stack.
96152 ///
@@ -109,9 +165,13 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
109165 where
110166 T : Unsize < Dyn > ,
111167 {
112- let meta = ptr:: metadata ( & value as & Dyn ) ;
113- let ptr = WithOpaqueHeader :: new ( meta, value) ;
114- ThinBox { ptr, _marker : PhantomData }
168+ if mem:: size_of :: < T > ( ) == 0 {
169+ Self :: new_unsize_zst ( value)
170+ } else {
171+ let meta = ptr:: metadata ( & value as & Dyn ) ;
172+ let ptr = WithOpaqueHeader :: new ( meta, value) ;
173+ ThinBox { ptr, _marker : PhantomData }
174+ }
115175 }
116176}
117177
@@ -300,20 +360,19 @@ impl<H> WithHeader<H> {
300360
301361 impl < H > Drop for DropGuard < H > {
302362 fn drop ( & mut self ) {
363+ // All ZST are allocated statically.
364+ if self . value_layout . size ( ) == 0 {
365+ return ;
366+ }
367+
303368 unsafe {
304369 // SAFETY: Layout must have been computable if we're in drop
305370 let ( layout, value_offset) =
306371 WithHeader :: < H > :: alloc_layout ( self . value_layout ) . unwrap_unchecked ( ) ;
307372
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- }
373+ // Since we only allocate for non-ZSTs, the layout size cannot be zero.
374+ debug_assert ! ( layout. size( ) != 0 ) ;
375+ alloc:: dealloc ( self . ptr . as_ptr ( ) . sub ( value_offset) , layout) ;
317376 }
318377 }
319378 }
0 commit comments