@@ -6,8 +6,10 @@ use core::error::Error;
66use core:: fmt:: { self , Debug , Display , Formatter } ;
77use core:: marker:: PhantomData ;
88#[ 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 } ;
1113use core:: ops:: { Deref , DerefMut } ;
1214use core:: ptr:: Pointee ;
1315use core:: ptr:: { self , NonNull } ;
@@ -91,6 +93,88 @@ impl<T> ThinBox<T> {
9193
9294#[ unstable( feature = "thin_box" , issue = "92791" ) ]
9395impl < 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+ // FIXME: empty array should be automatically `Freeze` for empty arrays.
114+ unsafe impl < T > Freeze for EmptyArray < T > { }
115+
116+ // Allocate header with padding in the beginning:
117+ // ```
118+ // [ padding | header ]
119+ // ```
120+ // where the struct is aligned to both header and value.
121+ #[ repr( C ) ]
122+ struct AlignedHeader < H : Copy , T > {
123+ header_data : MaybeUninit < ReprC < H , EmptyArray < T > > > ,
124+ }
125+
126+ impl < H : Copy + Freeze , T > AlignedHeader < H , T > {
127+ const fn make ( header : H ) -> Self {
128+ let mut data = MaybeUninit :: < ReprC < H , EmptyArray < T > > > :: zeroed ( ) ;
129+ unsafe {
130+ data. as_mut_ptr ( ) . add ( 1 ) . cast :: < H > ( ) . sub ( 1 ) . write ( header) ;
131+ }
132+ AlignedHeader { header_data : data }
133+ }
134+ }
135+
136+ #[ repr( C ) ]
137+ struct DynZstAlloc < T , Dyn : ?Sized > {
138+ header : AlignedHeader < <Dyn as Pointee >:: Metadata , T > ,
139+ value : EmptyArray < T > ,
140+ }
141+
142+ impl < T , Dyn : ?Sized > DynZstAlloc < T , Dyn >
143+ where
144+ T : Unsize < Dyn > ,
145+ {
146+ const ALLOCATION : DynZstAlloc < T , Dyn > = DynZstAlloc {
147+ header : AlignedHeader :: make ( ptr:: metadata :: < Dyn > (
148+ ptr:: dangling :: < T > ( ) as * const Dyn
149+ ) ) ,
150+ value : EmptyArray { _array : [ ] } ,
151+ } ;
152+
153+ fn static_alloc < ' a > ( ) -> & ' a DynZstAlloc < T , Dyn > {
154+ & Self :: ALLOCATION
155+ }
156+ }
157+
158+ let alloc: & DynZstAlloc < T , Dyn > = DynZstAlloc :: < T , Dyn > :: static_alloc ( ) ;
159+
160+ let value_offset = mem:: offset_of!( DynZstAlloc <T , Dyn >, value) ;
161+ assert_eq ! ( value_offset, mem:: size_of:: <AlignedHeader <<Dyn as Pointee >:: Metadata , T >>( ) ) ;
162+
163+ let ptr = WithOpaqueHeader (
164+ NonNull :: new (
165+ // SAFETY: there's no overflow here because we add field offset.
166+ unsafe {
167+ ( alloc as * const DynZstAlloc < T , Dyn > as * mut u8 ) . add ( value_offset) as * mut _
168+ } ,
169+ )
170+ . unwrap ( ) ,
171+ ) ;
172+ let thin_box = ThinBox :: < Dyn > { ptr, _marker : PhantomData } ;
173+ // Forget the value to avoid double drop.
174+ mem:: forget ( value) ;
175+ thin_box
176+ }
177+
94178 /// Moves a type to the heap with its [`Metadata`] stored in the heap allocation instead of on
95179 /// the stack.
96180 ///
@@ -109,9 +193,13 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
109193 where
110194 T : Unsize < Dyn > ,
111195 {
112- let meta = ptr:: metadata ( & value as & Dyn ) ;
113- let ptr = WithOpaqueHeader :: new ( meta, value) ;
114- ThinBox { ptr, _marker : PhantomData }
196+ if mem:: size_of :: < T > ( ) == 0 {
197+ Self :: new_unsize_zst ( value)
198+ } else {
199+ let meta = ptr:: metadata ( & value as & Dyn ) ;
200+ let ptr = WithOpaqueHeader :: new ( meta, value) ;
201+ ThinBox { ptr, _marker : PhantomData }
202+ }
115203 }
116204}
117205
@@ -300,20 +388,19 @@ impl<H> WithHeader<H> {
300388
301389 impl < H > Drop for DropGuard < H > {
302390 fn drop ( & mut self ) {
391+ // All ZST are allocated statically.
392+ if self . value_layout . size ( ) == 0 {
393+ return ;
394+ }
395+
303396 unsafe {
304397 // SAFETY: Layout must have been computable if we're in drop
305398 let ( layout, value_offset) =
306399 WithHeader :: < H > :: alloc_layout ( self . value_layout ) . unwrap_unchecked ( ) ;
307400
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- }
401+ // Since we only allocate for non-ZSTs, the layout size cannot be zero.
402+ debug_assert ! ( layout. size( ) != 0 ) ;
403+ alloc:: dealloc ( self . ptr . as_ptr ( ) . sub ( value_offset) , layout) ;
317404 }
318405 }
319406 }
0 commit comments