4
4
use crate :: alloc:: { self , Layout , LayoutError } ;
5
5
use core:: error:: Error ;
6
6
use core:: fmt:: { self , Debug , Display , Formatter } ;
7
+ #[ cfg( not( no_global_oom_handling) ) ]
8
+ use core:: intrinsics:: const_allocate;
7
9
use core:: marker:: PhantomData ;
8
10
#[ cfg( not( no_global_oom_handling) ) ]
9
11
use 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 ;
11
15
use core:: ops:: { Deref , DerefMut } ;
12
16
use core:: ptr:: Pointee ;
13
17
use core:: ptr:: { self , NonNull } ;
@@ -91,6 +95,74 @@ impl<T> ThinBox<T> {
91
95
92
96
#[ unstable( feature = "thin_box" , issue = "92791" ) ]
93
97
impl < 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
+ // Utility to statically allocate metadata followed by ZST value.
106
+ struct DynZstAlloc < T , Dyn : ?Sized > ( PhantomData < ( T , Dyn ) > ) ;
107
+
108
+ impl < ' a , T : ' a , Dyn : ?Sized + ' a > DynZstAlloc < T , Dyn >
109
+ where
110
+ T : Unsize < Dyn > ,
111
+ {
112
+ // We are returning `[T; 0]` here instead of `T` because
113
+ // compiler instantiates this code even for non-ZST,
114
+ // and reports a pointer going beyond the allocation.
115
+ const ALLOC : & ' a [ T ; 0 ] = {
116
+ const fn max ( a : usize , b : usize ) -> usize {
117
+ if a > b { a } else { b }
118
+ }
119
+
120
+ // ZST T pointer must be aligned to both the value and the metadata.
121
+ // Metadata must be at the address `&T - size_of::<Metadata>`.
122
+ // This is how we need to allocate the memory:
123
+ // [ padding | metadata | ZST T value ]
124
+
125
+ let alloc_align =
126
+ max ( mem:: align_of :: < T > ( ) , mem:: align_of :: < <Dyn as Pointee >:: Metadata > ( ) ) ;
127
+
128
+ let alloc_size =
129
+ max ( mem:: align_of :: < T > ( ) , mem:: size_of :: < <Dyn as Pointee >:: Metadata > ( ) ) ;
130
+
131
+ unsafe {
132
+ // SAFETY: align is power of two because it is the maximum of two alignments.
133
+ let alloc: * mut u8 = const_allocate ( alloc_size, alloc_align) ;
134
+
135
+ // Zerofill to be safe.
136
+ // SAFETY: `alloc_size` is the size of the allocation.
137
+ ptr:: write_bytes ( alloc, 0 , alloc_size) ;
138
+
139
+ // SAFETY: adding offset within the allocation.
140
+ let metadata_ptr = alloc. add (
141
+ alloc_size
142
+ . checked_sub ( mem:: size_of :: < <Dyn as Pointee >:: Metadata > ( ) )
143
+ . unwrap ( ) ,
144
+ ) ;
145
+ // SAFETY: `*metadata_ptr` is within the allocation.
146
+ ptr:: write (
147
+ metadata_ptr as * mut <Dyn as Pointee >:: Metadata ,
148
+ ptr:: metadata :: < Dyn > ( ptr:: dangling :: < T > ( ) as * const Dyn ) ,
149
+ ) ;
150
+
151
+ // SAFETY: `alloc_size` is the size of the allocation
152
+ // and the pointer we create is zero-sized.
153
+ & * ( alloc. add ( alloc_size) as * const [ T ; 0 ] )
154
+ }
155
+ } ;
156
+ }
157
+
158
+ let ptr = WithOpaqueHeader (
159
+ NonNull :: new ( DynZstAlloc :: < T , Dyn > :: ALLOC as * const _ as * mut u8 ) . unwrap ( ) ,
160
+ ) ;
161
+ // Forget the value to avoid double drop.
162
+ mem:: forget ( value) ;
163
+ ThinBox :: < Dyn > { ptr, _marker : PhantomData }
164
+ }
165
+
94
166
/// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
95
167
/// the stack.
96
168
///
@@ -109,9 +181,13 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
109
181
where
110
182
T : Unsize < Dyn > ,
111
183
{
112
- let meta = ptr:: metadata ( & value as & Dyn ) ;
113
- let ptr = WithOpaqueHeader :: new ( meta, value) ;
114
- ThinBox { ptr, _marker : PhantomData }
184
+ if mem:: size_of :: < T > ( ) == 0 {
185
+ Self :: new_unsize_zst ( value)
186
+ } else {
187
+ let meta = ptr:: metadata ( & value as & Dyn ) ;
188
+ let ptr = WithOpaqueHeader :: new ( meta, value) ;
189
+ ThinBox { ptr, _marker : PhantomData }
190
+ }
115
191
}
116
192
}
117
193
@@ -300,20 +376,19 @@ impl<H> WithHeader<H> {
300
376
301
377
impl < H > Drop for DropGuard < H > {
302
378
fn drop ( & mut self ) {
379
+ // All ZST are allocated statically.
380
+ if self . value_layout . size ( ) == 0 {
381
+ return ;
382
+ }
383
+
303
384
unsafe {
304
385
// SAFETY: Layout must have been computable if we're in drop
305
386
let ( layout, value_offset) =
306
387
WithHeader :: < H > :: alloc_layout ( self . value_layout ) . unwrap_unchecked ( ) ;
307
388
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
- }
389
+ // Since we only allocate for non-ZSTs, the layout size cannot be zero.
390
+ debug_assert ! ( layout. size( ) != 0 ) ;
391
+ alloc:: dealloc ( self . ptr . as_ptr ( ) . sub ( value_offset) , layout) ;
317
392
}
318
393
}
319
394
}
0 commit comments