@@ -6,8 +6,10 @@ use core::error::Error;
6
6
use core:: fmt:: { self , Debug , Display , Formatter } ;
7
7
use core:: marker:: PhantomData ;
8
8
#[ cfg( not( no_global_oom_handling) ) ]
9
- use core:: marker:: Unsize ;
10
- use core:: mem:: { self , SizedTypeProperties } ;
9
+ use core:: marker:: { Freeze , Unsize } ;
10
+ use core:: mem;
11
+ #[ cfg( not( no_global_oom_handling) ) ]
12
+ use core:: mem:: { MaybeUninit , SizedTypeProperties } ;
11
13
use core:: ops:: { Deref , DerefMut } ;
12
14
use core:: ptr:: Pointee ;
13
15
use core:: ptr:: { self , NonNull } ;
@@ -91,6 +93,93 @@ impl<T> ThinBox<T> {
91
93
92
94
#[ unstable( feature = "thin_box" , issue = "92791" ) ]
93
95
impl < Dyn : ?Sized > ThinBox < Dyn > {
96
+ #[ cfg( not( no_global_oom_handling) ) ]
97
+ fn new_unsize_zst < T > ( value : T ) -> Self
98
+ where
99
+ T : Unsize < Dyn > ,
100
+ {
101
+ assert ! ( mem:: size_of:: <T >( ) == 0 ) ;
102
+
103
+ #[ repr( C ) ]
104
+ struct ReprC < A , B > {
105
+ a : A ,
106
+ b : B ,
107
+ }
108
+
109
+ struct EmptyArray < T > {
110
+ _array : [ T ; 0 ] ,
111
+ }
112
+
113
+ // SAFETY: this is a zero-sized type, there can't be any mutable memory here.
114
+ // It's a private type so this can never leak to the user either.
115
+ // If the user tried to write through the shared reference to this type,
116
+ // they would be causing library UB as that's a write outside the memory
117
+ // inhabited by their type (which is zero-sized). Therefore making this
118
+ // language UB is justified.
119
+ unsafe impl < T > Freeze for EmptyArray < T > { }
120
+
121
+ // Allocate header with padding in the beginning:
122
+ // ```
123
+ // [ padding | header ]
124
+ // ```
125
+ // where the struct is aligned to both header and value.
126
+ #[ repr( C ) ]
127
+ struct AlignedHeader < H : Copy , T > {
128
+ header_data : MaybeUninit < ReprC < H , EmptyArray < T > > > ,
129
+ }
130
+
131
+ impl < H : Copy + Freeze , T > AlignedHeader < H , T > {
132
+ const fn make ( header : H ) -> Self {
133
+ let mut header_data = MaybeUninit :: < ReprC < H , EmptyArray < T > > > :: zeroed ( ) ;
134
+ unsafe {
135
+ header_data. as_mut_ptr ( ) . add ( 1 ) . cast :: < H > ( ) . sub ( 1 ) . write ( header) ;
136
+ }
137
+ AlignedHeader { header_data }
138
+ }
139
+ }
140
+
141
+ #[ repr( C ) ]
142
+ struct DynZstAlloc < T , Dyn : ?Sized > {
143
+ header : AlignedHeader < <Dyn as Pointee >:: Metadata , T > ,
144
+ value : EmptyArray < T > ,
145
+ }
146
+
147
+ impl < T , Dyn : ?Sized > DynZstAlloc < T , Dyn >
148
+ where
149
+ T : Unsize < Dyn > ,
150
+ {
151
+ const ALLOCATION : DynZstAlloc < T , Dyn > = DynZstAlloc {
152
+ header : AlignedHeader :: make ( ptr:: metadata :: < Dyn > (
153
+ ptr:: dangling :: < T > ( ) as * const Dyn
154
+ ) ) ,
155
+ value : EmptyArray { _array : [ ] } ,
156
+ } ;
157
+
158
+ fn static_alloc < ' a > ( ) -> & ' a DynZstAlloc < T , Dyn > {
159
+ & Self :: ALLOCATION
160
+ }
161
+ }
162
+
163
+ let alloc: & DynZstAlloc < T , Dyn > = DynZstAlloc :: < T , Dyn > :: static_alloc ( ) ;
164
+
165
+ let value_offset = mem:: offset_of!( DynZstAlloc <T , Dyn >, value) ;
166
+ assert_eq ! ( value_offset, mem:: size_of:: <AlignedHeader <<Dyn as Pointee >:: Metadata , T >>( ) ) ;
167
+
168
+ let ptr = WithOpaqueHeader (
169
+ NonNull :: new (
170
+ // SAFETY: there's no overflow here because we add field offset.
171
+ unsafe {
172
+ ( alloc as * const DynZstAlloc < T , Dyn > as * mut u8 ) . add ( value_offset) as * mut _
173
+ } ,
174
+ )
175
+ . unwrap ( ) ,
176
+ ) ;
177
+ let thin_box = ThinBox :: < Dyn > { ptr, _marker : PhantomData } ;
178
+ // Forget the value to avoid double drop.
179
+ mem:: forget ( value) ;
180
+ thin_box
181
+ }
182
+
94
183
/// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
95
184
/// the stack.
96
185
///
@@ -109,9 +198,13 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
109
198
where
110
199
T : Unsize < Dyn > ,
111
200
{
112
- let meta = ptr:: metadata ( & value as & Dyn ) ;
113
- let ptr = WithOpaqueHeader :: new ( meta, value) ;
114
- ThinBox { ptr, _marker : PhantomData }
201
+ if mem:: size_of :: < T > ( ) == 0 {
202
+ Self :: new_unsize_zst ( value)
203
+ } else {
204
+ let meta = ptr:: metadata ( & value as & Dyn ) ;
205
+ let ptr = WithOpaqueHeader :: new ( meta, value) ;
206
+ ThinBox { ptr, _marker : PhantomData }
207
+ }
115
208
}
116
209
}
117
210
@@ -300,20 +393,19 @@ impl<H> WithHeader<H> {
300
393
301
394
impl < H > Drop for DropGuard < H > {
302
395
fn drop ( & mut self ) {
396
+ // All ZST are allocated statically.
397
+ if self . value_layout . size ( ) == 0 {
398
+ return ;
399
+ }
400
+
303
401
unsafe {
304
402
// SAFETY: Layout must have been computable if we're in drop
305
403
let ( layout, value_offset) =
306
404
WithHeader :: < H > :: alloc_layout ( self . value_layout ) . unwrap_unchecked ( ) ;
307
405
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
- }
406
+ // Since we only allocate for non-ZSTs, the layout size cannot be zero.
407
+ debug_assert ! ( layout. size( ) != 0 ) ;
408
+ alloc:: dealloc ( self . ptr . as_ptr ( ) . sub ( value_offset) , layout) ;
317
409
}
318
410
}
319
411
}
0 commit comments