diff --git a/src/derive_util.rs b/src/derive_util.rs index 8f72ef09c6..0aa357fb87 100644 --- a/src/derive_util.rs +++ b/src/derive_util.rs @@ -62,3 +62,72 @@ macro_rules! union_has_padding { false $(|| core::mem::size_of::<$t>() != core::mem::size_of::<$ts>())* }; } + +#[doc(hidden)] +pub use project::project as __project; + +/// Implements `TryFromBytes` for a struct type by delegating to existing +/// implementations for each of its fields, and optionally supports a custom +/// validation method. +/// +/// ```rust +/// # use zerocopy::impl_try_from_bytes_for_struct; +/// +/// #[repr(C)] +/// struct Foo { +/// a: u8, +/// b: u16, +/// } +/// +/// impl_try_from_bytes_for_struct!(Foo { a: u8, b: u16 }); +/// +/// #[repr(transparent)] +/// struct Bar(Foo); +/// +/// impl Bar { +/// fn is_valid(&self) -> bool { +/// u16::from(self.0.a) < self.0.b +/// } +/// } +/// +/// impl_try_from_bytes_for_struct!(Bar { 0: Foo } => is_valid); +/// ``` +/// +/// # Safety +/// +/// `$ty` must be a struct type, `$f` must list every field's name, and `$f_ty` +/// must be the correct types for those fields. +#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`. +#[macro_export] +macro_rules! impl_try_from_bytes_for_struct { + ($ty:ty { $($f:tt: $f_ty:ty),* } $(=> $validation_method:ident)?) => { + // SAFETY: The caller promises that all fields are listed with their + // correct types. We validate that every field is valid, which is the + // only requirement for the entire struct to be valid. Thus, we + // correctly implement `is_bit_valid` as required by the trait's safety + // documentation. + #[allow(unused_qualifications)] + unsafe impl zerocopy::TryFromBytes for $ty { + fn is_bit_valid(bytes: &zerocopy::MaybeValid) -> bool { + true $(&& { + let f: &zerocopy::MaybeValid<$f_ty> + = zerocopy::derive_util::__project!(&bytes.$f); + zerocopy::TryFromBytes::is_bit_valid(f) + })* + $(&& { + // SAFETY: We just validated that all of the struct's fields + // are valid, which means that the struct itself is valid. + // That is the only precondition of `assume_valid_ref`. + let slf = unsafe { bytes.assume_valid_ref() }; + // TODO: What about interior mutability? One approach would + // be to have the validation method operate on a + // `#[repr(transparent)]` `Freeze` container that implements + // `Projectable`. If we eventually get a `Freeze` or + // `NoCell` trait, that container could implement `Deref` + // for types which don't contain any cells. + slf.$validation_method() + })? + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index a0246ccc70..324fa5afb9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -157,6 +157,8 @@ use core::{ ptr, slice, }; +use project::Projectable; + #[cfg(feature = "alloc")] extern crate alloc; #[cfg(feature = "alloc")] @@ -174,6 +176,120 @@ mod zerocopy { pub(crate) use crate::*; } +/// Documents multiple unsafe blocks with a single safety comment. +/// +/// Invoked as: +/// +/// ```rust,ignore +/// safety_comment! { +/// // Non-doc comments come first. +/// /// SAFETY: +/// /// Safety comment starts on its own line. +/// macro_1!(args); +/// macro_2! { args }; +/// /// SAFETY: +/// /// Subsequent safety comments are allowed but not required. +/// macro_3! { args }; +/// } +/// ``` +/// +/// The macro invocations are emitted, each decorated with the following +/// attribute: `#[allow(clippy::undocumented_unsafe_blocks)]`. +macro_rules! safety_comment { + (#[doc = r" SAFETY:"] $(#[doc = $_doc:literal])* $($macro:ident!$args:tt; $(#[doc = r" SAFETY:"] $(#[doc = $__doc:literal])*)?)*) => { + #[allow(clippy::undocumented_unsafe_blocks)] + const _: () = { $($macro!$args;)* }; + } +} + +/// Unsafely implements trait(s) for a type. +/// +/// # Safety +/// +/// The trait impl must be sound. +/// +/// When implementing `TryFromBytes`: +/// - If no `is_bit_valid` impl is provided, then it must be valid for +/// `is_bit_valid` to unconditionally return `true`. +/// - If an `is_bit_valid` impl is provided, then: +/// - It must be sound to transmute `&MaybeValid<$ty>` into `&$repr`. +/// - The impl of `is_bit_valid` must satisfy `TryFromByte`'s safety +/// requirements. +macro_rules! unsafe_impl { + // Implement `$trait` for `$ty` with no bounds. + ($ty:ty: $trait:ident $(; |$candidate:ident: &$repr:ty| $is_bit_valid:expr)?) => { + unsafe impl $trait for $ty { + unsafe_impl!(@method $trait $(; |$candidate: &$repr| $is_bit_valid)?); + } + }; + // Implement all `$traits` for `$ty` with no bounds. + ($ty:ty: $($traits:ident),*) => { + $( unsafe_impl!($ty: $traits); )* + }; + // For all `$tyvar` with no bounds, implement `$trait` for `$ty`. + ($tyvar:ident => $trait:ident for $ty:ty $(; |$candidate:ident: &$repr:ty| $is_bit_valid:expr)?) => { + unsafe impl<$tyvar> $trait for $ty { + unsafe_impl!(@method $trait $(; |$candidate: &$repr| $is_bit_valid)?); + } + }; + // For all `$tyvar: ?Sized` with no bounds, implement `$trait` for `$ty`. + ($tyvar:ident: ?Sized => $trait:ident for $ty:ty $(; |$candidate:ident: &$repr:ty| $is_bit_valid:expr)?) => { + unsafe impl<$tyvar: ?Sized> $trait for $ty { + unsafe_impl!(@method $trait $(; |$candidate: &$repr| $is_bit_valid)?); + } + }; + // For all `$tyvar: $bound`, implement `$trait` for `$ty`. + ($tyvar:ident: $bound:path => $trait:ident for $ty:ty $(; |$candidate:ident: &$repr:ty| $is_bit_valid:expr)?) => { + unsafe impl<$tyvar: $bound> $trait for $ty { + unsafe_impl!(@method $trait $(; |$candidate: &$repr| $is_bit_valid)?); + } + }; + // For all `$tyvar: $bound + ?Sized`, implement `$trait` for `$ty`. + ($tyvar:ident: ?Sized + $bound:path => $trait:ident for $ty:ty $(; |$candidate:ident: &$repr:ty| $is_bit_valid:expr)?) => { + unsafe impl<$tyvar: ?Sized + $bound> $trait for $ty { + unsafe_impl!(@method $trait $(; |$candidate: &$repr| $is_bit_valid)?); + } + }; + // For all `$tyvar: $bound` and for all `const $constvar: $constty`, + // implement `$trait` for `$ty`. + ($tyvar:ident: $bound:path, const $constvar:ident: $constty:ty => $trait:ident for $ty:ty $(; |$candidate:ident: &$repr:ty| $is_bit_valid:expr)?) => { + unsafe impl<$tyvar: $bound, const $constvar: $constty> $trait for $ty { + unsafe_impl!(@method $trait $(; |$candidate: &$repr| $is_bit_valid)?); + } + }; + + (@method TryFromBytes ; |$candidate:ident: &$repr:ty| $is_bit_valid:expr) => { + fn is_bit_valid(candidate: &MaybeValid) -> bool { + let $candidate = unsafe { &*(candidate as *const MaybeValid as *const $repr) }; + $is_bit_valid + } + }; + (@method TryFromBytes) => { fn is_bit_valid(_: &MaybeValid) -> bool { true } }; + (@method $trait:ident) => { + fn only_derive_is_allowed_to_implement_this_trait() {} + }; + (@method $trait:ident; |$_candidate:ident: &$_repr:ty| $_is_bit_valid:expr) => { + compile_error!("Can't provide `is_bit_valid` impl for trait other than `TryFromBytes`"); + }; +} + +/// Uses `align_of` to confirm that a type or set of types have alignment 1. +/// +/// Note that `align_of` requires `T: Sized`, so this macro doesn't work for +/// unsized types. +macro_rules! assert_unaligned { + ($ty:ty) => { + // We only compile this assertion under `cfg(test)` to avoid taking an + // extra non-dev dependency (and making this crate more expensive to + // compile for our dependents). + #[cfg(test)] + static_assertions::const_assert_eq!(core::mem::align_of::<$ty>(), 1); + }; + ($($ty:ty),*) => { + $(assert_unaligned!($ty);)* + }; +} + /// Types for which a sequence of bytes all set to zero represents a valid /// instance of the type. /// @@ -499,6 +615,346 @@ pub unsafe trait FromBytes: FromZeroes { } } +/// TODO +/// +/// # Safety +/// +/// `AsMaybeUninit` must only be implemented for types which are `Sized` or +/// whose last field is a slice whose element type is `Sized` (this includes +/// slice types themselves; in a slice type, the "last field" simply refers to +/// the slice itself). +pub unsafe trait AsMaybeUninit { + /// TODO + /// + /// # Safety + /// + /// For `T: AsMaybeUninit`, the following must hold: + /// - Given `m: T::MaybeUninit`, it is sound to write uninitialized bytes at + /// every byte offset in `m` (this description avoids the "what lengths + /// are valid" problem) + /// - `T` and `T::MaybeUninit` have the same alignment requirement (can't + /// use `align_of` to describe this because it requires that its argument + /// is sized) + /// - `T` and `T::MaybeUninit` are either both `Sized` or both `!Sized` + /// - If they are `Sized`, `size_of::() == size_of::()` + /// - If they are `!Sized`, then they are both DSTs with a trailing slice. + /// Given `t: &T` and `m: &T::MaybeUninit` with the same number of + /// elements in their trailing slices, `size_of_val(t) == size_of_val(m)`. + type MaybeUninit: ?Sized + FromBytes; +} + +// SAFETY: TODO +unsafe impl AsMaybeUninit for T { + type MaybeUninit = MaybeUninit; +} + +// SAFETY: TODO +unsafe impl AsMaybeUninit for [T] { + type MaybeUninit = [MaybeUninit]; +} + +// SAFETY: TODO +unsafe impl AsMaybeUninit for str { + type MaybeUninit = <[u8] as AsMaybeUninit>::MaybeUninit; +} + +/// A value which might or might not constitute a valid instance of `T`. +/// +/// `MaybeValid` has the same layout (size and alignment) and field offsets +/// as `T`. However, it may contain any bit pattern with a few restrictions: +/// Given `m: MaybeValid` and a byte offset, `b` in the range `[0, +/// size_of::>())`: +/// - If, in all valid instances `t: T`, the byte at offset `b` in `t` is +/// initialized, then the byte at offset `b` within `m` is guaranteed to be +/// initialized. +/// - Let `s` be the sequence of bytes of length `b` in the offset range `[0, +/// b)` in `m`. Let `TT` be the subset of valid instances of `T` which contain +/// this sequence in the offset range `[0, b)`. If, for all instances of `t: +/// T` in `TT`, the byte at offset `b` in `t` is initialized, then the byte at +/// offset `b` in `m` is guaranteed to be initialized. +/// +/// Pragmatically, this means that if `m` is guaranteed to contain an enum +/// type at a particular offset, and the enum discriminant stored in `m` +/// corresponds to a valid variant of that enum type, then it is guaranteed +/// that the appropriate bytes of `m` are initialized as defined by that +/// variant's layout (although note that the variant's layout may contain +/// another enum type, in which case the same rules apply depending on the +/// state of its discriminant, and so on recursively). +/// +/// # Safety +/// +/// TODO (make sure to mention enum layout) +#[repr(transparent)] +pub struct MaybeValid { + inner: T::MaybeUninit, +} + +safety_comment! { + /// SAFETY: + /// TODO + unsafe_impl!(T => TryFromBytes for MaybeValid); + unsafe_impl!(T => TryFromBytes for MaybeValid<[T]>); + unsafe_impl!(T => FromZeroes for MaybeValid); + unsafe_impl!(T => FromZeroes for MaybeValid<[T]>); + unsafe_impl!(T => FromBytes for MaybeValid); + unsafe_impl!(T => FromBytes for MaybeValid<[T]>); + unsafe_impl!(T: AsBytes => AsBytes for MaybeValid); + unsafe_impl!(T: AsBytes => AsBytes for MaybeValid<[T]>); + unsafe_impl!(T: Unaligned => Unaligned for MaybeValid); + unsafe_impl!(T: Unaligned => Unaligned for MaybeValid<[T]>); +} + +// SAFETY: TODO +unsafe impl AsMaybeUninit for MaybeValid<[T]> { + type MaybeUninit = [MaybeUninit]; +} + +impl Default for MaybeValid { + fn default() -> MaybeValid { + MaybeValid { inner: MaybeUninit::uninit() } + } +} + +impl MaybeValid { + /// Converts this `MaybeValid` to a `T`. + /// + /// # Safety + /// + /// `self` must contain a valid `T`. + pub const unsafe fn assume_valid(self) -> T { + // SAFETY: The caller has promised that `self` contains a valid `T`. + // Since `T: Sized`, we know that `T::MaybeUninit = MaybeUninit`. + // Since `MaybeValid` is `repr(transparent)`, its layout is identical to + // the contained `MaybeUninit`. Thus, the promise that `self` contains a + // valid `T` implies that the contained `MaybeUninit` contains a valid + // `T`. + unsafe { self.inner.assume_init() } + } + + /// Converts this `&MaybeValid` to a `&T`. + /// + /// # Safety + /// + /// `self` must contain a valid `T`. + pub const unsafe fn assume_valid_ref(&self) -> &T { + // SAFETY: The caller has promised that `self` contains a valid `T`. + // Since `T: Sized`, we know that `T::MaybeUninit = MaybeUninit`. + // Since `MaybeValid` is `repr(transparent)`, its layout is identical to + // the contained `MaybeUninit`. Thus, the promise that `self` contains a + // valid `T` implies that the contained `MaybeUninit` contains a valid + // `T`. + unsafe { self.inner.assume_init_ref() } + } + + /// Converts this `&mut MaybeValid` to a `&mut T`. + /// + /// # Safety + /// + /// `self` must contain a valid `T`. + pub unsafe fn assume_valid_mut(&mut self) -> &mut T { + // SAFETY: The caller has promised that `self` contains a valid `T`. + // Since `T: Sized`, we know that `T::MaybeUninit = MaybeUninit`. + // Since `MaybeValid` is `repr(transparent)`, its layout is identical to + // the contained `MaybeUninit`. Thus, the promise that `self` contains a + // valid `T` implies that the contained `MaybeUninit` contains a valid + // `T`. + unsafe { self.inner.assume_init_mut() } + } +} + +impl MaybeValid<[T]> { + /// Converts a `MaybeValid<[T]>` to a `[MaybeValid]`. + /// + /// `MaybeValid` has the same layout as `T`, so these layouts are + /// equivalent. + pub const fn as_slice_of_maybe_valids(&self) -> &[MaybeValid] { + let inner: &[::MaybeUninit] = &self.inner; + let inner_ptr: *const [::MaybeUninit] = inner; + let ret_ptr = inner_ptr as *const [MaybeValid]; + // SAFETY: Since `inner` is a `&[T::MaybeUninit]`, and `MaybeValid` + // is a `repr(transparent)` struct around `T::MaybeUninit`, `inner` has + // the same layout as `&[MaybeValid]`. + unsafe { &*ret_ptr } + } +} + +impl MaybeValid<[T; N]> { + /// Converts a `MaybeValid<[T; N]>` to a `MaybeValid<[T]>`. + pub const fn as_slice(&self) -> &MaybeValid<[T]> { + let base: *const MaybeValid<[T; N]> = self; + let slice_of_t: *const [T] = ptr::slice_from_raw_parts(base.cast::(), N); + let mv_of_slice: *const MaybeValid<[T]> = slice_of_t as _; + // SAFETY: TODO + unsafe { &*mv_of_slice } + } +} + +// SAFETY: TODO +unsafe impl Projectable> for MaybeValid { + type Inner = T; +} + +impl Debug for MaybeValid { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.pad(core::any::type_name::()) + } +} + +/// Types whose validity can be checked at runtime, allowing them to be +/// conditionally converted from byte slices. +/// +/// WARNING: Do not implement this trait yourself! Instead, use +/// `#[derive(TryFromBytes)]`. +/// +/// `TryFromBytes` types can safely be deserialized from an untrusted sequence +/// of bytes by performing a runtime check that the byte sequence contains a +/// valid instance of `Self`. +/// +/// `TryFromBytes` is ignorant of byte order. For byte order-aware types, see +/// the [`byteorder`] module. +/// +/// # Safety +/// +/// Unsafe code may assume that [`is_bit_valid`] is correct, that, if +/// `is_bit_valid(candidate)` returns true, `candidate` contains a valid `Self`, +/// and that it is sound to treat `candidate` as a `&Self`. +/// +/// Implementations of `TryFromBytes` must ensure that [`is_bit_valid`] +/// satisfies its documented safety invariants. +// TODO(#5): Describe in the documentation that `TryFromBytes` is compositional? +// Describe anything about the `is_bit_valid` impls that the custom derive +// emits? +pub unsafe trait TryFromBytes: AsMaybeUninit { + /// Does this [`MaybeValid`] contain a valid instance of `Self`? + /// + /// # Safety + /// + /// Unsafe code may assume that, if `is_bit_valid(candidate)` returns true, + /// `candidate` contains a valid `Self`, and that it is sound to treat + /// `candidate` as a `&Self`. + fn is_bit_valid(candidate: &MaybeValid) -> bool; + + /// Attempts to interpret a byte slice as a `Self`. + /// + /// `try_from_ref` validates that `bytes` contains a valid `Self` as defined + /// by [`is_bit_valid`]. If it does, then `bytes` is reinterpreted as a + /// `Self`. + /// + /// [`is_bit_valid`]: TryFromBytes::is_bit_valid + // TODO(#251): In a future in which we distinguish between `FromBytes` and + // `RefFromBytes`, this requires `where Self: RefFromBytes` to disallow + // interior mutability. + fn try_from_ref(bytes: &[u8]) -> Option<&Self> + where + // TODO(#5): Support unsized types. + Self: Sized, + { + // TODO(https://github.com/rust-lang/rust/issues/115080): Inline this + // function once #115080 is resolved. + #[inline(always)] + fn try_read_from_inner) -> bool>( + bytes: &[u8], + is_bit_valid: F, + ) -> Option<&T> { + let maybe_valid = Ref::<_, MaybeValid>::new(bytes)?.into_ref(); + if is_bit_valid(maybe_valid) { + // SAFETY: `is_bit_valid` promises that it only returns true if + // its argument contains a valid `T`. This is exactly the safety + // precondition of `MaybeValid::assume_valid_ref`. + Some(unsafe { maybe_valid.assume_valid_ref() }) + } else { + None + } + } + + try_read_from_inner(bytes, Self::is_bit_valid) + } + + /// Attempts to interpret a mutable byte slice as a `Self`. + /// + /// `try_from_mut` validates that `bytes` contains a valid `Self` as defined + /// by [`is_bit_valid`]. If it does, then `bytes` is reinterpreted as a + /// `Self`. + /// + /// [`is_bit_valid`]: TryFromBytes::is_bit_valid + // TODO(#251): In a future in which we distinguish between `FromBytes` and + // `RefFromBytes`, this requires `where Self: RefFromBytes` to disallow + // interior mutability. + fn try_from_mut(bytes: &mut [u8]) -> Option<&mut Self> + where + // TODO(#5): Support unsized types. + Self: AsBytes + Sized, + { + // TODO(https://github.com/rust-lang/rust/issues/115080): Inline this + // function once #115080 is resolved. + #[inline(always)] + fn try_read_from_mut_inner) -> bool>( + bytes: &mut [u8], + is_bit_valid: F, + ) -> Option<&mut T> { + let maybe_valid = Ref::<_, MaybeValid>::new(bytes)?.into_mut(); + if is_bit_valid(maybe_valid) { + // SAFETY: `is_bit_valid` promises that it only returns true if + // its argument contains a valid `T`. This is exactly the safety + // precondition of `MaybeValid::assume_valid_mut`. + Some(unsafe { maybe_valid.assume_valid_mut() }) + } else { + None + } + } + + try_read_from_mut_inner(bytes, Self::is_bit_valid) + } + + /// Attempts to read a `Self` from a byte slice. + /// + /// `try_read_from` validates that `bytes` contains a valid `Self` as + /// defined by [`is_bit_valid`]. If it does, then that `Self` is copied and + /// returned by-value. + /// + /// [`is_bit_valid`]: TryFromBytes::is_bit_valid + // TODO(#251): In a future in which we distinguish between `FromBytes` and + // `RefFromBytes`, this requires `where Self: RefFromBytes` to disallow + // interior mutability. + fn try_read_from(bytes: &[u8]) -> Option + where + Self: Sized, + { + // TODO(https://github.com/rust-lang/rust/issues/115080): Inline this + // function once #115080 is resolved. + #[inline(always)] + fn try_read_from_inner) -> bool>( + bytes: &[u8], + is_bit_valid: F, + ) -> Option { + // A note on performance: We unconditionally read `size_of::()` + // bytes into the local stack frame before validation. This has + // advantages and disadvantages: + // - It allows `MaybeValid` to be aligned to `T`, and thus allows + // `is_bit_valid` to operate on an aligned value. + // - It requires us to perform the copy even if validation fails. + // + // The authors believe that this is a worthwhile tradeoff. Allowing + // `is_bit_valid` to operate on an aligned value can make the + // generated machine code significantly smaller and faster. On the + // other hand, we expect the vast majority of calls to + // `try_read_from` to succeed, and in these cases, the copy will not + // be wasted. + let maybe_valid = MaybeValid::::read_from(bytes)?; + if is_bit_valid(&maybe_valid) { + // SAFETY: `is_bit_valid` promises that it only returns true if + // its argument contains a valid `T`. This is exactly the safety + // precondition of `MaybeValid::assume_valid`. + Some(unsafe { maybe_valid.assume_valid() }) + } else { + None + } + } + + try_read_from_inner(bytes, Self::is_bit_valid) + } +} + /// Types which are safe to treat as an immutable byte slice. /// /// WARNING: Do not implement this trait yourself! Instead, use @@ -696,98 +1152,24 @@ pub unsafe trait Unaligned { Self: Sized; } -/// Documents multiple unsafe blocks with a single safety comment. -/// -/// Invoked as: -/// -/// ```rust,ignore -/// safety_comment! { -/// // Non-doc comments come first. -/// /// SAFETY: -/// /// Safety comment starts on its own line. -/// macro_1!(args); -/// macro_2! { args }; -/// } -/// ``` -/// -/// The macro invocations are emitted, each decorated with the following -/// attribute: `#[allow(clippy::undocumented_unsafe_blocks)]`. -macro_rules! safety_comment { - (#[doc = r" SAFETY:"] $(#[doc = $_doc:literal])* $($macro:ident!$args:tt;)*) => { - #[allow(clippy::undocumented_unsafe_blocks)] - const _: () = { $($macro!$args;)* }; - } -} - -/// Unsafely implements trait(s) for a type. -macro_rules! unsafe_impl { - // Implement `$trait` for `$ty` with no bounds. - ($ty:ty: $trait:ty) => { - unsafe impl $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} } - }; - // Implement all `$traits` for `$ty` with no bounds. - ($ty:ty: $($traits:ty),*) => { - $( unsafe_impl!($ty: $traits); )* - }; - // For all `$tyvar` with no bounds, implement `$trait` for `$ty`. - ($tyvar:ident => $trait:ident for $ty:ty) => { - unsafe impl<$tyvar> $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} } - }; - // For all `$tyvar: ?Sized` with no bounds, implement `$trait` for `$ty`. - ($tyvar:ident: ?Sized => $trait:ident for $ty:ty) => { - unsafe impl<$tyvar: ?Sized> $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} } - }; - // For all `$tyvar: $bound`, implement `$trait` for `$ty`. - ($tyvar:ident: $bound:path => $trait:ident for $ty:ty) => { - unsafe impl<$tyvar: $bound> $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} } - }; - // For all `$tyvar: $bound + ?Sized`, implement `$trait` for `$ty`. - ($tyvar:ident: ?Sized + $bound:path => $trait:ident for $ty:ty) => { - unsafe impl<$tyvar: ?Sized + $bound> $trait for $ty { fn only_derive_is_allowed_to_implement_this_trait() {} } - }; - // For all `$tyvar: $bound` and for all `const $constvar: $constty`, - // implement `$trait` for `$ty`. - ($tyvar:ident: $bound:path, const $constvar:ident: $constty:ty => $trait:ident for $ty:ty) => { - unsafe impl<$tyvar: $bound, const $constvar: $constty> $trait for $ty { - fn only_derive_is_allowed_to_implement_this_trait() {} - } - }; -} - -/// Uses `align_of` to confirm that a type or set of types have alignment 1. -/// -/// Note that `align_of` requires `T: Sized`, so this macro doesn't work for -/// unsized types. -macro_rules! assert_unaligned { - ($ty:ty) => { - // We only compile this assertion under `cfg(test)` to avoid taking an - // extra non-dev dependency (and making this crate more expensive to - // compile for our dependents). - #[cfg(test)] - static_assertions::const_assert_eq!(core::mem::align_of::<$ty>(), 1); - }; - ($($ty:ty),*) => { - $(assert_unaligned!($ty);)* - }; -} - safety_comment! { /// SAFETY: /// Per the reference [1], "the unit tuple (`()`) ... is guaranteed as a /// zero-sized type to have a size of 0 and an alignment of 1." - /// - `FromZeroes`, `FromBytes`: There is only one possible sequence of 0 - /// bytes, and `()` is inhabited. + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`: There + /// is only one possible sequence of 0 bytes, and `()` is inhabited. /// - `AsBytes`: Since `()` has size 0, it contains no padding bytes. /// - `Unaligned`: `()` has alignment 1. /// /// [1] https://doc.rust-lang.org/reference/type-layout.html#tuple-layout - unsafe_impl!((): FromZeroes, FromBytes, AsBytes, Unaligned); + unsafe_impl!((): TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); assert_unaligned!(()); } safety_comment! { /// SAFETY: - /// - `FromZeroes`, `FromBytes`: all bit patterns are valid for integers [1] + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`: all bit + /// patterns are valid for integers [1] /// - `AsBytes`: integers have no padding bytes [1] /// - `Unaligned` (`u8` and `i8` only): The reference [2] specifies the size /// of `u8` and `i8` as 1 byte. We also know that: @@ -799,26 +1181,26 @@ safety_comment! { /// [1] TODO(https://github.com/rust-lang/reference/issues/1291): Once the /// reference explicitly guarantees these properties, cite it. /// [2] https://doc.rust-lang.org/reference/type-layout.html#primitive-data-layout - unsafe_impl!(u8: FromZeroes, FromBytes, AsBytes, Unaligned); - unsafe_impl!(i8: FromZeroes, FromBytes, AsBytes, Unaligned); + unsafe_impl!(u8: TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + unsafe_impl!(i8: TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); assert_unaligned!(u8, i8); - unsafe_impl!(u16: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(i16: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(u32: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(i32: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(u64: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(i64: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(u128: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(i128: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(usize: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(isize: FromZeroes, FromBytes, AsBytes); + unsafe_impl!(u16: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(i16: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(u32: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(i32: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(u64: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(i64: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(u128: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(i128: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(usize: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(isize: TryFromBytes, FromZeroes, FromBytes, AsBytes); } safety_comment! { /// SAFETY: - /// - `FromZeroes`, `FromBytes`: the `{f32,f64}::from_bits` constructors' - /// documentation [1,2] states that they are currently equivalent to - /// `transmute`. [3] + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`: the + /// `{f32,f64}::from_bits` constructors' documentation [1, 2] states that + /// they are currently equivalent to `transmute`. [3] /// - `AsBytes`: the `{f32,f64}::to_bits` methods' documentation [4,5] /// states that they are currently equivalent to `transmute`. [3] /// @@ -830,8 +1212,8 @@ safety_comment! { /// reference explicitly guarantees these properties, cite it. /// [4] https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.to_bits /// [5] https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.to_bits - unsafe_impl!(f32: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(f64: FromZeroes, FromBytes, AsBytes); + unsafe_impl!(f32: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(f64: TryFromBytes, FromZeroes, FromBytes, AsBytes); } safety_comment! { @@ -847,6 +1229,9 @@ safety_comment! { /// [1] https://doc.rust-lang.org/reference/types/boolean.html unsafe_impl!(bool: FromZeroes, AsBytes, Unaligned); assert_unaligned!(bool); + /// SAFETY: + /// TODO + unsafe_impl!(bool: TryFromBytes; |byte: &u8| *byte < 2); } safety_comment! { /// SAFETY: @@ -858,6 +1243,12 @@ safety_comment! { /// /// [1] https://doc.rust-lang.org/reference/types/textual.html unsafe_impl!(char: FromZeroes, AsBytes); + /// SAFETY: + /// TODO + unsafe_impl!(char: TryFromBytes; |bytes: &[u8; 4]| { + let c = u32::from_ne_bytes(*bytes); + char::from_u32(c).is_some() + }); } safety_comment! { /// SAFETY: @@ -870,6 +1261,11 @@ safety_comment! { /// /// [1] https://doc.rust-lang.org/reference/type-layout.html#str-layout unsafe_impl!(str: FromZeroes, AsBytes, Unaligned); + /// SAFETY: + /// TODO + unsafe_impl!(str: TryFromBytes; |bytes: &[u8]| { + core::str::from_utf8(bytes).is_ok() + }); } safety_comment! { @@ -907,12 +1303,34 @@ safety_comment! { unsafe_impl!(NonZeroI128: AsBytes); unsafe_impl!(NonZeroUsize: AsBytes); unsafe_impl!(NonZeroIsize: AsBytes); + + /// SAFETY: + /// `NonZeroXxx` has the same layout as `Xxx`. Also, every byte of + /// `NonZeroXxx` is required to be initialized, so it is guaranteed that + /// every byte of `MaybeValid` must also be initialized. Thus, + /// it is sound to transmute a `&MaybeValid` to a `&Xxx`. + /// Finally, `NonZeroXxx`'s only validity constraint is that it is non-zero, + /// which all of these closures ensure. Thus, these closures are sound + /// implementations of `TryFromBytes::is_bit_valid`. + unsafe_impl!(NonZeroU8: TryFromBytes; |n: &u8| *n != 0); + unsafe_impl!(NonZeroI8: TryFromBytes; |n: &i8| *n != 0); + unsafe_impl!(NonZeroU16: TryFromBytes; |n: &u16| *n != 0); + unsafe_impl!(NonZeroI16: TryFromBytes; |n: &i16| *n != 0); + unsafe_impl!(NonZeroU32: TryFromBytes; |n: &u32| *n != 0); + unsafe_impl!(NonZeroI32: TryFromBytes; |n: &i32| *n != 0); + unsafe_impl!(NonZeroU64: TryFromBytes; |n: &u64| *n != 0); + unsafe_impl!(NonZeroI64: TryFromBytes; |n: &i64| *n != 0); + unsafe_impl!(NonZeroU128: TryFromBytes; |n: &u128| *n != 0); + unsafe_impl!(NonZeroI128: TryFromBytes; |n: &i128| *n != 0); + unsafe_impl!(NonZeroUsize: TryFromBytes; |n: &usize| *n != 0); + unsafe_impl!(NonZeroIsize: TryFromBytes; |n: &isize| *n != 0); } safety_comment! { /// SAFETY: - /// - `FromZeroes`, `FromBytes`, `AsBytes`: The Rust compiler reuses `0` - /// value to represent `None`, so `size_of::>() == - /// size_of::()`; see `NonZeroXxx` documentation. + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`, + /// `AsBytes`: The Rust compiler reuses `0` value to represent `None`, so + /// `size_of::>() == size_of::()`; see + /// `NonZeroXxx` documentation. /// - `Unaligned`: `NonZeroU8` and `NonZeroI8` document that /// `Option` and `Option` both have size 1. [1] [2] /// This is worded in a way that makes it unclear whether it's meant as a @@ -925,32 +1343,34 @@ safety_comment! { /// /// TODO(https://github.com/rust-lang/rust/pull/104082): Cite documentation /// for layout guarantees. - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes, Unaligned); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes, Unaligned); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); assert_unaligned!(Option, Option); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes); - unsafe_impl!(Option: FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes); + unsafe_impl!(Option: TryFromBytes, FromZeroes, FromBytes, AsBytes); } safety_comment! { /// SAFETY: /// For all `T`, `PhantomData` has size 0 and alignment 1. [1] - /// - `FromZeroes`, `FromBytes`: There is only one possible sequence of 0 - /// bytes, and `PhantomData` is inhabited. + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`: There + /// is only one possible sequence of 0 bytes, and `PhantomData` is + /// inhabited. /// - `AsBytes`: Since `PhantomData` has size 0, it contains no padding /// bytes. /// - `Unaligned`: Per the preceding reference, `PhantomData` has alignment /// 1. /// /// [1] https://doc.rust-lang.org/std/marker/struct.PhantomData.html#layout-1 + unsafe_impl!(T: ?Sized => TryFromBytes for PhantomData); unsafe_impl!(T: ?Sized => FromZeroes for PhantomData); unsafe_impl!(T: ?Sized => FromBytes for PhantomData); unsafe_impl!(T: ?Sized => AsBytes for PhantomData); @@ -966,6 +1386,7 @@ safety_comment! { /// /// [1] https://doc.rust-lang.org/nightly/core/num/struct.Wrapping.html#layout-1 /// [2] https://doc.rust-lang.org/nomicon/other-reprs.html#reprtransparent + // TODO(#5): Implement `TryFromBytes` for `Wrapping`. unsafe_impl!(T: FromZeroes => FromZeroes for Wrapping); unsafe_impl!(T: FromBytes => FromBytes for Wrapping); unsafe_impl!(T: AsBytes => AsBytes for Wrapping); @@ -977,12 +1398,13 @@ safety_comment! { // since it may contain uninitialized bytes. // /// SAFETY: - /// - `FromZeroes`, `FromBytes`: `MaybeUninit` has no restrictions on its - /// contents. + /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`: + /// `MaybeUninit` has no restrictions on its contents. /// - `Unaligned`: `MaybeUninit` is guaranteed by its documentation [1] /// to have the same alignment as `T`. /// /// [1] https://doc.rust-lang.org/nightly/core/mem/union.MaybeUninit.html#layout-1 + unsafe_impl!(T => TryFromBytes for MaybeUninit); unsafe_impl!(T => FromZeroes for MaybeUninit); unsafe_impl!(T => FromBytes for MaybeUninit); unsafe_impl!(T: Unaligned => Unaligned for MaybeUninit); @@ -1005,6 +1427,7 @@ safety_comment! { /// code can only ever access a `ManuallyDrop` with all initialized bytes. /// - `Unaligned`: `ManuallyDrop` has the same layout (and thus alignment) /// as `T`, and `T: Unaligned` guarantees that that alignment is 1. + // TODO(#5): Implement `TryFromBytes` for `ManuallyDrop`. unsafe_impl!(T: ?Sized + FromZeroes => FromZeroes for ManuallyDrop); unsafe_impl!(T: ?Sized + FromBytes => FromBytes for ManuallyDrop); unsafe_impl!(T: ?Sized + AsBytes => AsBytes for ManuallyDrop); @@ -1030,15 +1453,34 @@ safety_comment! { /// (respectively). Furthermore, since an array/slice has "the same /// alignment of `T`", `[T]` and `[T; N]` are `Unaligned` if `T` is. /// + /// Finally, because of this layout equivalence, an instance of `[T]` or + /// `[T; N]` is valid if each `T` is valid. Thus, it is sound to implement + /// `TryFromBytes::is_bit_valid` by calling `is_bit_valid` on each element. + /// /// Note that we don't `assert_unaligned!` for slice types because /// `assert_unaligned!` uses `align_of`, which only works for `Sized` types. /// /// [1] https://doc.rust-lang.org/reference/type-layout.html#array-layout + unsafe_impl!(T: TryFromBytes, const N: usize => TryFromBytes for [T; N]; |c: &MaybeValid<[T; N]>| { + <[T] as TryFromBytes>::is_bit_valid(c.as_slice()) + }); unsafe_impl!(T: FromZeroes, const N: usize => FromZeroes for [T; N]); unsafe_impl!(T: FromBytes, const N: usize => FromBytes for [T; N]); unsafe_impl!(T: AsBytes, const N: usize => AsBytes for [T; N]); unsafe_impl!(T: Unaligned, const N: usize => Unaligned for [T; N]); assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]); + unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; |c: &MaybeValid<[T]>| { + // TODO(https://github.com/rust-lang/rust/issues/115080): Inline this + // function once #115080 is resolved. + #[inline(always)] + fn is_bit_valid) -> bool>( + c: &MaybeValid<[T]>, + is_bit_valid: F, + ) -> bool { + c.as_slice_of_maybe_valids().iter().all(is_bit_valid) + } + is_bit_valid(c, T::is_bit_valid) + }); unsafe_impl!(T: FromZeroes => FromZeroes for [T]); unsafe_impl!(T: FromBytes => FromBytes for [T]); unsafe_impl!(T: AsBytes => AsBytes for [T]); @@ -1129,7 +1571,7 @@ mod simd { safety_comment! { /// SAFETY: /// See comment on module definition for justification. - $( unsafe_impl!($typ: FromZeroes, FromBytes, AsBytes); )* + $( unsafe_impl!($typ: TryFromBytes, FromZeroes, FromBytes, AsBytes); )* } } };