@@ -177,7 +177,7 @@ use core::{
177
177
fmt:: { self , Debug , Display , Formatter } ,
178
178
hash:: Hasher ,
179
179
marker:: PhantomData ,
180
- mem:: { self , ManuallyDrop , MaybeUninit } ,
180
+ mem:: { self , ManuallyDrop } ,
181
181
num:: {
182
182
NonZeroI128 , NonZeroI16 , NonZeroI32 , NonZeroI64 , NonZeroI8 , NonZeroIsize , NonZeroU128 ,
183
183
NonZeroU16 , NonZeroU32 , NonZeroU64 , NonZeroU8 , NonZeroUsize , Wrapping ,
@@ -229,6 +229,31 @@ pub unsafe trait KnownLayout: sealed::KnownLayoutSealed {
229
229
#[ doc( hidden) ]
230
230
const TRAILING_SLICE_ELEM_SIZE : Option < usize > ;
231
231
232
+ /// A type which has the same layout as `Self`, but which has no validity
233
+ /// constraints.
234
+ ///
235
+ /// Roughly speaking, this type is equivalent to what the standard library's
236
+ /// [`MaybeUninit<Self>`] would be if it supported unsized types.
237
+ ///
238
+ /// # Safety
239
+ ///
240
+ /// For `T: KnownLayout`, the following must hold:
241
+ /// - Given `m: T::MaybeUninit`, it is sound to write any byte value,
242
+ /// including an uninitialized byte, at any byte offset in `m`
243
+ /// - `T` and `T::MaybeUninit` have the same alignment requirement
244
+ /// - It is valid to use an `as` cast to convert a `t: *const T` to a `m:
245
+ /// *const T::MaybeUninit` and vice-versa (and likewise for `*mut T`/`*mut
246
+ /// T::MaybeUninit`). Regardless of which direction the conversion was
247
+ /// performed, the sizes of the pointers' referents are always equal (in
248
+ /// terms of an API which is not yet stable, `size_of_val_raw(t) ==
249
+ /// size_of_val_raw(m)`).
250
+ /// - `T::MaybeUninit` contains [`UnsafeCell`]s at exactly the same byte
251
+ /// ranges that `T` does.
252
+ ///
253
+ /// [`MaybeUninit<Self>`]: core::mem::MaybeUninit
254
+ /// [`UnsafeCell`]: core::cell::UnsafeCell
255
+ type MaybeUninit : ?Sized + KnownLayout ;
256
+
232
257
/// Validates that the memory region at `addr` of length `bytes_len`
233
258
/// satisfies `Self`'s size and alignment requirements, returning `(elems,
234
259
/// split_at, prefix_suffix_bytes)`.
@@ -302,6 +327,24 @@ pub unsafe trait KnownLayout: sealed::KnownLayoutSealed {
302
327
/// elements in its trailing slice.
303
328
#[ doc( hidden) ]
304
329
fn raw_from_ptr_len ( bytes : NonNull < u8 > , elems : usize ) -> NonNull < Self > ;
330
+
331
+ /// Converts a pointer at the type level.
332
+ ///
333
+ /// # Safety
334
+ ///
335
+ /// Callers may assume that the memory region addressed by the return value
336
+ /// is the same as that addressed by the argument, and that both the return
337
+ /// value and the argument have the same provenance.
338
+ fn cast_from_maybe_uninit ( maybe_uninit : NonNull < Self :: MaybeUninit > ) -> NonNull < Self > ;
339
+
340
+ /// Converts a pointer at the type level.
341
+ ///
342
+ /// # Safety
343
+ ///
344
+ /// Callers may assume that the memory region addressed by the return value
345
+ /// is the same as that addressed by the argument, and that both the return
346
+ /// value and the argument have the same provenance.
347
+ fn cast_to_maybe_uninit ( slf : NonNull < Self > ) -> NonNull < Self :: MaybeUninit > ;
305
348
}
306
349
307
350
impl < T : KnownLayout > sealed:: KnownLayoutSealed for [ T ] { }
@@ -321,6 +364,22 @@ unsafe impl<T: KnownLayout> KnownLayout for [T] {
321
364
} ;
322
365
const TRAILING_SLICE_ELEM_SIZE : Option < usize > = Some ( mem:: size_of :: < T > ( ) ) ;
323
366
367
+ // SAFETY:
368
+ // - `MaybeUninit` has no bit validity requirements and `[U]` has the same
369
+ // bit validity requirements as `U`, so `[MaybeUninit<T>]` has no bit
370
+ // validity requirements. Thus, it is sound to write any byte value,
371
+ // including an uninitialized byte, at any byte offset.
372
+ // - Since `MaybeUninit<T>` has the same layout as `T`, and `[U]` has the
373
+ // same alignment as `U`, `[MaybeUninit<T>]` has the same alignment as
374
+ // `[T]`.
375
+ // - `[T]` and `[MaybeUninit<T>]` are both slice types, and so pointers can
376
+ // be converted using an `as` cast. Since `T` and `MaybeUninit<T>` have
377
+ // the same size, and since such a cast preserves the number of elements
378
+ // in the slice, the referent slices themselves will have the same size.
379
+ // - `MaybeUninit<T>` has the same field offsets as `[T]`, and so it
380
+ // contains `UnsafeCell`s at exactly the same byte ranges as `[T]`.
381
+ type MaybeUninit = [ mem:: MaybeUninit < T > ] ;
382
+
324
383
// SAFETY: `.cast` preserves address and provenance. The returned pointer
325
384
// refers to an object with `elems` elements by construction.
326
385
#[ inline( always) ]
@@ -329,6 +388,20 @@ unsafe impl<T: KnownLayout> KnownLayout for [T] {
329
388
#[ allow( unstable_name_collisions) ]
330
389
NonNull :: slice_from_raw_parts ( data. cast :: < T > ( ) , elems)
331
390
}
391
+
392
+ fn cast_from_maybe_uninit ( maybe_uninit : NonNull < [ mem:: MaybeUninit < T > ] > ) -> NonNull < [ T ] > {
393
+ let ( ptr, len) = ( maybe_uninit. cast :: < T > ( ) , maybe_uninit. len ( ) ) ;
394
+ // TODO(#67): Remove this allow. See NonNullExt for more details.
395
+ #[ allow( unstable_name_collisions) ]
396
+ NonNull :: slice_from_raw_parts ( ptr, len)
397
+ }
398
+
399
+ fn cast_to_maybe_uninit ( slf : NonNull < [ T ] > ) -> NonNull < [ mem:: MaybeUninit < T > ] > {
400
+ let ( ptr, len) = ( slf. cast :: < mem:: MaybeUninit < T > > ( ) , slf. len ( ) ) ;
401
+ // TODO(#67): Remove this allow. See NonNullExt for more details.
402
+ #[ allow( unstable_name_collisions) ]
403
+ NonNull :: slice_from_raw_parts ( ptr, len)
404
+ }
332
405
}
333
406
334
407
/// Implements `KnownLayout` for a sized type.
@@ -365,11 +438,36 @@ macro_rules! impl_known_layout {
365
438
// `T` is sized so it has no trailing slice.
366
439
const TRAILING_SLICE_ELEM_SIZE : Option <usize > = None ;
367
440
441
+ // SAFETY:
442
+ // - `MaybeUninit` has no validity requirements, so it is sound to
443
+ // write any byte value, including an uninitialized byte, at any
444
+ // offset.
445
+ // - `MaybeUninit<T>` has the same layout as `T`, so they have the
446
+ // same alignment requirement. For the same reason, their sizes
447
+ // are equal.
448
+ // - Since their sizes are equal, raw pointers to both types are
449
+ // thin pointers, and thus can be converted using as casts. For
450
+ // the same reason, the sizes of these pointers' referents are
451
+ // always equal.
452
+ // - `MaybeUninit<T>` has the same field offsets as `T`, and so it
453
+ // contains `UnsafeCell`s at exactly the same byte ranges as `T`.
454
+ type MaybeUninit = mem:: MaybeUninit <$ty>;
455
+
368
456
// SAFETY: `.cast` preserves address and provenance.
369
457
#[ inline( always) ]
370
458
fn raw_from_ptr_len( bytes: NonNull <u8 >, _elems: usize ) -> NonNull <Self > {
371
459
bytes. cast:: <Self >( )
372
460
}
461
+
462
+ // SAFETY: `.cast` preserves pointer address and provenance.
463
+ fn cast_from_maybe_uninit( maybe_uninit: NonNull <Self :: MaybeUninit >) -> NonNull <Self > {
464
+ maybe_uninit. cast:: <Self >( )
465
+ }
466
+
467
+ // SAFETY: `.cast` preserves pointer address and provenance.
468
+ fn cast_to_maybe_uninit( slf: NonNull <Self >) -> NonNull <Self :: MaybeUninit > {
469
+ slf. cast:: <Self :: MaybeUninit >( )
470
+ }
373
471
}
374
472
} ;
375
473
}
@@ -385,16 +483,85 @@ impl_known_layout!(
385
483
impl_known_layout ! ( T => Option <T >) ;
386
484
impl_known_layout ! ( T : ?Sized => PhantomData <T >) ;
387
485
impl_known_layout ! ( T => Wrapping <T >) ;
388
- impl_known_layout ! ( T => MaybeUninit <T >) ;
486
+ impl_known_layout ! ( T => mem :: MaybeUninit <T >) ;
389
487
impl_known_layout ! ( const N : usize , T => [ T ; N ] ) ;
390
488
391
489
safety_comment ! {
392
490
/// SAFETY:
393
491
/// `str` and `ManuallyDrop<[T]>` have the same representations as `[u8]`
394
- /// and `[T]` repsectively. `str` has different bit validity than `[u8]`,
395
- /// but that doesn't affect the soundness of this impl.
492
+ /// and `[T]` repsectively, including with respect to the locations of
493
+ /// `UnsafeCell`s. `str` has different bit validity than `[u8]`, but that
494
+ /// doesn't affect the soundness of this impl.
396
495
unsafe_impl_known_layout!( #[ repr( [ u8 ] ) ] str ) ;
397
496
unsafe_impl_known_layout!( T : ?Sized + KnownLayout => #[ repr( T ) ] ManuallyDrop <T >) ;
497
+ /// SAFETY:
498
+ /// `Cell<T>` and `UnsafeCell<T>` have the same representations, including
499
+ /// (trivially) with respect to the locations of `UnsafeCell`s.
500
+ unsafe_impl_known_layout!( T : ?Sized + KnownLayout => #[ repr( cell:: UnsafeCell <T >) ] cell:: Cell <T >) ;
501
+ }
502
+
503
+ impl < T : ?Sized + sealed:: KnownLayoutSealed > sealed:: KnownLayoutSealed for cell:: UnsafeCell < T > { }
504
+ // SAFETY: See inline comments.
505
+ unsafe impl < T : ?Sized + KnownLayout > KnownLayout for cell:: UnsafeCell < T > {
506
+ // SAFETY: `UnsafeCell<T>` and `T` have the same size, alignment, and
507
+ // trailing element size.
508
+ const FIXED_PREFIX_SIZE : usize = <T as KnownLayout >:: FIXED_PREFIX_SIZE ;
509
+ const ALIGN : NonZeroUsize = <T as KnownLayout >:: ALIGN ;
510
+ const TRAILING_SLICE_ELEM_SIZE : Option < usize > = <T as KnownLayout >:: TRAILING_SLICE_ELEM_SIZE ;
511
+
512
+ // SAFETY:
513
+ // - By `MaybeUninit` invariant, it is sound to write any byte - including
514
+ // an uninitialized byte - at any byte offset in
515
+ // `UnsafeCell<T::MaybeUninit>`.
516
+ // - `UnsafeCell<T>` and `T` have the same size, alignment, and trailing
517
+ // element size. Also, by `MaybeUninit` invariants:
518
+ // - `T` and `T::MaybeUninit` have the same alignment.
519
+ // - It is valid to cast `*const T` to `*const T::MaybeUninit` and
520
+ // vice-versa (and likewise for `*mut`), and these operations preserve
521
+ // pointer referent size.
522
+ //
523
+ // Thus, these properties hold between `UnsafeCell<T>` and
524
+ // `UnsafeCell<T::MaybeUninit>`.
525
+ // - `UnsafeCell<T>` and `UnsafeCell<T::MaybeUninit>` trivially have
526
+ // `UnsafeCell`s in exactly the same locations.
527
+ type MaybeUninit = cell:: UnsafeCell < <T as KnownLayout >:: MaybeUninit > ;
528
+
529
+ // SAFETY: All operations preserve address and provenance. Caller
530
+ // has promised that the `as` cast preserves size.
531
+ #[ inline( always) ]
532
+ fn raw_from_ptr_len ( bytes : NonNull < u8 > , elems : usize ) -> NonNull < Self > {
533
+ let slf = T :: raw_from_ptr_len ( bytes, elems) . as_ptr ( ) ;
534
+ #[ allow( clippy:: as_conversions) ]
535
+ let slf = slf as * mut cell:: UnsafeCell < T > ;
536
+ // SAFETY: `.as_ptr()` called on a non-null pointer.
537
+ unsafe { NonNull :: new_unchecked ( slf) }
538
+ }
539
+
540
+ // SAFETY: All operations preserve pointer address and provenance.
541
+ fn cast_from_maybe_uninit ( maybe_uninit : NonNull < Self :: MaybeUninit > ) -> NonNull < Self > {
542
+ #[ allow( clippy:: as_conversions) ]
543
+ let maybe_uninit = maybe_uninit. as_ptr ( ) as * mut <T as KnownLayout >:: MaybeUninit ;
544
+ // SAFETY: `.as_ptr()` called on a non-null pointer.
545
+ let maybe_uninit = unsafe { NonNull :: new_unchecked ( maybe_uninit) } ;
546
+ let repr = <T as KnownLayout >:: cast_from_maybe_uninit ( maybe_uninit) . as_ptr ( ) ;
547
+ #[ allow( clippy:: as_conversions) ]
548
+ let slf = repr as * mut Self ;
549
+ // SAFETY: `.as_ptr()` called on non-null pointer.
550
+ unsafe { NonNull :: new_unchecked ( slf) }
551
+ }
552
+
553
+ // SAFETY: `.cast` preserves pointer address and provenance.
554
+ fn cast_to_maybe_uninit ( slf : NonNull < Self > ) -> NonNull < Self :: MaybeUninit > {
555
+ #[ allow( clippy:: as_conversions) ]
556
+ let repr = slf. as_ptr ( ) as * mut T ;
557
+ // SAFETY: `.as_ptr()` called on non-null pointer.
558
+ let repr = unsafe { NonNull :: new_unchecked ( repr) } ;
559
+ let maybe_uninit = <T as KnownLayout >:: cast_to_maybe_uninit ( repr) . as_ptr ( ) ;
560
+ #[ allow( clippy:: as_conversions) ]
561
+ let maybe_uninit = maybe_uninit as * mut cell:: UnsafeCell < T :: MaybeUninit > ;
562
+ // SAFETY: `.as_ptr()` called on non-null pointer.
563
+ unsafe { NonNull :: new_unchecked ( maybe_uninit) }
564
+ }
398
565
}
399
566
400
567
/// Types for which a sequence of bytes all set to zero represents a valid
@@ -1201,17 +1368,16 @@ safety_comment! {
1201
1368
/// - `Unaligned`: `MaybeUninit<T>` is guaranteed by its documentation [1]
1202
1369
/// to have the same alignment as `T`.
1203
1370
///
1204
- /// [1]
1205
- /// https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1
1371
+ /// [1] https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1
1206
1372
///
1207
1373
/// TODO(https://github.com/google/zerocopy/issues/251): If we split
1208
1374
/// `FromBytes` and `RefFromBytes`, or if we introduce a separate
1209
1375
/// `NoCell`/`Freeze` trait, we can relax the trait bounds for `FromZeroes`
1210
1376
/// and `FromBytes`.
1211
- unsafe_impl!( T : FromZeroes => FromZeroes for MaybeUninit <T >) ;
1212
- unsafe_impl!( T : FromBytes => FromBytes for MaybeUninit <T >) ;
1213
- unsafe_impl!( T : Unaligned => Unaligned for MaybeUninit <T >) ;
1214
- assert_unaligned!( MaybeUninit <( ) >, MaybeUninit <u8 >) ;
1377
+ unsafe_impl!( T : FromZeroes => FromZeroes for mem :: MaybeUninit <T >) ;
1378
+ unsafe_impl!( T : FromBytes => FromBytes for mem :: MaybeUninit <T >) ;
1379
+ unsafe_impl!( T : Unaligned => Unaligned for mem :: MaybeUninit <T >) ;
1380
+ assert_unaligned!( mem :: MaybeUninit <( ) >, mem :: MaybeUninit <u8 >) ;
1215
1381
}
1216
1382
safety_comment ! {
1217
1383
/// SAFETY:
@@ -3716,8 +3882,11 @@ mod tests {
3716
3882
assert_impls ! ( Option <NonZeroUsize >: FromZeroes , FromBytes , AsBytes , !Unaligned ) ;
3717
3883
assert_impls ! ( Option <NonZeroIsize >: FromZeroes , FromBytes , AsBytes , !Unaligned ) ;
3718
3884
3719
- // Implements none of the ZC traits.
3885
+ // Implements none of the ZC traits, but implements `KnownLayout` so
3886
+ // that types like `MaybeUninit<KnownLayout>` can at least be written
3887
+ // down without causing errors so that we can test them.
3720
3888
struct NotZerocopy ;
3889
+ impl_known_layout ! ( NotZerocopy ) ;
3721
3890
3722
3891
assert_impls ! ( PhantomData <NotZerocopy >: FromZeroes , FromBytes , AsBytes , Unaligned ) ;
3723
3892
assert_impls ! ( PhantomData <[ u8 ] >: FromZeroes , FromBytes , AsBytes , Unaligned ) ;
@@ -3727,8 +3896,15 @@ mod tests {
3727
3896
assert_impls ! ( ManuallyDrop <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
3728
3897
assert_impls ! ( ManuallyDrop <[ NotZerocopy ] >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
3729
3898
3899
+ assert_impls ! ( mem:: MaybeUninit <u8 >: FromZeroes , FromBytes , Unaligned , !AsBytes ) ;
3900
+ assert_impls ! ( mem:: MaybeUninit <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
3901
+
3730
3902
assert_impls ! ( MaybeUninit <u8 >: FromZeroes , FromBytes , Unaligned , !AsBytes ) ;
3903
+ assert_impls ! ( MaybeUninit <MaybeUninit <u8 >>: FromZeroes , FromBytes , Unaligned , !AsBytes ) ;
3904
+ assert_impls ! ( MaybeUninit <[ u8 ] >: FromZeroes , FromBytes , Unaligned , !AsBytes ) ;
3905
+ assert_impls ! ( MaybeUninit <MaybeUninit <[ u8 ] >>: FromZeroes , FromBytes , Unaligned , !AsBytes ) ;
3731
3906
assert_impls ! ( MaybeUninit <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
3907
+ assert_impls ! ( MaybeUninit <MaybeUninit <NotZerocopy >>: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
3732
3908
3733
3909
assert_impls ! ( Wrapping <u8 >: FromZeroes , FromBytes , AsBytes , Unaligned ) ;
3734
3910
assert_impls ! ( Wrapping <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
0 commit comments