diff --git a/src/impls.rs b/src/impls.rs index 46c66f7e73..f41973a188 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -108,10 +108,14 @@ assert_unaligned!(bool); // The value false has the bit pattern 0x00 and the value true has the bit // pattern 0x01. const _: () = unsafe { - unsafe_impl!(=> TryFromBytes for bool; |byte| { - let byte = byte.transmute_with::(); - *byte.unaligned_as_ref() < 2 - }) + unsafe_impl!(=> TryFromBytes for bool; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |byte| { + let byte = byte.transmute_with::(); + *byte.unaligned_as_ref() < 2 + } + ) }; // SAFETY: @@ -137,11 +141,15 @@ const _: () = unsafe { unsafe_impl!(char: Immutable, FromZeros, IntoBytes) }; // `from_u32()` will return `None` if the input is not a valid value for a // `char`. const _: () = unsafe { - unsafe_impl!(=> TryFromBytes for char; |c| { - let c = c.transmute_with::, invariant::Valid, CastSizedExact, BecauseImmutable>(); - let c = c.read().into_inner(); - char::from_u32(c).is_some() - }); + unsafe_impl!(=> TryFromBytes for char; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |c| { + let c = c.transmute_with::, invariant::Valid, CastSizedExact, BecauseImmutable>(); + let c = c.read().into_inner(); + char::from_u32(c).is_some() + } + ); }; // SAFETY: Per the Reference [1], `str` has the same layout as `[u8]`. @@ -170,20 +178,28 @@ const _: () = unsafe { unsafe_impl!(str: Immutable, FromZeros, IntoBytes, Unalig // // Returns `Err` if the slice is not UTF-8. const _: () = unsafe { - unsafe_impl!(=> TryFromBytes for str; |c| { - let c = c.transmute_with::<[u8], invariant::Valid, CastUnsized, BecauseImmutable>(); - let c = c.unaligned_as_ref(); - core::str::from_utf8(c).is_ok() - }) + unsafe_impl!(=> TryFromBytes for str; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |c| { + let c = c.transmute_with::<[u8], invariant::Valid, CastUnsized, BecauseImmutable>(); + let c = c.unaligned_as_ref(); + core::str::from_utf8(c).is_ok() + } + ) }; macro_rules! unsafe_impl_try_from_bytes_for_nonzero { ($($nonzero:ident[$prim:ty]),*) => { $( - unsafe_impl!(=> TryFromBytes for $nonzero; |n| { - let n = n.transmute_with::, invariant::Valid, CastSizedExact, BecauseImmutable>(); - $nonzero::new(n.read().into_inner()).is_some() - }); + unsafe_impl!(=> TryFromBytes for $nonzero; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |n| { + let n = n.transmute_with::, invariant::Valid, CastSizedExact, BecauseImmutable>(); + $nonzero::new(n.read().into_inner()).is_some() + } + ); )* } } @@ -331,7 +347,10 @@ const _: () = unsafe { #[cfg(feature = "alloc")] unsafe_impl!( #[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))] - T => TryFromBytes for Option>; |c| pointer::is_zeroed(c) + T => TryFromBytes for Option>; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |c| pointer::is_zeroed(c) ); #[cfg(feature = "alloc")] unsafe_impl!( @@ -339,35 +358,52 @@ const _: () = unsafe { T => FromZeros for Option> ); unsafe_impl!( - T => TryFromBytes for Option<&'_ T>; |c| pointer::is_zeroed(c) + T => TryFromBytes for Option<&'_ T>; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |c| pointer::is_zeroed(c) ); unsafe_impl!(T => FromZeros for Option<&'_ T>); unsafe_impl!( - T => TryFromBytes for Option<&'_ mut T>; |c| pointer::is_zeroed(c) + T => TryFromBytes for Option<&'_ mut T>; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |c| pointer::is_zeroed(c) ); unsafe_impl!(T => FromZeros for Option<&'_ mut T>); unsafe_impl!( - T => TryFromBytes for Option>; |c| pointer::is_zeroed(c) + T => TryFromBytes for Option>; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |c| pointer::is_zeroed(c) ); unsafe_impl!(T => FromZeros for Option>); unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeros for opt_fn!(...)); unsafe_impl_for_power_set!( A, B, C, D, E, F, G, H, I, J, K, L -> M => TryFromBytes for opt_fn!(...); + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; |c| pointer::is_zeroed(c) ); unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeros for opt_unsafe_fn!(...)); unsafe_impl_for_power_set!( A, B, C, D, E, F, G, H, I, J, K, L -> M => TryFromBytes for opt_unsafe_fn!(...); + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; |c| pointer::is_zeroed(c) ); unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeros for opt_extern_c_fn!(...)); unsafe_impl_for_power_set!( A, B, C, D, E, F, G, H, I, J, K, L -> M => TryFromBytes for opt_extern_c_fn!(...); + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; |c| pointer::is_zeroed(c) ); unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeros for opt_unsafe_extern_c_fn!(...)); unsafe_impl_for_power_set!( A, B, C, D, E, F, G, H, I, J, K, L -> M => TryFromBytes for opt_unsafe_extern_c_fn!(...); + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; |c| pointer::is_zeroed(c) ); }; @@ -650,6 +686,7 @@ mod atomics { #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl!(T: ?Sized => Immutable for PhantomData); + // FIXME(#2749): Justify this `Uninit`. unsafe_impl!(T: ?Sized => TryFromBytes for PhantomData); unsafe_impl!(T: ?Sized => FromZeros for PhantomData); unsafe_impl!(T: ?Sized => FromBytes for PhantomData); @@ -684,6 +721,7 @@ const _: () = unsafe { unsafe_impl!(T: Unaligned => Unaligned for Wrapping) } // `MaybeUninit` has no restrictions on its contents. #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { + // FIXME(#2749): Justify this `Uninit`. unsafe_impl!(T => TryFromBytes for CoreMaybeUninit); unsafe_impl!(T => FromZeros for CoreMaybeUninit); unsafe_impl!(T => FromBytes for CoreMaybeUninit); @@ -856,6 +894,9 @@ unsafe impl TryFromBytes for UnsafeCell { { } + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + #[inline(always)] fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool where @@ -889,48 +930,56 @@ unsafe impl TryFromBytes for UnsafeCell { #[allow(clippy::multiple_unsafe_ops_per_block)] const _: () = unsafe { unsafe_impl!(const N: usize, T: Immutable => Immutable for [T; N]); - unsafe_impl!(const N: usize, T: TryFromBytes => TryFromBytes for [T; N]; |c| { - let c: Ptr<'_, [ReadOnly; N], _> = c.cast::<_, crate::pointer::cast::CastSized, _>(); - let c: Ptr<'_, [ReadOnly], _> = c.as_slice(); - let c: Ptr<'_, ReadOnly<[T]>, _> = c.cast::<_, crate::pointer::cast::CastUnsized, _>(); - - // Note that this call may panic, but it would still be sound even if it - // did. `is_bit_valid` does not promise that it will not panic (in fact, - // it explicitly warns that it's a possibility), and we have not - // violated any safety invariants that we must fix before returning. - <[T] as TryFromBytes>::is_bit_valid(c) - }); + unsafe_impl!(const N: usize, T: TryFromBytes => TryFromBytes for [T; N]; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |c| { + let c: Ptr<'_, [ReadOnly; N], _> = c.cast::<_, crate::pointer::cast::CastSized, _>(); + let c: Ptr<'_, [ReadOnly], _> = c.as_slice(); + let c: Ptr<'_, ReadOnly<[T]>, _> = c.cast::<_, crate::pointer::cast::CastUnsized, _>(); + + // Note that this call may panic, but it would still be sound even if it + // did. `is_bit_valid` does not promise that it will not panic (in fact, + // it explicitly warns that it's a possibility), and we have not + // violated any safety invariants that we must fix before returning. + <[T] as TryFromBytes>::is_bit_valid(c) + } + ); unsafe_impl!(const N: usize, T: FromZeros => FromZeros for [T; N]); unsafe_impl!(const N: usize, T: FromBytes => FromBytes for [T; N]); unsafe_impl!(const N: usize, T: IntoBytes => IntoBytes for [T; N]); unsafe_impl!(const N: usize, T: Unaligned => Unaligned for [T; N]); assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]); unsafe_impl!(T: Immutable => Immutable for [T]); - unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; |c| { - let c: Ptr<'_, [ReadOnly], _> = c.cast::<_, crate::pointer::cast::CastUnsized, _>(); + unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |c| { + let c: Ptr<'_, [ReadOnly], _> = c.cast::<_, crate::pointer::cast::CastUnsized, _>(); - // SAFETY: Per the reference [1]: - // - // An array of `[T; N]` has a size of `size_of::() * N` and the - // same alignment of `T`. Arrays are laid out so that the zero-based - // `nth` element of the array is offset from the start of the array by - // `n * size_of::()` bytes. - // - // ... - // - // Slices have the same layout as the section of the array they slice. - // - // In other words, the layout of a `[T] is a sequence of `T`s laid out - // back-to-back with no bytes in between. If all elements in `candidate` - // are `is_bit_valid`, so too is `candidate`. - // - // Note that any of the below calls may panic, but it would still be - // sound even if it did. `is_bit_valid` does not promise that it will - // not panic (in fact, it explicitly warns that it's a possibility), and - // we have not violated any safety invariants that we must fix before - // returning. - c.iter().all(::is_bit_valid) - }); + // SAFETY: Per the reference [1]: + // + // An array of `[T; N]` has a size of `size_of::() * N` and the + // same alignment of `T`. Arrays are laid out so that the zero-based + // `nth` element of the array is offset from the start of the array by + // `n * size_of::()` bytes. + // + // ... + // + // Slices have the same layout as the section of the array they slice. + // + // In other words, the layout of a `[T] is a sequence of `T`s laid out + // back-to-back with no bytes in between. If all elements in `candidate` + // are `is_bit_valid`, so too is `candidate`. + // + // Note that any of the below calls may panic, but it would still be + // sound even if it did. `is_bit_valid` does not promise that it will + // not panic (in fact, it explicitly warns that it's a possibility), and + // we have not violated any safety invariants that we must fix before + // returning. + c.iter().all(::is_bit_valid) + } + ); unsafe_impl!(T: FromZeros => FromZeros for [T]); unsafe_impl!(T: FromBytes => FromBytes for [T]); unsafe_impl!(T: IntoBytes => IntoBytes for [T]); @@ -963,9 +1012,17 @@ const _: () = unsafe { const _: () = unsafe { unsafe_impl!(T: ?Sized => Immutable for *const T); unsafe_impl!(T: ?Sized => Immutable for *mut T); - unsafe_impl!(T => TryFromBytes for *const T; |c| pointer::is_zeroed(c)); + unsafe_impl!(T => TryFromBytes for *const T; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |c| pointer::is_zeroed(c) + ); unsafe_impl!(T => FromZeros for *const T); - unsafe_impl!(T => TryFromBytes for *mut T; |c| pointer::is_zeroed(c)); + unsafe_impl!(T => TryFromBytes for *mut T; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + |c| pointer::is_zeroed(c) + ); unsafe_impl!(T => FromZeros for *mut T); }; @@ -990,6 +1047,8 @@ const _: () = unsafe { unsafe_impl!(T: Immutable => Immutable for Option) }; mod tuples { use super::*; + type Uninit = crate::invariant::Uninit; + /// Generates various trait implementations for tuples. /// /// # Safety @@ -1011,11 +1070,14 @@ mod tuples { unsafe_impl!($($head_T: Immutable,)* $next_T: Immutable => Immutable for ($($head_T,)* $next_T,)); // SAFETY: If all fields in `c` are `is_bit_valid`, so too is `c`. - unsafe_impl!($($head_T: TryFromBytes,)* $next_T: TryFromBytes => TryFromBytes for ($($head_T,)* $next_T,); |c| { - let mut c = c; - $(TryFromBytes::is_bit_valid(into_inner!(c.reborrow().project::<_, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!($head_I) }>())) &&)* - TryFromBytes::is_bit_valid(into_inner!(c.reborrow().project::<_, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!($next_I) }>())) - }); + unsafe_impl!($($head_T: TryFromBytes,)* $next_T: TryFromBytes => TryFromBytes for ($($head_T,)* $next_T,); + type Uninit = ($(Uninit<$head_I>,)* Uninit<$next_I>,); + |c| { + let mut c = c; + $(TryFromBytes::is_bit_valid(into_inner!(c.reborrow().project::<_, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!($head_I) }>())) &&)* + TryFromBytes::is_bit_valid(into_inner!(c.reborrow().project::<_, { crate::STRUCT_VARIANT_ID }, { crate::ident_id!($next_I) }>())) + } + ); // SAFETY: If all fields in `Self` are `FromZeros`, so too is `Self`. unsafe_impl!($($head_T: FromZeros,)* $next_T: FromZeros => FromZeros for ($($head_T,)* $next_T,)); diff --git a/src/lib.rs b/src/lib.rs index 0603659c5a..9b488e8230 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1700,6 +1700,10 @@ pub unsafe trait TryFromBytes { where Self: Sized; + /// The validity of `Self` in an uninitialized state. + #[doc(hidden)] + type Uninit; + /// Does a given memory range contain a valid instance of `Self`? /// /// # Safety diff --git a/src/util/macros.rs b/src/util/macros.rs index 28fedadac9..160fd6840f 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -21,13 +21,13 @@ /// must only return `true` if its argument refers to a valid `$ty`. macro_rules! unsafe_impl { // Implement `$trait` for `$ty` with no bounds. - ($(#[$attr:meta])* $ty:ty: $trait:ident $(; |$candidate:ident| $is_bit_valid:expr)?) => {{ + ($(#[$attr:meta])* $ty:ty: $trait:ident $(; type Uninit = $uninit_state:ty; |$candidate:ident| $is_bit_valid:expr)?) => {{ crate::util::macros::__unsafe(); $(#[$attr])* // SAFETY: The caller promises that this is sound. unsafe impl $trait for $ty { - unsafe_impl!(@method $trait $(; |$candidate| $is_bit_valid)?); + unsafe_impl!(@method $trait $(; type Uninit = $uninit_state; |$candidate| $is_bit_valid)?); } }}; @@ -90,26 +90,26 @@ macro_rules! unsafe_impl { $(#[$attr:meta])* const $constname:ident : $constty:ident $(,)? $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),* - => $trait:ident for $ty:ty $(; |$candidate:ident| $is_bit_valid:expr)? + => $trait:ident for $ty:ty $(; type Uninit = $uninit_state:ty; |$candidate:ident| $is_bit_valid:expr)? ) => { unsafe_impl!( @inner $(#[$attr])* @const $constname: $constty, $($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)* - => $trait for $ty $(; |$candidate| $is_bit_valid)? + => $trait for $ty $(; type Uninit = $uninit_state; |$candidate| $is_bit_valid)? ); }; ( $(#[$attr:meta])* $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),* - => $trait:ident for $ty:ty $(; |$candidate:ident| $is_bit_valid:expr)? + => $trait:ident for $ty:ty $(; type Uninit = $uninit_state:ty; |$candidate:ident| $is_bit_valid:expr)? ) => {{ unsafe_impl!( @inner $(#[$attr])* $($tyvar $(: $(? $optbound +)* + $($bound +)*)?,)* - => $trait for $ty $(; |$candidate| $is_bit_valid)? + => $trait for $ty $(; type Uninit = $uninit_state; |$candidate| $is_bit_valid)? ); }}; ( @@ -117,7 +117,7 @@ macro_rules! unsafe_impl { $(#[$attr:meta])* $(@const $constname:ident : $constty:ident,)* $($tyvar:ident $(: $(? $optbound:ident +)* + $($bound:ident +)* )?,)* - => $trait:ident for $ty:ty $(; |$candidate:ident| $is_bit_valid:expr)? + => $trait:ident for $ty:ty $(; type Uninit = $uninit_state:ty; |$candidate:ident| $is_bit_valid:expr)? ) => {{ crate::util::macros::__unsafe(); @@ -125,15 +125,17 @@ macro_rules! unsafe_impl { #[allow(non_local_definitions)] // SAFETY: The caller promises that this is sound. unsafe impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),* $(, const $constname: $constty,)*> $trait for $ty { - unsafe_impl!(@method $trait $(; |$candidate| $is_bit_valid)?); + unsafe_impl!(@method $trait $(; type Uninit = $uninit_state; |$candidate| $is_bit_valid)?); } }}; - (@method TryFromBytes ; |$candidate:ident| $is_bit_valid:expr) => { + (@method TryFromBytes ; type Uninit = $uninit_state:ty; |$candidate:ident| $is_bit_valid:expr) => { #[allow(clippy::missing_inline_in_public_items, dead_code)] #[cfg_attr(all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), coverage(off))] fn only_derive_is_allowed_to_implement_this_trait() {} + type Uninit = $uninit_state; + #[inline] fn is_bit_valid($candidate: Maybe<'_, Self, Alignment>) -> bool where @@ -146,6 +148,10 @@ macro_rules! unsafe_impl { #[allow(clippy::missing_inline_in_public_items)] #[cfg_attr(all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), coverage(off))] fn only_derive_is_allowed_to_implement_this_trait() {} + + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + #[inline(always)] fn is_bit_valid(_candidate: Maybe<'_, Self, Alignment>) -> bool where @@ -226,6 +232,9 @@ macro_rules! impl_for_transmute_from { $(<$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?>)? TryFromBytes for $ty:ty [$repr:ty] ) => { + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; + #[inline(always)] fn is_bit_valid(candidate: $crate::Maybe<'_, Self, Alignment>) -> bool where @@ -267,33 +276,33 @@ macro_rules! impl_for_transmute_from { macro_rules! unsafe_impl_for_power_set { ( $first:ident $(, $rest:ident)* $(-> $ret:ident)? => $trait:ident for $macro:ident!(...) - $(; |$candidate:ident| $is_bit_valid:expr)? + $(; type Uninit = $uninit_state:ty; |$candidate:ident| $is_bit_valid:expr)? ) => { unsafe_impl_for_power_set!( $($rest),* $(-> $ret)? => $trait for $macro!(...) - $(; |$candidate| $is_bit_valid)? + $(; type Uninit = $uninit_state; |$candidate| $is_bit_valid)? ); unsafe_impl_for_power_set!( @impl $first $(, $rest)* $(-> $ret)? => $trait for $macro!(...) - $(; |$candidate| $is_bit_valid)? + $(; type Uninit = $uninit_state; |$candidate| $is_bit_valid)? ); }; ( $(-> $ret:ident)? => $trait:ident for $macro:ident!(...) - $(; |$candidate:ident| $is_bit_valid:expr)? + $(; type Uninit = $uninit_state:ty; |$candidate:ident| $is_bit_valid:expr)? ) => { unsafe_impl_for_power_set!( @impl $(-> $ret)? => $trait for $macro!(...) - $(; |$candidate| $is_bit_valid)? + $(; type Uninit = $uninit_state; |$candidate| $is_bit_valid)? ); }; ( @impl $($vars:ident),* $(-> $ret:ident)? => $trait:ident for $macro:ident!(...) - $(; |$candidate:ident| $is_bit_valid:expr)? + $(; type Uninit = $uninit_state:ty; |$candidate:ident| $is_bit_valid:expr)? ) => { unsafe_impl!( $($vars,)* $($ret)? => $trait for $macro!($($vars),* $(-> $ret)?) - $(; |$candidate| $is_bit_valid)? + $(; type Uninit = $uninit_state; |$candidate| $is_bit_valid)? ); }; } @@ -393,11 +402,11 @@ macro_rules! impl_or_verify { }; ( $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),* - => $trait:ident for $ty:ty $(; |$candidate:ident| $is_bit_valid:expr)? + => $trait:ident for $ty:ty $(; type Uninit = $uninit_state:ty; |$candidate:ident| $is_bit_valid:expr)? ) => { impl_or_verify!(@impl { unsafe_impl!( $($tyvar $(: $(? $optbound +)* $($bound +)*)?),* => $trait for $ty - $(; |$candidate| $is_bit_valid)? + $(; type Uninit = $uninit_state; |$candidate| $is_bit_valid)? ); }); impl_or_verify!(@verify $trait, { impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?),*> Subtrait for $ty {} diff --git a/src/wrappers.rs b/src/wrappers.rs index 5f2414b0c6..9ce7e4ed1c 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -149,6 +149,8 @@ const _: () = unsafe { impl_or_verify!(T: Immutable => Immutable for Unalign); impl_or_verify!( T: TryFromBytes => TryFromBytes for Unalign; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; |c| T::is_bit_valid(c.transmute::<_, _, BecauseImmutable>()) ); impl_or_verify!(T: FromZeros => FromZeros for Unalign); @@ -668,6 +670,8 @@ const _: () = unsafe { unsafe_impl!(T: ?Sized + Unaligned => Unaligned for ReadOnly); unsafe_impl!( T: ?Sized + TryFromBytes => TryFromBytes for ReadOnly; + // FIXME(#2749): Justify this `Uninit`. + type Uninit = crate::invariant::Uninit; |c| T::is_bit_valid(c.cast::<_, as SizeEq>>>::CastFrom, _>()) ); unsafe_impl!(T: ?Sized + FromZeros => FromZeros for ReadOnly);