@@ -138,7 +138,11 @@ impl<T: ?Sized> ThinBox<T> {
138
138
}
139
139
}
140
140
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.
142
146
struct WithHeader < H > ( NonNull < u8 > , PhantomData < H > ) ;
143
147
144
148
impl < H > WithHeader < H > {
@@ -156,16 +160,27 @@ impl<H> WithHeader<H> {
156
160
} ;
157
161
158
162
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
+ } ;
169
184
170
185
let result = WithHeader ( ptr, PhantomData ) ;
171
186
ptr:: write ( result. header ( ) , header) ;
@@ -175,18 +190,28 @@ impl<H> WithHeader<H> {
175
190
}
176
191
}
177
192
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.
180
196
unsafe fn drop < T : ?Sized > ( & self , value : * mut T ) {
181
197
unsafe {
198
+ let value_layout = Layout :: for_value_raw ( value) ;
182
199
// 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 ( ) ;
185
201
186
- ptr:: drop_in_place :: < T > ( value) ;
187
202
// 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
+ }
190
215
}
191
216
}
192
217
@@ -198,7 +223,9 @@ impl<H> WithHeader<H> {
198
223
// needed to align the header. Subtracting the header size from the aligned data pointer
199
224
// will always result in an aligned header pointer, it just may not point to the
200
225
// 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
202
229
}
203
230
204
231
fn value ( & self ) -> * mut u8 {
0 commit comments