@@ -138,7 +138,11 @@ impl<T: ?Sized> ThinBox<T> {
138138 }
139139}
140140
141- /// A pointer to type-erased data, guaranteed to have a header `H` before the pointed-to location.
141+ /// A pointer to type-erased data, guaranteed to either be:
142+ /// 1. `NonNull::dangling()`, in the case where both the pointee (`T`) and
143+ /// metadata (`H`) are ZSTs.
144+ /// 2. A pointer to a valid `T` that has a header `H` directly before the
145+ /// pointed-to location.
142146struct WithHeader < H > ( NonNull < u8 > , PhantomData < H > ) ;
143147
144148impl < H > WithHeader < H > {
@@ -156,16 +160,27 @@ impl<H> WithHeader<H> {
156160 } ;
157161
158162 unsafe {
159- let ptr = alloc:: alloc ( layout) ;
160-
161- if ptr. is_null ( ) {
162- alloc:: handle_alloc_error ( layout) ;
163- }
164- // Safety:
165- // - The size is at least `aligned_header_size`.
166- let ptr = ptr. add ( value_offset) as * mut _ ;
167-
168- let ptr = NonNull :: new_unchecked ( ptr) ;
163+ // Note: It's UB to pass a layout with a zero size to `alloc::alloc`, so
164+ // we use `layout.dangling()` for this case, which should have a valid
165+ // alignment for both `T` and `H`.
166+ let ptr = if layout. size ( ) == 0 {
167+ // Some paranoia checking, mostly so that the ThinBox tests are
168+ // more able to catch issues.
169+ debug_assert ! (
170+ value_offset == 0 && mem:: size_of:: <T >( ) == 0 && mem:: size_of:: <H >( ) == 0
171+ ) ;
172+ layout. dangling ( )
173+ } else {
174+ let ptr = alloc:: alloc ( layout) ;
175+ if ptr. is_null ( ) {
176+ alloc:: handle_alloc_error ( layout) ;
177+ }
178+ // Safety:
179+ // - The size is at least `aligned_header_size`.
180+ let ptr = ptr. add ( value_offset) as * mut _ ;
181+
182+ NonNull :: new_unchecked ( ptr)
183+ } ;
169184
170185 let result = WithHeader ( ptr, PhantomData ) ;
171186 ptr:: write ( result. header ( ) , header) ;
@@ -175,18 +190,28 @@ impl<H> WithHeader<H> {
175190 }
176191 }
177192
178- // Safety:
179- // - Assumes that `value` can be dereferenced.
193+ // Safety:
194+ // - Assumes that either `value` can be dereferenced, or is the
195+ // `NonNull::dangling()` we use when both `T` and `H` are ZSTs.
180196 unsafe fn drop < T : ?Sized > ( & self , value : * mut T ) {
181197 unsafe {
198+ let value_layout = Layout :: for_value_raw ( value) ;
182199 // SAFETY: Layout must have been computable if we're in drop
183- let ( layout, value_offset) =
184- Self :: alloc_layout ( Layout :: for_value_raw ( value) ) . unwrap_unchecked ( ) ;
200+ let ( layout, value_offset) = Self :: alloc_layout ( value_layout) . unwrap_unchecked ( ) ;
185201
186- ptr:: drop_in_place :: < T > ( value) ;
187202 // We only drop the value because the Pointee trait requires that the metadata is copy
188- // aka trivially droppable
189- alloc:: dealloc ( self . 0 . as_ptr ( ) . sub ( value_offset) , layout) ;
203+ // aka trivially droppable.
204+ ptr:: drop_in_place :: < T > ( value) ;
205+
206+ // Note: Don't deallocate if the layout size is zero, because the pointer
207+ // didn't come from the allocator.
208+ if layout. size ( ) != 0 {
209+ alloc:: dealloc ( self . 0 . as_ptr ( ) . sub ( value_offset) , layout) ;
210+ } else {
211+ debug_assert ! (
212+ value_offset == 0 && mem:: size_of:: <H >( ) == 0 && value_layout. size( ) == 0
213+ ) ;
214+ }
190215 }
191216 }
192217
@@ -198,7 +223,9 @@ impl<H> WithHeader<H> {
198223 // needed to align the header. Subtracting the header size from the aligned data pointer
199224 // will always result in an aligned header pointer, it just may not point to the
200225 // beginning of the allocation.
201- unsafe { self . 0 . as_ptr ( ) . sub ( Self :: header_size ( ) ) as * mut H }
226+ let hp = unsafe { self . 0 . as_ptr ( ) . sub ( Self :: header_size ( ) ) as * mut H } ;
227+ debug_assert ! ( hp. is_aligned( ) ) ;
228+ hp
202229 }
203230
204231 fn value ( & self ) -> * mut u8 {
0 commit comments