@@ -177,7 +177,7 @@ use core::{
177177 fmt:: { self , Debug , Display , Formatter } ,
178178 hash:: Hasher ,
179179 marker:: PhantomData ,
180- mem:: { self , ManuallyDrop , MaybeUninit } ,
180+ mem:: { self , ManuallyDrop } ,
181181 num:: {
182182 NonZeroI128 , NonZeroI16 , NonZeroI32 , NonZeroI64 , NonZeroI8 , NonZeroIsize , NonZeroU128 ,
183183 NonZeroU16 , NonZeroU32 , NonZeroU64 , NonZeroU8 , NonZeroUsize , Wrapping ,
@@ -229,11 +229,54 @@ pub unsafe trait KnownLayout: sealed::KnownLayoutSealed {
229229 #[ doc( hidden) ]
230230 const TRAILING_SLICE_ELEM_SIZE : Option < usize > ;
231231
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+
232257 /// SAFETY: The returned pointer has the same address and provenance as
233258 /// `bytes`. If `Self` is a DST, the returned pointer's referent has `elems`
234259 /// elements in its trailing slice.
235260 #[ doc( hidden) ]
236261 fn raw_from_ptr_len ( bytes : NonNull < u8 > , elems : usize ) -> NonNull < Self > ;
262+
263+ /// Converts a pointer at the type level.
264+ ///
265+ /// # Safety
266+ ///
267+ /// Callers may assume that the memory region addressed by the return value
268+ /// is the same as that addressed by the argument, and that both the return
269+ /// value and the argument have the same provenance.
270+ fn cast_from_maybe_uninit ( maybe_uninit : NonNull < Self :: MaybeUninit > ) -> NonNull < Self > ;
271+
272+ /// Converts a pointer at the type level.
273+ ///
274+ /// # Safety
275+ ///
276+ /// Callers may assume that the memory region addressed by the return value
277+ /// is the same as that addressed by the argument, and that both the return
278+ /// value and the argument have the same provenance.
279+ fn cast_to_maybe_uninit ( slf : NonNull < Self > ) -> NonNull < Self :: MaybeUninit > ;
237280}
238281
239282impl < T : KnownLayout > sealed:: KnownLayoutSealed for [ T ] { }
@@ -253,6 +296,22 @@ unsafe impl<T: KnownLayout> KnownLayout for [T] {
253296 } ;
254297 const TRAILING_SLICE_ELEM_SIZE : Option < usize > = Some ( mem:: size_of :: < T > ( ) ) ;
255298
299+ // SAFETY:
300+ // - `MaybeUninit` has no bit validity requirements and `[U]` has the same
301+ // bit validity requirements as `U`, so `[MaybeUninit<T>]` has no bit
302+ // validity requirements. Thus, it is sound to write any byte value,
303+ // including an uninitialized byte, at any byte offset.
304+ // - Since `MaybeUninit<T>` has the same layout as `T`, and `[U]` has the
305+ // same alignment as `U`, `[MaybeUninit<T>]` has the same alignment as
306+ // `[T]`.
307+ // - `[T]` and `[MaybeUninit<T>]` are both slice types, and so pointers can
308+ // be converted using an `as` cast. Since `T` and `MaybeUninit<T>` have
309+ // the same size, and since such a cast preserves the number of elements
310+ // in the slice, the referent slices themselves will have the same size.
311+ // - `MaybeUninit<T>` has the same field offsets as `[T]`, and so it
312+ // contains `UnsafeCell`s at exactly the same byte ranges as `[T]`.
313+ type MaybeUninit = [ mem:: MaybeUninit < T > ] ;
314+
256315 // SAFETY: `.cast` preserves address and provenance. The returned pointer
257316 // refers to an object with `elems` elements by construction.
258317 #[ inline( always) ]
@@ -261,6 +320,20 @@ unsafe impl<T: KnownLayout> KnownLayout for [T] {
261320 #[ allow( unstable_name_collisions) ]
262321 NonNull :: slice_from_raw_parts ( data. cast :: < T > ( ) , elems)
263322 }
323+
324+ fn cast_from_maybe_uninit ( maybe_uninit : NonNull < [ mem:: MaybeUninit < T > ] > ) -> NonNull < [ T ] > {
325+ let ( ptr, len) = ( maybe_uninit. cast :: < T > ( ) , maybe_uninit. len ( ) ) ;
326+ // TODO(#67): Remove this allow. See NonNullExt for more details.
327+ #[ allow( unstable_name_collisions) ]
328+ NonNull :: slice_from_raw_parts ( ptr, len)
329+ }
330+
331+ fn cast_to_maybe_uninit ( slf : NonNull < [ T ] > ) -> NonNull < [ mem:: MaybeUninit < T > ] > {
332+ let ( ptr, len) = ( slf. cast :: < mem:: MaybeUninit < T > > ( ) , slf. len ( ) ) ;
333+ // TODO(#67): Remove this allow. See NonNullExt for more details.
334+ #[ allow( unstable_name_collisions) ]
335+ NonNull :: slice_from_raw_parts ( ptr, len)
336+ }
264337}
265338
266339/// Implements `KnownLayout` for a sized type.
@@ -297,11 +370,36 @@ macro_rules! impl_known_layout {
297370 // `T` is sized so it has no trailing slice.
298371 const TRAILING_SLICE_ELEM_SIZE : Option <usize > = None ;
299372
373+ // SAFETY:
374+ // - `MaybeUninit` has no validity requirements, so it is sound to
375+ // write any byte value, including an uninitialized byte, at any
376+ // offset.
377+ // - `MaybeUninit<T>` has the same layout as `T`, so they have the
378+ // same alignment requirement. For the same reason, their sizes
379+ // are equal.
380+ // - Since their sizes are equal, raw pointers to both types are
381+ // thin pointers, and thus can be converted using as casts. For
382+ // the same reason, the sizes of these pointers' referents are
383+ // always equal.
384+ // - `MaybeUninit<T>` has the same field offsets as `T`, and so it
385+ // contains `UnsafeCell`s at exactly the same byte ranges as `T`.
386+ type MaybeUninit = mem:: MaybeUninit <$ty>;
387+
300388 // SAFETY: `.cast` preserves address and provenance.
301389 #[ inline( always) ]
302390 fn raw_from_ptr_len( bytes: NonNull <u8 >, _elems: usize ) -> NonNull <Self > {
303391 bytes. cast:: <Self >( )
304392 }
393+
394+ // SAFETY: `.cast` preserves pointer address and provenance.
395+ fn cast_from_maybe_uninit( maybe_uninit: NonNull <Self :: MaybeUninit >) -> NonNull <Self > {
396+ maybe_uninit. cast:: <Self >( )
397+ }
398+
399+ // SAFETY: `.cast` preserves pointer address and provenance.
400+ fn cast_to_maybe_uninit( slf: NonNull <Self >) -> NonNull <Self :: MaybeUninit > {
401+ slf. cast:: <Self :: MaybeUninit >( )
402+ }
305403 }
306404 } ;
307405}
@@ -317,16 +415,85 @@ impl_known_layout!(
317415impl_known_layout ! ( T => Option <T >) ;
318416impl_known_layout ! ( T : ?Sized => PhantomData <T >) ;
319417impl_known_layout ! ( T => Wrapping <T >) ;
320- impl_known_layout ! ( T => MaybeUninit <T >) ;
418+ impl_known_layout ! ( T => mem :: MaybeUninit <T >) ;
321419impl_known_layout ! ( const N : usize , T => [ T ; N ] ) ;
322420
323421safety_comment ! {
324422 /// SAFETY:
325423 /// `str` and `ManuallyDrop<[T]>` have the same representations as `[u8]`
326- /// and `[T]` repsectively. `str` has different bit validity than `[u8]`,
327- /// but that doesn't affect the soundness of this impl.
424+ /// and `[T]` repsectively, including with respect to the locations of
425+ /// `UnsafeCell`s. `str` has different bit validity than `[u8]`, but that
426+ /// doesn't affect the soundness of this impl.
328427 unsafe_impl_known_layout!( #[ repr( [ u8 ] ) ] str ) ;
329428 unsafe_impl_known_layout!( T : ?Sized + KnownLayout => #[ repr( T ) ] ManuallyDrop <T >) ;
429+ /// SAFETY:
430+ /// `Cell<T>` and `UnsafeCell<T>` have the same representations, including
431+ /// (trivially) with respect to the locations of `UnsafeCell`s.
432+ unsafe_impl_known_layout!( T : ?Sized + KnownLayout => #[ repr( cell:: UnsafeCell <T >) ] cell:: Cell <T >) ;
433+ }
434+
435+ impl < T : ?Sized + sealed:: KnownLayoutSealed > sealed:: KnownLayoutSealed for cell:: UnsafeCell < T > { }
436+ // SAFETY: See inline comments.
437+ unsafe impl < T : ?Sized + KnownLayout > KnownLayout for cell:: UnsafeCell < T > {
438+ // SAFETY: `UnsafeCell<T>` and `T` have the same size, alignment, and
439+ // trailing element size.
440+ const FIXED_PREFIX_SIZE : usize = <T as KnownLayout >:: FIXED_PREFIX_SIZE ;
441+ const ALIGN : NonZeroUsize = <T as KnownLayout >:: ALIGN ;
442+ const TRAILING_SLICE_ELEM_SIZE : Option < usize > = <T as KnownLayout >:: TRAILING_SLICE_ELEM_SIZE ;
443+
444+ // SAFETY:
445+ // - By `MaybeUninit` invariant, it is sound to write any byte - including
446+ // an uninitialized byte - at any byte offset in
447+ // `UnsafeCell<T::MaybeUninit>`.
448+ // - `UnsafeCell<T>` and `T` have the same size, alignment, and trailing
449+ // element size. Also, by `MaybeUninit` invariants:
450+ // - `T` and `T::MaybeUninit` have the same alignment.
451+ // - It is valid to cast `*const T` to `*const T::MaybeUninit` and
452+ // vice-versa (and likewise for `*mut`), and these operations preserve
453+ // pointer referent size.
454+ //
455+ // Thus, these properties hold between `UnsafeCell<T>` and
456+ // `UnsafeCell<T::MaybeUninit>`.
457+ // - `UnsafeCell<T>` and `UnsafeCell<T::MaybeUninit>` trivially have
458+ // `UnsafeCell`s in exactly the same locations.
459+ type MaybeUninit = cell:: UnsafeCell < <T as KnownLayout >:: MaybeUninit > ;
460+
461+ // SAFETY: All operations preserve address and provenance. Caller
462+ // has promised that the `as` cast preserves size.
463+ #[ inline( always) ]
464+ fn raw_from_ptr_len ( bytes : NonNull < u8 > , elems : usize ) -> NonNull < Self > {
465+ let slf = T :: raw_from_ptr_len ( bytes, elems) . as_ptr ( ) ;
466+ #[ allow( clippy:: as_conversions) ]
467+ let slf = slf as * mut cell:: UnsafeCell < T > ;
468+ // SAFETY: `.as_ptr()` called on a non-null pointer.
469+ unsafe { NonNull :: new_unchecked ( slf) }
470+ }
471+
472+ // SAFETY: All operations preserve pointer address and provenance.
473+ fn cast_from_maybe_uninit ( maybe_uninit : NonNull < Self :: MaybeUninit > ) -> NonNull < Self > {
474+ #[ allow( clippy:: as_conversions) ]
475+ let maybe_uninit = maybe_uninit. as_ptr ( ) as * mut <T as KnownLayout >:: MaybeUninit ;
476+ // SAFETY: `.as_ptr()` called on a non-null pointer.
477+ let maybe_uninit = unsafe { NonNull :: new_unchecked ( maybe_uninit) } ;
478+ let repr = <T as KnownLayout >:: cast_from_maybe_uninit ( maybe_uninit) . as_ptr ( ) ;
479+ #[ allow( clippy:: as_conversions) ]
480+ let slf = repr as * mut Self ;
481+ // SAFETY: `.as_ptr()` called on non-null pointer.
482+ unsafe { NonNull :: new_unchecked ( slf) }
483+ }
484+
485+ // SAFETY: `.cast` preserves pointer address and provenance.
486+ fn cast_to_maybe_uninit ( slf : NonNull < Self > ) -> NonNull < Self :: MaybeUninit > {
487+ #[ allow( clippy:: as_conversions) ]
488+ let repr = slf. as_ptr ( ) as * mut T ;
489+ // SAFETY: `.as_ptr()` called on non-null pointer.
490+ let repr = unsafe { NonNull :: new_unchecked ( repr) } ;
491+ let maybe_uninit = <T as KnownLayout >:: cast_to_maybe_uninit ( repr) . as_ptr ( ) ;
492+ #[ allow( clippy:: as_conversions) ]
493+ let maybe_uninit = maybe_uninit as * mut cell:: UnsafeCell < T :: MaybeUninit > ;
494+ // SAFETY: `.as_ptr()` called on non-null pointer.
495+ unsafe { NonNull :: new_unchecked ( maybe_uninit) }
496+ }
330497}
331498
332499/// Types for which a sequence of bytes all set to zero represents a valid
@@ -1133,17 +1300,16 @@ safety_comment! {
11331300 /// - `Unaligned`: `MaybeUninit<T>` is guaranteed by its documentation [1]
11341301 /// to have the same alignment as `T`.
11351302 ///
1136- /// [1]
1137- /// https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1
1303+ /// [1] https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1
11381304 ///
11391305 /// TODO(https://github.com/google/zerocopy/issues/251): If we split
11401306 /// `FromBytes` and `RefFromBytes`, or if we introduce a separate
11411307 /// `NoCell`/`Freeze` trait, we can relax the trait bounds for `FromZeroes`
11421308 /// and `FromBytes`.
1143- unsafe_impl!( T : FromZeroes => FromZeroes for MaybeUninit <T >) ;
1144- unsafe_impl!( T : FromBytes => FromBytes for MaybeUninit <T >) ;
1145- unsafe_impl!( T : Unaligned => Unaligned for MaybeUninit <T >) ;
1146- assert_unaligned!( MaybeUninit <( ) >, MaybeUninit <u8 >) ;
1309+ unsafe_impl!( T : FromZeroes => FromZeroes for mem :: MaybeUninit <T >) ;
1310+ unsafe_impl!( T : FromBytes => FromBytes for mem :: MaybeUninit <T >) ;
1311+ unsafe_impl!( T : Unaligned => Unaligned for mem :: MaybeUninit <T >) ;
1312+ assert_unaligned!( mem :: MaybeUninit <( ) >, mem :: MaybeUninit <u8 >) ;
11471313}
11481314safety_comment ! {
11491315 /// SAFETY:
@@ -3648,8 +3814,11 @@ mod tests {
36483814 assert_impls ! ( Option <NonZeroUsize >: FromZeroes , FromBytes , AsBytes , !Unaligned ) ;
36493815 assert_impls ! ( Option <NonZeroIsize >: FromZeroes , FromBytes , AsBytes , !Unaligned ) ;
36503816
3651- // Implements none of the ZC traits.
3817+ // Implements none of the ZC traits, but implements `KnownLayout` so
3818+ // that types like `MaybeUninit<KnownLayout>` can at least be written
3819+ // down without causing errors so that we can test them.
36523820 struct NotZerocopy ;
3821+ impl_known_layout ! ( NotZerocopy ) ;
36533822
36543823 assert_impls ! ( PhantomData <NotZerocopy >: FromZeroes , FromBytes , AsBytes , Unaligned ) ;
36553824 assert_impls ! ( PhantomData <[ u8 ] >: FromZeroes , FromBytes , AsBytes , Unaligned ) ;
@@ -3659,8 +3828,15 @@ mod tests {
36593828 assert_impls ! ( ManuallyDrop <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
36603829 assert_impls ! ( ManuallyDrop <[ NotZerocopy ] >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
36613830
3831+ assert_impls ! ( mem:: MaybeUninit <u8 >: FromZeroes , FromBytes , Unaligned , !AsBytes ) ;
3832+ assert_impls ! ( mem:: MaybeUninit <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
3833+
36623834 assert_impls ! ( MaybeUninit <u8 >: FromZeroes , FromBytes , Unaligned , !AsBytes ) ;
3835+ assert_impls ! ( MaybeUninit <MaybeUninit <u8 >>: FromZeroes , FromBytes , Unaligned , !AsBytes ) ;
3836+ assert_impls ! ( MaybeUninit <[ u8 ] >: FromZeroes , FromBytes , Unaligned , !AsBytes ) ;
3837+ assert_impls ! ( MaybeUninit <MaybeUninit <[ u8 ] >>: FromZeroes , FromBytes , Unaligned , !AsBytes ) ;
36633838 assert_impls ! ( MaybeUninit <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
3839+ assert_impls ! ( MaybeUninit <MaybeUninit <NotZerocopy >>: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
36643840
36653841 assert_impls ! ( Wrapping <u8 >: FromZeroes , FromBytes , AsBytes , Unaligned ) ;
36663842 assert_impls ! ( Wrapping <NotZerocopy >: !FromZeroes , !FromBytes , !AsBytes , !Unaligned ) ;
0 commit comments