4
4
use crate :: alloc:: { self , Layout , LayoutError } ;
5
5
use core:: error:: Error ;
6
6
use core:: fmt:: { self , Debug , Display , Formatter } ;
7
- use core:: marker:: PhantomData ;
8
7
#[ cfg( not( no_global_oom_handling) ) ]
9
8
use core:: marker:: Unsize ;
10
- use core:: mem:: { self , SizedTypeProperties } ;
9
+ use core:: marker:: { Freeze , PhantomData } ;
10
+ use core:: mem:: { self , MaybeUninit , SizedTypeProperties } ;
11
11
use core:: ops:: { Deref , DerefMut } ;
12
12
use core:: ptr:: Pointee ;
13
13
use core:: ptr:: { self , NonNull } ;
@@ -91,6 +91,83 @@ impl<T> ThinBox<T> {
91
91
92
92
#[ unstable( feature = "thin_box" , issue = "92791" ) ]
93
93
impl < Dyn : ?Sized > ThinBox < Dyn > {
94
+ #[ cfg( not( no_global_oom_handling) ) ]
95
+ fn new_unsize_zst < T > ( value : T ) -> Self
96
+ where
97
+ T : Unsize < Dyn > ,
98
+ {
99
+ #[ repr( C ) ]
100
+ struct ReprC < A , B > {
101
+ a : A ,
102
+ b : B ,
103
+ }
104
+
105
+ // Allocate header like this:
106
+ // ```
107
+ // [ ... | header ]
108
+ // ```
109
+ // where the struct is aligned to both header and value.
110
+ #[ repr( C ) ]
111
+ struct AlignedHeader < H : Copy , T > {
112
+ header_data : MaybeUninit < ReprC < H , [ T ; 0 ] > > ,
113
+ }
114
+
115
+ impl < H : Copy , T > AlignedHeader < H , T > {
116
+ const fn make ( header : H ) -> Self {
117
+ let mut data = MaybeUninit :: < ReprC < H , [ T ; 0 ] > > :: zeroed ( ) ;
118
+ unsafe {
119
+ data. as_mut_ptr ( ) . add ( 1 ) . cast :: < H > ( ) . sub ( 1 ) . write ( header) ;
120
+ }
121
+ AlignedHeader { header_data : data }
122
+ }
123
+ }
124
+
125
+ #[ repr( C ) ]
126
+ struct DynZstAlloc < T , Dyn : ?Sized > {
127
+ header : AlignedHeader < <Dyn as Pointee >:: Metadata , T > ,
128
+ value : MaybeUninit < T > ,
129
+ }
130
+
131
+ // We need `Freeze` so we could call `&DynZstAlloc::LAYOUT`.
132
+ // SAFETY: data is immutable.
133
+ unsafe impl < T , Dyn : ?Sized > Freeze for DynZstAlloc < T , Dyn > { }
134
+
135
+ impl < T , Dyn : ?Sized > DynZstAlloc < T , Dyn >
136
+ where
137
+ T : Unsize < Dyn > ,
138
+ {
139
+ const LAYOUT : DynZstAlloc < T , Dyn > = DynZstAlloc {
140
+ header : AlignedHeader :: make ( ptr:: metadata :: < Dyn > (
141
+ ptr:: dangling :: < T > ( ) as * const Dyn
142
+ ) ) ,
143
+ value : MaybeUninit :: uninit ( ) ,
144
+ } ;
145
+
146
+ fn static_alloc < ' a > ( ) -> & ' a DynZstAlloc < T , Dyn > {
147
+ & Self :: LAYOUT
148
+ }
149
+ }
150
+
151
+ let alloc: & DynZstAlloc < T , Dyn > = DynZstAlloc :: < T , Dyn > :: static_alloc ( ) ;
152
+
153
+ let value_offset = mem:: offset_of!( DynZstAlloc <T , Dyn >, value) ;
154
+ assert_eq ! ( value_offset, mem:: size_of:: <AlignedHeader <<Dyn as Pointee >:: Metadata , T >>( ) ) ;
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
+ let thin_box = ThinBox :: < Dyn > { ptr, _marker : PhantomData } ;
166
+ // Forget the value to avoid double drop.
167
+ mem:: forget ( value) ;
168
+ thin_box
169
+ }
170
+
94
171
/// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
95
172
/// the stack.
96
173
///
@@ -109,9 +186,13 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
109
186
where
110
187
T : Unsize < Dyn > ,
111
188
{
112
- let meta = ptr:: metadata ( & value as & Dyn ) ;
113
- let ptr = WithOpaqueHeader :: new ( meta, value) ;
114
- ThinBox { ptr, _marker : PhantomData }
189
+ if mem:: size_of :: < T > ( ) == 0 && mem:: size_of :: < <Dyn as Pointee >:: Metadata > ( ) != 0 {
190
+ Self :: new_unsize_zst ( value)
191
+ } else {
192
+ let meta = ptr:: metadata ( & value as & Dyn ) ;
193
+ let ptr = WithOpaqueHeader :: new ( meta, value) ;
194
+ ThinBox { ptr, _marker : PhantomData }
195
+ }
115
196
}
116
197
}
117
198
@@ -155,7 +236,7 @@ impl<T: ?Sized> DerefMut for ThinBox<T> {
155
236
impl < T : ?Sized > Drop for ThinBox < T > {
156
237
fn drop ( & mut self ) {
157
238
unsafe {
158
- let value = self . deref_mut ( ) ;
239
+ let value: & mut T = self . deref_mut ( ) ;
159
240
let value = value as * mut T ;
160
241
self . with_header ( ) . drop :: < T > ( value) ;
161
242
}
@@ -300,20 +381,20 @@ impl<H> WithHeader<H> {
300
381
301
382
impl < H > Drop for DropGuard < H > {
302
383
fn drop ( & mut self ) {
384
+ // All ZST are allocated statically.
385
+ if self . value_layout . size ( ) == 0 {
386
+ return ;
387
+ }
388
+
303
389
unsafe {
304
390
// SAFETY: Layout must have been computable if we're in drop
305
391
let ( layout, value_offset) =
306
392
WithHeader :: < H > :: alloc_layout ( self . value_layout ) . unwrap_unchecked ( ) ;
307
393
308
394
// Note: Don't deallocate if the layout size is zero, because the pointer
309
395
// 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
- }
396
+ debug_assert ! ( layout. size( ) != 0 ) ;
397
+ alloc:: dealloc ( self . ptr . as_ptr ( ) . sub ( value_offset) , layout) ;
317
398
}
318
399
}
319
400
}
0 commit comments