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,62 @@ 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+ // We are using `[T; 0]` here instead of `T` because
110+ // compiler instantiates this code even for non-ZST,
111+ // and reports a pointer going beyond the allocation.
112+ let alloc: & [ T ; 0 ] = const {
113+ // ZST T pointer must be aligned to both the value and the metadata.
114+ // Metadata must be at the address `&T - size_of::<Metadata>`.
115+ // This is how we need to allocate the memory:
116+ // [ padding | metadata | ZST T value ]
117+
118+ let alloc_align =
119+ max ( mem:: align_of :: < T > ( ) , mem:: align_of :: < <Dyn as Pointee >:: Metadata > ( ) ) ;
120+
121+ let alloc_size =
122+ max ( mem:: align_of :: < T > ( ) , mem:: size_of :: < <Dyn as Pointee >:: Metadata > ( ) ) ;
123+
124+ unsafe {
125+ // SAFETY: align is power of two because it is the maximum of two alignments.
126+ let alloc: * mut u8 = const_allocate ( alloc_size, alloc_align) ;
127+
128+ // Zerofill to be safe.
129+ // SAFETY: `alloc_size` is the size of the allocation.
130+ ptr:: write_bytes ( alloc, 0 , alloc_size) ;
131+
132+ // SAFETY: adding offset within the allocation.
133+ let metadata_ptr: * mut u8 = alloc. add (
134+ alloc_size. checked_sub ( mem:: size_of :: < <Dyn as Pointee >:: Metadata > ( ) ) . unwrap ( ) ,
135+ ) ;
136+ // SAFETY: `*metadata_ptr` is within the allocation.
137+ ptr:: write (
138+ metadata_ptr as * mut <Dyn as Pointee >:: Metadata ,
139+ ptr:: metadata :: < Dyn > ( ptr:: dangling :: < T > ( ) as * const Dyn ) ,
140+ ) ;
141+
142+ // SAFETY: `alloc_size` is the size of the allocation
143+ // and the pointer we create is zero-sized.
144+ & * ( alloc. add ( alloc_size) as * const [ T ; 0 ] )
145+ }
146+ } ;
147+
148+ let ptr = WithOpaqueHeader ( NonNull :: new ( alloc as * const _ as * mut u8 ) . unwrap ( ) ) ;
149+ // Forget the value to avoid double drop.
150+ mem:: forget ( value) ;
151+ ThinBox :: < Dyn > { ptr, _marker : PhantomData }
152+ }
153+
94154 /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
95155 /// the stack.
96156 ///
@@ -109,9 +169,13 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
109169 where
110170 T : Unsize < Dyn > ,
111171 {
112- let meta = ptr:: metadata ( & value as & Dyn ) ;
113- let ptr = WithOpaqueHeader :: new ( meta, value) ;
114- ThinBox { ptr, _marker : PhantomData }
172+ if mem:: size_of :: < T > ( ) == 0 {
173+ Self :: new_unsize_zst ( value)
174+ } else {
175+ let meta = ptr:: metadata ( & value as & Dyn ) ;
176+ let ptr = WithOpaqueHeader :: new ( meta, value) ;
177+ ThinBox { ptr, _marker : PhantomData }
178+ }
115179 }
116180}
117181
@@ -300,20 +364,19 @@ impl<H> WithHeader<H> {
300364
301365 impl < H > Drop for DropGuard < H > {
302366 fn drop ( & mut self ) {
367+ // All ZST are allocated statically.
368+ if self . value_layout . size ( ) == 0 {
369+ return ;
370+ }
371+
303372 unsafe {
304373 // SAFETY: Layout must have been computable if we're in drop
305374 let ( layout, value_offset) =
306375 WithHeader :: < H > :: alloc_layout ( self . value_layout ) . unwrap_unchecked ( ) ;
307376
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- }
377+ // Since we only allocate for non-ZSTs, the layout size cannot be zero.
378+ debug_assert ! ( layout. size( ) != 0 ) ;
379+ alloc:: dealloc ( self . ptr . as_ptr ( ) . sub ( value_offset) , layout) ;
317380 }
318381 }
319382 }
0 commit comments