summaryrefslogtreecommitdiffstats
path: root/rust/zerocopy/src/macros.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/zerocopy/src/macros.rs')
-rw-r--r--rust/zerocopy/src/macros.rs1825
1 files changed, 1825 insertions, 0 deletions
diff --git a/rust/zerocopy/src/macros.rs b/rust/zerocopy/src/macros.rs
new file mode 100644
index 000000000..ec67c03a4
--- /dev/null
+++ b/rust/zerocopy/src/macros.rs
@@ -0,0 +1,1825 @@
+// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
+//
+// Copyright 2024 The Fuchsia Authors
+//
+// Licensed under the 2-Clause BSD License <LICENSE-BSD or
+// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0
+// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
+// This file may not be copied, modified, or distributed except according to
+// those terms.
+
+/// Safely transmutes a value of one type to a value of another type of the same
+/// size.
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// const fn transmute<Src, Dst>(src: Src) -> Dst
+/// where
+/// Src: IntoBytes,
+/// Dst: FromBytes,
+/// size_of::<Src>() == size_of::<Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// However, unlike a function, this macro can only be invoked when the types of
+/// `Src` and `Dst` are completely concrete. The types `Src` and `Dst` are
+/// inferred from the calling context; they cannot be explicitly specified in
+/// the macro invocation.
+///
+/// Note that the `Src` produced by the expression `$e` will *not* be dropped.
+/// Semantically, its bits will be copied into a new value of type `Dst`, the
+/// original `Src` will be forgotten, and the value of type `Dst` will be
+/// returned.
+///
+/// # `#![allow(shrink)]`
+///
+/// If `#![allow(shrink)]` is provided, `transmute!` additionally supports
+/// transmutations that shrink the size of the value; e.g.:
+///
+/// ```
+/// # use zerocopy::transmute;
+/// let u: u32 = transmute!(#![allow(shrink)] 0u64);
+/// assert_eq!(u, 0u32);
+/// ```
+///
+/// # Examples
+///
+/// ```
+/// # use zerocopy::transmute;
+/// let one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
+///
+/// let two_dimensional: [[u8; 4]; 2] = transmute!(one_dimensional);
+///
+/// assert_eq!(two_dimensional, [[0, 1, 2, 3], [4, 5, 6, 7]]);
+/// ```
+///
+/// # Use in `const` contexts
+///
+/// This macro can be invoked in `const` contexts.
+///
+#[doc = codegen_section!(
+ header = "h2",
+ bench = "transmute",
+ format = "coco_static_size",
+)]
+#[macro_export]
+macro_rules! transmute {
+ // NOTE: This must be a macro (rather than a function with trait bounds)
+ // because there's no way, in a generic context, to enforce that two types
+ // have the same size. `core::mem::transmute` uses compiler magic to enforce
+ // this so long as the types are concrete.
+ (#![allow(shrink)] $e:expr) => {{
+ let mut e = $e;
+ if false {
+ // This branch, though never taken, ensures that the type of `e` is
+ // `IntoBytes` and that the type of the outer macro invocation
+ // expression is `FromBytes`.
+
+ fn transmute<Src, Dst>(src: Src) -> Dst
+ where
+ Src: $crate::IntoBytes,
+ Dst: $crate::FromBytes,
+ {
+ let _ = src;
+ loop {}
+ }
+ loop {}
+ #[allow(unreachable_code)]
+ transmute(e)
+ } else {
+ use $crate::util::macro_util::core_reexport::mem::ManuallyDrop;
+
+ // NOTE: `repr(packed)` is important! It ensures that the size of
+ // `Transmute` won't be rounded up to accommodate `Src`'s or `Dst`'s
+ // alignment, which would break the size comparison logic below.
+ //
+ // As an example of why this is problematic, consider `Src = [u8;
+ // 5]`, `Dst = u32`. The total size of `Transmute<Src, Dst>` would
+ // be 8, and so we would reject a `[u8; 5]` to `u32` transmute as
+ // being size-increasing, which it isn't.
+ #[repr(C, packed)]
+ union Transmute<Src, Dst> {
+ src: ManuallyDrop<Src>,
+ dst: ManuallyDrop<Dst>,
+ }
+
+ // SAFETY: `Transmute` is a `repr(C)` union whose `src` field has
+ // type `ManuallyDrop<Src>`. Thus, the `src` field starts at byte
+ // offset 0 within `Transmute` [1]. `ManuallyDrop<T>` has the same
+ // layout and bit validity as `T`, so it is sound to transmute `Src`
+ // to `Transmute`.
+ //
+ // [1] https://doc.rust-lang.org/1.85.0/reference/type-layout.html#reprc-unions
+ //
+ // [2] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
+ //
+ // `ManuallyDrop<T>` is guaranteed to have the same layout and bit
+ // validity as `T`
+ let u: Transmute<_, _> = unsafe {
+ // Clippy: We can't annotate the types; this macro is designed
+ // to infer the types from the calling context.
+ #[allow(clippy::missing_transmute_annotations)]
+ $crate::util::macro_util::core_reexport::mem::transmute(e)
+ };
+
+ if false {
+ // SAFETY: This code is never executed.
+ e = ManuallyDrop::into_inner(unsafe { u.src });
+ // Suppress the `unused_assignments` lint on the previous line.
+ let _ = e;
+ loop {}
+ } else {
+ // SAFETY: Per the safety comment on `let u` above, the `dst`
+ // field in `Transmute` starts at byte offset 0, and has the
+ // same layout and bit validity as `Dst`.
+ //
+ // Transmuting `Src` to `Transmute<Src, Dst>` above using
+ // `core::mem::transmute` ensures that `size_of::<Src>() ==
+ // size_of::<Transmute<Src, Dst>>()`. A `#[repr(C, packed)]`
+ // union has the maximum size of all of its fields [1], so this
+ // is equivalent to `size_of::<Src>() >= size_of::<Dst>()`.
+ //
+ // The outer `if`'s `false` branch ensures that `Src: IntoBytes`
+ // and `Dst: FromBytes`. This, combined with the size bound,
+ // ensures that this transmute is sound.
+ //
+ // [1] Per https://doc.rust-lang.org/1.85.0/reference/type-layout.html#reprc-unions:
+ //
+ // The union will have a size of the maximum size of all of
+ // its fields rounded to its alignment
+ let dst = unsafe { u.dst };
+ $crate::util::macro_util::must_use(ManuallyDrop::into_inner(dst))
+ }
+ }
+ }};
+ ($e:expr) => {{
+ let e = $e;
+ if false {
+ // This branch, though never taken, ensures that the type of `e` is
+ // `IntoBytes` and that the type of the outer macro invocation
+ // expression is `FromBytes`.
+
+ fn transmute<Src, Dst>(src: Src) -> Dst
+ where
+ Src: $crate::IntoBytes,
+ Dst: $crate::FromBytes,
+ {
+ let _ = src;
+ loop {}
+ }
+ loop {}
+ #[allow(unreachable_code)]
+ transmute(e)
+ } else {
+ // SAFETY: `core::mem::transmute` ensures that the type of `e` and
+ // the type of this macro invocation expression have the same size.
+ // We know this transmute is safe thanks to the `IntoBytes` and
+ // `FromBytes` bounds enforced by the `false` branch.
+ let u = unsafe {
+ // Clippy: We can't annotate the types; this macro is designed
+ // to infer the types from the calling context.
+ #[allow(clippy::missing_transmute_annotations, unnecessary_transmutes)]
+ $crate::util::macro_util::core_reexport::mem::transmute(e)
+ };
+ $crate::util::macro_util::must_use(u)
+ }
+ }};
+}
+
+/// Safely transmutes a mutable or immutable reference of one type to an
+/// immutable reference of another type of the same size and compatible
+/// alignment.
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// fn transmute_ref<'src, 'dst, Src, Dst>(src: &'src Src) -> &'dst Dst
+/// where
+/// 'src: 'dst,
+/// Src: IntoBytes + Immutable + ?Sized,
+/// Dst: FromBytes + Immutable + ?Sized,
+/// align_of::<Src>() >= align_of::<Dst>(),
+/// size_compatible::<Src, Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// The types `Src` and `Dst` are inferred from the calling context; they cannot
+/// be explicitly specified in the macro invocation.
+///
+/// # Size compatibility
+///
+/// `transmute_ref!` supports transmuting between `Sized` types, between unsized
+/// (i.e., `?Sized`) types, and from a `Sized` type to an unsized type. It
+/// supports any transmutation that preserves the number of bytes of the
+/// referent, even if doing so requires updating the metadata stored in an
+/// unsized "fat" reference:
+///
+/// ```
+/// # use zerocopy::transmute_ref;
+/// # use core::mem::size_of_val; // Not in the prelude on our MSRV
+/// let src: &[[u8; 2]] = &[[0, 1], [2, 3]][..];
+/// let dst: &[u8] = transmute_ref!(src);
+///
+/// assert_eq!(src.len(), 2);
+/// assert_eq!(dst.len(), 4);
+/// assert_eq!(dst, [0, 1, 2, 3]);
+/// assert_eq!(size_of_val(src), size_of_val(dst));
+/// ```
+///
+/// # Errors
+///
+/// Violations of the alignment and size compatibility checks are detected
+/// *after* the compiler performs monomorphization. This has two important
+/// consequences.
+///
+/// First, it means that generic code will *never* fail these conditions:
+///
+/// ```
+/// # use zerocopy::{transmute_ref, FromBytes, IntoBytes, Immutable};
+/// fn transmute_ref<Src, Dst>(src: &Src) -> &Dst
+/// where
+/// Src: IntoBytes + Immutable,
+/// Dst: FromBytes + Immutable,
+/// {
+/// transmute_ref!(src)
+/// }
+/// ```
+///
+/// Instead, failures will only be detected once generic code is instantiated
+/// with concrete types:
+///
+/// ```compile_fail,E0080
+/// # use zerocopy::{transmute_ref, FromBytes, IntoBytes, Immutable};
+/// #
+/// # fn transmute_ref<Src, Dst>(src: &Src) -> &Dst
+/// # where
+/// # Src: IntoBytes + Immutable,
+/// # Dst: FromBytes + Immutable,
+/// # {
+/// # transmute_ref!(src)
+/// # }
+/// let src: &u16 = &0;
+/// let dst: &u8 = transmute_ref(src);
+/// ```
+///
+/// Second, the fact that violations are detected after monomorphization means
+/// that `cargo check` will usually not detect errors, even when types are
+/// concrete. Instead, `cargo build` must be used to detect such errors.
+///
+/// # Examples
+///
+/// Transmuting between `Sized` types:
+///
+/// ```
+/// # use zerocopy::transmute_ref;
+/// let one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
+///
+/// let two_dimensional: &[[u8; 4]; 2] = transmute_ref!(&one_dimensional);
+///
+/// assert_eq!(two_dimensional, &[[0, 1, 2, 3], [4, 5, 6, 7]]);
+/// ```
+///
+/// Transmuting between unsized types:
+///
+/// ```
+/// # use {zerocopy::*, zerocopy_derive::*};
+/// # type u16 = zerocopy::byteorder::native_endian::U16;
+/// # type u32 = zerocopy::byteorder::native_endian::U32;
+/// #[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
+/// #[repr(C)]
+/// struct SliceDst<T, U> {
+/// t: T,
+/// u: [U],
+/// }
+///
+/// type Src = SliceDst<u32, u16>;
+/// type Dst = SliceDst<u16, u8>;
+///
+/// let src = Src::ref_from_bytes(&[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
+/// let dst: &Dst = transmute_ref!(src);
+///
+/// assert_eq!(src.t.as_bytes(), [0, 1, 2, 3]);
+/// assert_eq!(src.u.len(), 2);
+/// assert_eq!(src.u.as_bytes(), [4, 5, 6, 7]);
+///
+/// assert_eq!(dst.t.as_bytes(), [0, 1]);
+/// assert_eq!(dst.u, [2, 3, 4, 5, 6, 7]);
+/// ```
+///
+/// # Use in `const` contexts
+///
+/// This macro can be invoked in `const` contexts only when `Src: Sized` and
+/// `Dst: Sized`.
+///
+#[doc = codegen_section!(
+ header = "h2",
+ bench = "transmute_ref",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ]
+)]
+#[macro_export]
+macro_rules! transmute_ref {
+ ($e:expr) => {{
+ // NOTE: This must be a macro (rather than a function with trait bounds)
+ // because there's no way, in a generic context, to enforce that two
+ // types have the same size or alignment.
+
+ // Ensure that the source type is a reference or a mutable reference
+ // (note that mutable references are implicitly reborrowed here).
+ let e: &_ = $e;
+
+ #[allow(unused, clippy::diverging_sub_expression)]
+ if false {
+ // This branch, though never taken, ensures that the type of `e` is
+ // `&T` where `T: IntoBytes + Immutable`, and that the type of this
+ // macro expression is `&U` where `U: FromBytes + Immutable`.
+
+ struct AssertSrcIsIntoBytes<'a, T: ?::core::marker::Sized + $crate::IntoBytes>(&'a T);
+ struct AssertSrcIsImmutable<'a, T: ?::core::marker::Sized + $crate::Immutable>(&'a T);
+ struct AssertDstIsFromBytes<'a, U: ?::core::marker::Sized + $crate::FromBytes>(&'a U);
+ struct AssertDstIsImmutable<'a, T: ?::core::marker::Sized + $crate::Immutable>(&'a T);
+
+ let _ = AssertSrcIsIntoBytes(e);
+ let _ = AssertSrcIsImmutable(e);
+
+ if true {
+ #[allow(unused, unreachable_code)]
+ let u = AssertDstIsFromBytes(loop {});
+ u.0
+ } else {
+ #[allow(unused, unreachable_code)]
+ let u = AssertDstIsImmutable(loop {});
+ u.0
+ }
+ } else {
+ use $crate::util::macro_util::TransmuteRefDst;
+ let t = $crate::util::macro_util::Wrap::new(e);
+
+ if false {
+ // This branch exists solely to force the compiler to infer the
+ // type of `Dst` *before* it attempts to resolve the method call
+ // to `transmute_ref` in the `else` branch.
+ //
+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
+ // compiler will eagerly select the inherent impl of
+ // `transmute_ref` (which requires `Dst: Sized`) because inherent
+ // methods take priority over trait methods. It does this before
+ // it realizes `Dst` is `!Sized`, leading to a compile error when
+ // it checks the bounds later.
+ //
+ // By calling this helper (which returns `&Dst`), we force `Dst`
+ // to be fully resolved. By the time it gets to the `else`
+ // branch, the compiler knows `Dst` is `!Sized`, properly
+ // disqualifies the inherent method, and falls back to the trait
+ // implementation.
+ t.transmute_ref_inference_helper()
+ } else {
+ // SAFETY: The outer `if false` branch ensures that:
+ // - `Src: IntoBytes + Immutable`
+ // - `Dst: FromBytes + Immutable`
+ unsafe {
+ t.transmute_ref()
+ }
+ }
+ }
+ }}
+}
+
+/// Safely transmutes a mutable reference of one type to a mutable reference of
+/// another type of the same size and compatible alignment.
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// const fn transmute_mut<'src, 'dst, Src, Dst>(src: &'src mut Src) -> &'dst mut Dst
+/// where
+/// 'src: 'dst,
+/// Src: FromBytes + IntoBytes + ?Sized,
+/// Dst: FromBytes + IntoBytes + ?Sized,
+/// align_of::<Src>() >= align_of::<Dst>(),
+/// size_compatible::<Src, Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// The types `Src` and `Dst` are inferred from the calling context; they cannot
+/// be explicitly specified in the macro invocation.
+///
+/// # Size compatibility
+///
+/// `transmute_mut!` supports transmuting between `Sized` types, between unsized
+/// (i.e., `?Sized`) types, and from a `Sized` type to an unsized type. It
+/// supports any transmutation that preserves the number of bytes of the
+/// referent, even if doing so requires updating the metadata stored in an
+/// unsized "fat" reference:
+///
+/// ```
+/// # use zerocopy::transmute_mut;
+/// # use core::mem::size_of_val; // Not in the prelude on our MSRV
+/// let src: &mut [[u8; 2]] = &mut [[0, 1], [2, 3]][..];
+/// let dst: &mut [u8] = transmute_mut!(src);
+///
+/// assert_eq!(dst.len(), 4);
+/// assert_eq!(dst, [0, 1, 2, 3]);
+/// let dst_size = size_of_val(dst);
+/// assert_eq!(src.len(), 2);
+/// assert_eq!(size_of_val(src), dst_size);
+/// ```
+///
+/// # Errors
+///
+/// Violations of the alignment and size compatibility checks are detected
+/// *after* the compiler performs monomorphization. This has two important
+/// consequences.
+///
+/// First, it means that generic code will *never* fail these conditions:
+///
+/// ```
+/// # use zerocopy::{transmute_mut, FromBytes, IntoBytes, Immutable};
+/// fn transmute_mut<Src, Dst>(src: &mut Src) -> &mut Dst
+/// where
+/// Src: FromBytes + IntoBytes,
+/// Dst: FromBytes + IntoBytes,
+/// {
+/// transmute_mut!(src)
+/// }
+/// ```
+///
+/// Instead, failures will only be detected once generic code is instantiated
+/// with concrete types:
+///
+/// ```compile_fail,E0080
+/// # use zerocopy::{transmute_mut, FromBytes, IntoBytes, Immutable};
+/// #
+/// # fn transmute_mut<Src, Dst>(src: &mut Src) -> &mut Dst
+/// # where
+/// # Src: FromBytes + IntoBytes,
+/// # Dst: FromBytes + IntoBytes,
+/// # {
+/// # transmute_mut!(src)
+/// # }
+/// let src: &mut u16 = &mut 0;
+/// let dst: &mut u8 = transmute_mut(src);
+/// ```
+///
+/// Second, the fact that violations are detected after monomorphization means
+/// that `cargo check` will usually not detect errors, even when types are
+/// concrete. Instead, `cargo build` must be used to detect such errors.
+///
+///
+/// # Examples
+///
+/// Transmuting between `Sized` types:
+///
+/// ```
+/// # use zerocopy::transmute_mut;
+/// let mut one_dimensional: [u8; 8] = [0, 1, 2, 3, 4, 5, 6, 7];
+///
+/// let two_dimensional: &mut [[u8; 4]; 2] = transmute_mut!(&mut one_dimensional);
+///
+/// assert_eq!(two_dimensional, &[[0, 1, 2, 3], [4, 5, 6, 7]]);
+///
+/// two_dimensional.reverse();
+///
+/// assert_eq!(one_dimensional, [4, 5, 6, 7, 0, 1, 2, 3]);
+/// ```
+///
+/// Transmuting between unsized types:
+///
+/// ```
+/// # use {zerocopy::*, zerocopy_derive::*};
+/// # type u16 = zerocopy::byteorder::native_endian::U16;
+/// # type u32 = zerocopy::byteorder::native_endian::U32;
+/// #[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
+/// #[repr(C)]
+/// struct SliceDst<T, U> {
+/// t: T,
+/// u: [U],
+/// }
+///
+/// type Src = SliceDst<u32, u16>;
+/// type Dst = SliceDst<u16, u8>;
+///
+/// let mut bytes = [0, 1, 2, 3, 4, 5, 6, 7];
+/// let src = Src::mut_from_bytes(&mut bytes[..]).unwrap();
+/// let dst: &mut Dst = transmute_mut!(src);
+///
+/// assert_eq!(dst.t.as_bytes(), [0, 1]);
+/// assert_eq!(dst.u, [2, 3, 4, 5, 6, 7]);
+///
+/// assert_eq!(src.t.as_bytes(), [0, 1, 2, 3]);
+/// assert_eq!(src.u.len(), 2);
+/// assert_eq!(src.u.as_bytes(), [4, 5, 6, 7]);
+/// ```
+#[macro_export]
+macro_rules! transmute_mut {
+ ($e:expr) => {{
+ // NOTE: This must be a macro (rather than a function with trait bounds)
+ // because, for backwards-compatibility on v0.8.x, we use the autoref
+ // specialization trick to dispatch to different `transmute_mut`
+ // implementations: one which doesn't require `Src: KnownLayout + Dst:
+ // KnownLayout` when `Src: Sized + Dst: Sized`, and one which requires
+ // `KnownLayout` bounds otherwise.
+
+ // Ensure that the source type is a mutable reference.
+ let e: &mut _ = $e;
+
+ #[allow(unused)]
+ use $crate::util::macro_util::TransmuteMutDst as _;
+ let t = $crate::util::macro_util::Wrap::new(e);
+ if false {
+ // This branch exists solely to force the compiler to infer the type
+ // of `Dst` *before* it attempts to resolve the method call to
+ // `transmute_mut` in the `else` branch.
+ //
+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
+ // compiler will eagerly select the inherent impl of `transmute_mut`
+ // (which requires `Dst: Sized`) because inherent methods take
+ // priority over trait methods. It does this before it realizes
+ // `Dst` is `!Sized`, leading to a compile error when it checks the
+ // bounds later.
+ //
+ // By calling this helper (which returns `&mut Dst`), we force `Dst`
+ // to be fully resolved. By the time it gets to the `else` branch,
+ // the compiler knows `Dst` is `!Sized`, properly disqualifies the
+ // inherent method, and falls back to the trait implementation.
+ t.transmute_mut_inference_helper()
+ } else {
+ t.transmute_mut()
+ }
+ }}
+}
+
+/// Conditionally transmutes a value of one type to a value of another type of
+/// the same size.
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// fn try_transmute<Src, Dst>(src: Src) -> Result<Dst, ValidityError<Src, Dst>>
+/// where
+/// Src: IntoBytes,
+/// Dst: TryFromBytes,
+/// size_of::<Src>() == size_of::<Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// However, unlike a function, this macro can only be invoked when the types of
+/// `Src` and `Dst` are completely concrete. The types `Src` and `Dst` are
+/// inferred from the calling context; they cannot be explicitly specified in
+/// the macro invocation.
+///
+/// Note that the `Src` produced by the expression `$e` will *not* be dropped.
+/// Semantically, its bits will be copied into a new value of type `Dst`, the
+/// original `Src` will be forgotten, and the value of type `Dst` will be
+/// returned.
+///
+/// # Examples
+///
+/// ```
+/// # use zerocopy::*;
+/// // 0u8 → bool = false
+/// assert_eq!(try_transmute!(0u8), Ok(false));
+///
+/// // 1u8 → bool = true
+/// assert_eq!(try_transmute!(1u8), Ok(true));
+///
+/// // 2u8 → bool = error
+/// assert!(matches!(
+/// try_transmute!(2u8),
+/// Result::<bool, _>::Err(ValidityError { .. })
+/// ));
+/// ```
+///
+#[doc = codegen_section!(
+ header = "h2",
+ bench = "try_transmute",
+ format = "coco_static_size",
+)]
+#[macro_export]
+macro_rules! try_transmute {
+ ($e:expr) => {{
+ // NOTE: This must be a macro (rather than a function with trait bounds)
+ // because there's no way, in a generic context, to enforce that two
+ // types have the same size. `core::mem::transmute` uses compiler magic
+ // to enforce this so long as the types are concrete.
+
+ let e = $e;
+ if false {
+ // Check that the sizes of the source and destination types are
+ // equal.
+
+ // SAFETY: This code is never executed.
+ Ok(unsafe {
+ // Clippy: We can't annotate the types; this macro is designed
+ // to infer the types from the calling context.
+ #[allow(clippy::missing_transmute_annotations)]
+ $crate::util::macro_util::core_reexport::mem::transmute(e)
+ })
+ } else {
+ $crate::util::macro_util::try_transmute::<_, _>(e)
+ }
+ }}
+}
+
+/// Conditionally transmutes a mutable or immutable reference of one type to an
+/// immutable reference of another type of the same size and compatible
+/// alignment.
+///
+/// *Note that while the **value** of the referent is checked for validity at
+/// runtime, the **size** and **alignment** are checked at compile time. For
+/// conversions which are fallible with respect to size and alignment, see the
+/// methods on [`TryFromBytes`].*
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// fn try_transmute_ref<Src, Dst>(src: &Src) -> Result<&Dst, ValidityError<&Src, Dst>>
+/// where
+/// Src: IntoBytes + Immutable + ?Sized,
+/// Dst: TryFromBytes + Immutable + ?Sized,
+/// align_of::<Src>() >= align_of::<Dst>(),
+/// size_compatible::<Src, Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// The types `Src` and `Dst` are inferred from the calling context; they cannot
+/// be explicitly specified in the macro invocation.
+///
+/// [`TryFromBytes`]: crate::TryFromBytes
+///
+/// # Size compatibility
+///
+/// `try_transmute_ref!` supports transmuting between `Sized` types, between
+/// unsized (i.e., `?Sized`) types, and from a `Sized` type to an unsized type.
+/// It supports any transmutation that preserves the number of bytes of the
+/// referent, even if doing so requires updating the metadata stored in an
+/// unsized "fat" reference:
+///
+/// ```
+/// # use zerocopy::try_transmute_ref;
+/// # use core::mem::size_of_val; // Not in the prelude on our MSRV
+/// let src: &[[u8; 2]] = &[[0, 1], [2, 3]][..];
+/// let dst: &[u8] = try_transmute_ref!(src).unwrap();
+///
+/// assert_eq!(src.len(), 2);
+/// assert_eq!(dst.len(), 4);
+/// assert_eq!(dst, [0, 1, 2, 3]);
+/// assert_eq!(size_of_val(src), size_of_val(dst));
+/// ```
+///
+/// # Examples
+///
+/// Transmuting between `Sized` types:
+///
+/// ```
+/// # use zerocopy::*;
+/// // 0u8 → bool = false
+/// assert_eq!(try_transmute_ref!(&0u8), Ok(&false));
+///
+/// // 1u8 → bool = true
+/// assert_eq!(try_transmute_ref!(&1u8), Ok(&true));
+///
+/// // 2u8 → bool = error
+/// assert!(matches!(
+/// try_transmute_ref!(&2u8),
+/// Result::<&bool, _>::Err(ValidityError { .. })
+/// ));
+/// ```
+///
+/// Transmuting between unsized types:
+///
+/// ```
+/// # use {zerocopy::*, zerocopy_derive::*};
+/// # type u16 = zerocopy::byteorder::native_endian::U16;
+/// # type u32 = zerocopy::byteorder::native_endian::U32;
+/// #[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
+/// #[repr(C)]
+/// struct SliceDst<T, U> {
+/// t: T,
+/// u: [U],
+/// }
+///
+/// type Src = SliceDst<u32, u16>;
+/// type Dst = SliceDst<u16, bool>;
+///
+/// let src = Src::ref_from_bytes(&[0, 1, 0, 1, 0, 1, 0, 1]).unwrap();
+/// let dst: &Dst = try_transmute_ref!(src).unwrap();
+///
+/// assert_eq!(src.t.as_bytes(), [0, 1, 0, 1]);
+/// assert_eq!(src.u.len(), 2);
+/// assert_eq!(src.u.as_bytes(), [0, 1, 0, 1]);
+///
+/// assert_eq!(dst.t.as_bytes(), [0, 1]);
+/// assert_eq!(dst.u, [false, true, false, true, false, true]);
+/// ```
+///
+#[doc = codegen_section!(
+ header = "h2",
+ bench = "try_transmute_ref",
+ format = "coco",
+ arity = 2,
+ [
+ open
+ @index 1
+ @title "Sized"
+ @variant "static_size"
+ ],
+ [
+ @index 2
+ @title "Unsized"
+ @variant "dynamic_size"
+ ]
+)]
+#[macro_export]
+macro_rules! try_transmute_ref {
+ ($e:expr) => {{
+ // Ensure that the source type is a reference or a mutable reference
+ // (note that mutable references are implicitly reborrowed here).
+ let e: &_ = $e;
+
+ #[allow(unused_imports)]
+ use $crate::util::macro_util::TryTransmuteRefDst as _;
+ let t = $crate::util::macro_util::Wrap::new(e);
+ if false {
+ // This branch exists solely to force the compiler to infer the type
+ // of `Dst` *before* it attempts to resolve the method call to
+ // `try_transmute_ref` in the `else` branch.
+ //
+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
+ // compiler will eagerly select the inherent impl of
+ // `try_transmute_ref` (which requires `Dst: Sized`) because
+ // inherent methods take priority over trait methods. It does this
+ // before it realizes `Dst` is `!Sized`, leading to a compile error
+ // when it checks the bounds later.
+ //
+ // By calling this helper (which returns `&Dst`), we force `Dst`
+ // to be fully resolved. By the time it gets to the `else`
+ // branch, the compiler knows `Dst` is `!Sized`, properly
+ // disqualifies the inherent method, and falls back to the trait
+ // implementation.
+ Ok(t.transmute_ref_inference_helper())
+ } else {
+ t.try_transmute_ref()
+ }
+ }}
+}
+
+/// Conditionally transmutes a mutable reference of one type to a mutable
+/// reference of another type of the same size and compatible alignment.
+///
+/// *Note that while the **value** of the referent is checked for validity at
+/// runtime, the **size** and **alignment** are checked at compile time. For
+/// conversions which are fallible with respect to size and alignment, see the
+/// methods on [`TryFromBytes`].*
+///
+/// This macro behaves like an invocation of this function:
+///
+/// ```ignore
+/// fn try_transmute_mut<Src, Dst>(src: &mut Src) -> Result<&mut Dst, ValidityError<&mut Src, Dst>>
+/// where
+/// Src: FromBytes + IntoBytes + ?Sized,
+/// Dst: TryFromBytes + IntoBytes + ?Sized,
+/// align_of::<Src>() >= align_of::<Dst>(),
+/// size_compatible::<Src, Dst>(),
+/// {
+/// # /*
+/// ...
+/// # */
+/// }
+/// ```
+///
+/// The types `Src` and `Dst` are inferred from the calling context; they cannot
+/// be explicitly specified in the macro invocation.
+///
+/// [`TryFromBytes`]: crate::TryFromBytes
+///
+/// # Size compatibility
+///
+/// `try_transmute_mut!` supports transmuting between `Sized` types, between
+/// unsized (i.e., `?Sized`) types, and from a `Sized` type to an unsized type.
+/// It supports any transmutation that preserves the number of bytes of the
+/// referent, even if doing so requires updating the metadata stored in an
+/// unsized "fat" reference:
+///
+/// ```
+/// # use zerocopy::try_transmute_mut;
+/// # use core::mem::size_of_val; // Not in the prelude on our MSRV
+/// let src: &mut [[u8; 2]] = &mut [[0, 1], [2, 3]][..];
+/// let dst: &mut [u8] = try_transmute_mut!(src).unwrap();
+///
+/// assert_eq!(dst.len(), 4);
+/// assert_eq!(dst, [0, 1, 2, 3]);
+/// let dst_size = size_of_val(dst);
+/// assert_eq!(src.len(), 2);
+/// assert_eq!(size_of_val(src), dst_size);
+/// ```
+///
+/// # Examples
+///
+/// Transmuting between `Sized` types:
+///
+/// ```
+/// # use zerocopy::*;
+/// // 0u8 → bool = false
+/// let src = &mut 0u8;
+/// assert_eq!(try_transmute_mut!(src), Ok(&mut false));
+///
+/// // 1u8 → bool = true
+/// let src = &mut 1u8;
+/// assert_eq!(try_transmute_mut!(src), Ok(&mut true));
+///
+/// // 2u8 → bool = error
+/// let src = &mut 2u8;
+/// assert!(matches!(
+/// try_transmute_mut!(src),
+/// Result::<&mut bool, _>::Err(ValidityError { .. })
+/// ));
+/// ```
+///
+/// Transmuting between unsized types:
+///
+/// ```
+/// # use {zerocopy::*, zerocopy_derive::*};
+/// # type u16 = zerocopy::byteorder::native_endian::U16;
+/// # type u32 = zerocopy::byteorder::native_endian::U32;
+/// #[derive(KnownLayout, FromBytes, IntoBytes, Immutable)]
+/// #[repr(C)]
+/// struct SliceDst<T, U> {
+/// t: T,
+/// u: [U],
+/// }
+///
+/// type Src = SliceDst<u32, u16>;
+/// type Dst = SliceDst<u16, bool>;
+///
+/// let mut bytes = [0, 1, 0, 1, 0, 1, 0, 1];
+/// let src = Src::mut_from_bytes(&mut bytes).unwrap();
+///
+/// assert_eq!(src.t.as_bytes(), [0, 1, 0, 1]);
+/// assert_eq!(src.u.len(), 2);
+/// assert_eq!(src.u.as_bytes(), [0, 1, 0, 1]);
+///
+/// let dst: &Dst = try_transmute_mut!(src).unwrap();
+///
+/// assert_eq!(dst.t.as_bytes(), [0, 1]);
+/// assert_eq!(dst.u, [false, true, false, true, false, true]);
+/// ```
+#[macro_export]
+macro_rules! try_transmute_mut {
+ ($e:expr) => {{
+ // Ensure that the source type is a mutable reference.
+ let e: &mut _ = $e;
+
+ #[allow(unused_imports)]
+ use $crate::util::macro_util::TryTransmuteMutDst as _;
+ let t = $crate::util::macro_util::Wrap::new(e);
+ if false {
+ // This branch exists solely to force the compiler to infer the type
+ // of `Dst` *before* it attempts to resolve the method call to
+ // `try_transmute_mut` in the `else` branch.
+ //
+ // Without this, if `Src` is `Sized` but `Dst` is `!Sized`, the
+ // compiler will eagerly select the inherent impl of
+ // `try_transmute_mut` (which requires `Dst: Sized`) because
+ // inherent methods take priority over trait methods. It does this
+ // before it realizes `Dst` is `!Sized`, leading to a compile error
+ // when it checks the bounds later.
+ //
+ // By calling this helper (which returns `&Dst`), we force `Dst`
+ // to be fully resolved. By the time it gets to the `else`
+ // branch, the compiler knows `Dst` is `!Sized`, properly
+ // disqualifies the inherent method, and falls back to the trait
+ // implementation.
+ Ok(t.transmute_mut_inference_helper())
+ } else {
+ t.try_transmute_mut()
+ }
+ }}
+}
+
+/// Includes a file and safely transmutes it to a value of an arbitrary type.
+///
+/// The file will be included as a byte array, `[u8; N]`, which will be
+/// transmuted to another type, `T`. `T` is inferred from the calling context,
+/// and must implement [`FromBytes`].
+///
+/// The file is located relative to the current file (similarly to how modules
+/// are found). The provided path is interpreted in a platform-specific way at
+/// compile time. So, for instance, an invocation with a Windows path containing
+/// backslashes `\` would not compile correctly on Unix.
+///
+/// `include_value!` is ignorant of byte order. For byte order-aware types, see
+/// the [`byteorder`] module.
+///
+/// [`FromBytes`]: crate::FromBytes
+/// [`byteorder`]: crate::byteorder
+///
+/// # Examples
+///
+/// Assume there are two files in the same directory with the following
+/// contents:
+///
+/// File `data` (no trailing newline):
+///
+/// ```text
+/// abcd
+/// ```
+///
+/// File `main.rs`:
+///
+/// ```rust
+/// use zerocopy::include_value;
+/// # macro_rules! include_value {
+/// # ($file:expr) => { zerocopy::include_value!(concat!("../testdata/include_value/", $file)) };
+/// # }
+///
+/// fn main() {
+/// let as_u32: u32 = include_value!("data");
+/// assert_eq!(as_u32, u32::from_ne_bytes([b'a', b'b', b'c', b'd']));
+/// let as_i32: i32 = include_value!("data");
+/// assert_eq!(as_i32, i32::from_ne_bytes([b'a', b'b', b'c', b'd']));
+/// }
+/// ```
+///
+/// # Use in `const` contexts
+///
+/// This macro can be invoked in `const` contexts.
+#[doc(alias("include_bytes", "include_data", "include_type"))]
+#[macro_export]
+macro_rules! include_value {
+ ($file:expr $(,)?) => {
+ $crate::transmute!(*::core::include_bytes!($file))
+ };
+}
+
+#[doc(hidden)]
+#[macro_export]
+macro_rules! cryptocorrosion_derive_traits {
+ (
+ #[repr($repr:ident)]
+ $(#[$attr:meta])*
+ $vis:vis struct $name:ident $(<$($tyvar:ident),*>)?
+ $(
+ (
+ $($tuple_field_vis:vis $tuple_field_ty:ty),*
+ );
+ )?
+
+ $(
+ {
+ $($field_vis:vis $field_name:ident: $field_ty:ty,)*
+ }
+ )?
+ ) => {
+ $crate::cryptocorrosion_derive_traits!(@assert_allowed_struct_repr #[repr($repr)]);
+
+ $(#[$attr])*
+ #[repr($repr)]
+ $vis struct $name $(<$($tyvar),*>)?
+ $(
+ (
+ $($tuple_field_vis $tuple_field_ty),*
+ );
+ )?
+
+ $(
+ {
+ $($field_vis $field_name: $field_ty,)*
+ }
+ )?
+
+ // SAFETY: See inline.
+ unsafe impl $(<$($tyvar),*>)? $crate::TryFromBytes for $name$(<$($tyvar),*>)?
+ where
+ $(
+ $($tuple_field_ty: $crate::FromBytes,)*
+ )?
+
+ $(
+ $($field_ty: $crate::FromBytes,)*
+ )?
+ {
+ #[inline(always)]
+ fn is_bit_valid<A>(_: $crate::Maybe<'_, Self, A>) -> bool
+ where
+ A: $crate::invariant::Alignment,
+ {
+ // SAFETY: This macro only accepts `#[repr(C)]` and
+ // `#[repr(transparent)]` structs, and this `impl` block
+ // requires all field types to be `FromBytes`. Thus, all
+ // initialized byte sequences constitutes valid instances of
+ // `Self`.
+ true
+ }
+
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` and
+ // `#[repr(transparent)]` structs, and this `impl` block requires all
+ // field types to be `FromBytes`, which is a sub-trait of `FromZeros`.
+ unsafe impl $(<$($tyvar),*>)? $crate::FromZeros for $name$(<$($tyvar),*>)?
+ where
+ $(
+ $($tuple_field_ty: $crate::FromBytes,)*
+ )?
+
+ $(
+ $($field_ty: $crate::FromBytes,)*
+ )?
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` and
+ // `#[repr(transparent)]` structs, and this `impl` block requires all
+ // field types to be `FromBytes`.
+ unsafe impl $(<$($tyvar),*>)? $crate::FromBytes for $name$(<$($tyvar),*>)?
+ where
+ $(
+ $($tuple_field_ty: $crate::FromBytes,)*
+ )?
+
+ $(
+ $($field_ty: $crate::FromBytes,)*
+ )?
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` and
+ // `#[repr(transparent)]` structs, this `impl` block requires all field
+ // types to be `IntoBytes`, and a padding check is used to ensures that
+ // there are no padding bytes.
+ unsafe impl $(<$($tyvar),*>)? $crate::IntoBytes for $name$(<$($tyvar),*>)?
+ where
+ $(
+ $($tuple_field_ty: $crate::IntoBytes,)*
+ )?
+
+ $(
+ $($field_ty: $crate::IntoBytes,)*
+ )?
+
+ (): $crate::util::macro_util::PaddingFree<
+ Self,
+ {
+ $crate::cryptocorrosion_derive_traits!(
+ @struct_padding_check #[repr($repr)]
+ $(($($tuple_field_ty),*))?
+ $({$($field_ty),*})?
+ )
+ },
+ >,
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` and
+ // `#[repr(transparent)]` structs, and this `impl` block requires all
+ // field types to be `Immutable`.
+ unsafe impl $(<$($tyvar),*>)? $crate::Immutable for $name$(<$($tyvar),*>)?
+ where
+ $(
+ $($tuple_field_ty: $crate::Immutable,)*
+ )?
+
+ $(
+ $($field_ty: $crate::Immutable,)*
+ )?
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+ };
+ (@assert_allowed_struct_repr #[repr(transparent)]) => {};
+ (@assert_allowed_struct_repr #[repr(C)]) => {};
+ (@assert_allowed_struct_repr #[$_attr:meta]) => {
+ compile_error!("repr must be `#[repr(transparent)]` or `#[repr(C)]`");
+ };
+ (
+ @struct_padding_check #[repr(transparent)]
+ $(($($tuple_field_ty:ty),*))?
+ $({$($field_ty:ty),*})?
+ ) => {
+ // SAFETY: `#[repr(transparent)]` structs cannot have the same layout as
+ // their single non-zero-sized field, and so cannot have any padding
+ // outside of that field.
+ 0
+ };
+ (
+ @struct_padding_check #[repr(C)]
+ $(($($tuple_field_ty:ty),*))?
+ $({$($field_ty:ty),*})?
+ ) => {
+ $crate::struct_padding!(
+ Self,
+ None,
+ None,
+ [
+ $($($tuple_field_ty),*)?
+ $($($field_ty),*)?
+ ]
+ )
+ };
+ (
+ #[repr(C)]
+ $(#[$attr:meta])*
+ $vis:vis union $name:ident {
+ $(
+ $field_name:ident: $field_ty:ty,
+ )*
+ }
+ ) => {
+ $(#[$attr])*
+ #[repr(C)]
+ $vis union $name {
+ $(
+ $field_name: $field_ty,
+ )*
+ }
+
+ // SAFETY: See inline.
+ unsafe impl $crate::TryFromBytes for $name
+ where
+ $(
+ $field_ty: $crate::FromBytes,
+ )*
+ {
+ #[inline(always)]
+ fn is_bit_valid<A>(_: $crate::Maybe<'_, Self, A>) -> bool
+ where
+ A: $crate::invariant::Alignment,
+ {
+ // SAFETY: This macro only accepts `#[repr(C)]` unions, and this
+ // `impl` block requires all field types to be `FromBytes`.
+ // Thus, all initialized byte sequences constitutes valid
+ // instances of `Self`.
+ true
+ }
+
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` unions, and this `impl`
+ // block requires all field types to be `FromBytes`, which is a
+ // sub-trait of `FromZeros`.
+ unsafe impl $crate::FromZeros for $name
+ where
+ $(
+ $field_ty: $crate::FromBytes,
+ )*
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` unions, and this `impl`
+ // block requires all field types to be `FromBytes`.
+ unsafe impl $crate::FromBytes for $name
+ where
+ $(
+ $field_ty: $crate::FromBytes,
+ )*
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` unions, this `impl`
+ // block requires all field types to be `IntoBytes`, and a padding check
+ // is used to ensures that there are no padding bytes before or after
+ // any field.
+ unsafe impl $crate::IntoBytes for $name
+ where
+ $(
+ $field_ty: $crate::IntoBytes,
+ )*
+ (): $crate::util::macro_util::PaddingFree<
+ Self,
+ {
+ $crate::union_padding!(
+ Self,
+ None::<usize>,
+ None::<usize>,
+ [$($field_ty),*]
+ )
+ },
+ >,
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+
+ // SAFETY: This macro only accepts `#[repr(C)]` unions, and this `impl`
+ // block requires all field types to be `Immutable`.
+ unsafe impl $crate::Immutable for $name
+ where
+ $(
+ $field_ty: $crate::Immutable,
+ )*
+ {
+ fn only_derive_is_allowed_to_implement_this_trait() {}
+ }
+ };
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::{
+ byteorder::native_endian::{U16, U32},
+ util::testutil::*,
+ *,
+ };
+
+ #[derive(KnownLayout, Immutable, FromBytes, IntoBytes, PartialEq, Debug)]
+ #[repr(C)]
+ struct SliceDst<T, U> {
+ a: T,
+ b: [U],
+ }
+
+ #[test]
+ fn test_transmute() {
+ // Test that memory is transmuted as expected.
+ let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let x: [[u8; 2]; 4] = transmute!(array_of_u8s);
+ assert_eq!(x, array_of_arrays);
+ let x: [u8; 8] = transmute!(array_of_arrays);
+ assert_eq!(x, array_of_u8s);
+
+ // Test that memory is transmuted as expected when shrinking.
+ let x: [[u8; 2]; 3] = transmute!(#![allow(shrink)] array_of_u8s);
+ assert_eq!(x, [[0u8, 1], [2, 3], [4, 5]]);
+
+ // Test that the source expression's value is forgotten rather than
+ // dropped.
+ #[derive(IntoBytes)]
+ #[repr(transparent)]
+ struct PanicOnDrop(());
+ impl Drop for PanicOnDrop {
+ fn drop(&mut self) {
+ panic!("PanicOnDrop::drop");
+ }
+ }
+ #[allow(clippy::let_unit_value)]
+ let _: () = transmute!(PanicOnDrop(()));
+ #[allow(clippy::let_unit_value)]
+ let _: () = transmute!(#![allow(shrink)] PanicOnDrop(()));
+
+ // Test that `transmute!` is legal in a const context.
+ const ARRAY_OF_U8S: [u8; 8] = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ const ARRAY_OF_ARRAYS: [[u8; 2]; 4] = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ const X: [[u8; 2]; 4] = transmute!(ARRAY_OF_U8S);
+ assert_eq!(X, ARRAY_OF_ARRAYS);
+ const X_SHRINK: [[u8; 2]; 3] = transmute!(#![allow(shrink)] ARRAY_OF_U8S);
+ assert_eq!(X_SHRINK, [[0u8, 1], [2, 3], [4, 5]]);
+
+ // Test that `transmute!` works with `!Immutable` types.
+ let x: usize = transmute!(UnsafeCell::new(1usize));
+ assert_eq!(x, 1);
+ let x: UnsafeCell<usize> = transmute!(1usize);
+ assert_eq!(x.into_inner(), 1);
+ let x: UnsafeCell<isize> = transmute!(UnsafeCell::new(1usize));
+ assert_eq!(x.into_inner(), 1);
+ }
+
+ // A `Sized` type which doesn't implement `KnownLayout` (it is "not
+ // `KnownLayout`", or `Nkl`).
+ //
+ // This permits us to test that `transmute_ref!` and `transmute_mut!` work
+ // for types which are `Sized + !KnownLayout`. When we added support for
+ // slice DSTs in #1924, this new support relied on `KnownLayout`, but we
+ // need to make sure to remain backwards-compatible with code which uses
+ // these macros with types which are `!KnownLayout`.
+ #[derive(FromBytes, IntoBytes, Immutable, PartialEq, Eq, Debug)]
+ #[repr(transparent)]
+ struct Nkl<T>(T);
+
+ #[test]
+ fn test_transmute_ref() {
+ // Test that memory is transmuted as expected.
+ let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let x: &[[u8; 2]; 4] = transmute_ref!(&array_of_u8s);
+ assert_eq!(*x, array_of_arrays);
+ let x: &[u8; 8] = transmute_ref!(&array_of_arrays);
+ assert_eq!(*x, array_of_u8s);
+
+ // Test that `transmute_ref!` is legal in a const context.
+ const ARRAY_OF_U8S: [u8; 8] = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ const ARRAY_OF_ARRAYS: [[u8; 2]; 4] = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ #[allow(clippy::redundant_static_lifetimes)]
+ const X: &'static [[u8; 2]; 4] = transmute_ref!(&ARRAY_OF_U8S);
+ assert_eq!(*X, ARRAY_OF_ARRAYS);
+
+ // Test sized -> unsized transmutation.
+ let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let slice_of_arrays = &array_of_arrays[..];
+ let x: &[[u8; 2]] = transmute_ref!(&array_of_u8s);
+ assert_eq!(x, slice_of_arrays);
+
+ // Before 1.61.0, we can't define the `const fn transmute_ref` function
+ // that we do on and after 1.61.0.
+ #[cfg(no_zerocopy_generic_bounds_in_const_fn_1_61_0)]
+ {
+ // Test that `transmute_ref!` supports non-`KnownLayout` `Sized`
+ // types.
+ const ARRAY_OF_NKL_U8S: Nkl<[u8; 8]> = Nkl([0u8, 1, 2, 3, 4, 5, 6, 7]);
+ const ARRAY_OF_NKL_ARRAYS: Nkl<[[u8; 2]; 4]> = Nkl([[0, 1], [2, 3], [4, 5], [6, 7]]);
+ const X_NKL: &Nkl<[[u8; 2]; 4]> = transmute_ref!(&ARRAY_OF_NKL_U8S);
+ assert_eq!(*X_NKL, ARRAY_OF_NKL_ARRAYS);
+ }
+
+ #[cfg(not(no_zerocopy_generic_bounds_in_const_fn_1_61_0))]
+ {
+ // Call through a generic function to make sure our autoref
+ // specialization trick works even when types are generic.
+ const fn transmute_ref<T, U>(t: &T) -> &U
+ where
+ T: IntoBytes + Immutable,
+ U: FromBytes + Immutable,
+ {
+ transmute_ref!(t)
+ }
+
+ // Test that `transmute_ref!` supports non-`KnownLayout` `Sized`
+ // types.
+ const ARRAY_OF_NKL_U8S: Nkl<[u8; 8]> = Nkl([0u8, 1, 2, 3, 4, 5, 6, 7]);
+ const ARRAY_OF_NKL_ARRAYS: Nkl<[[u8; 2]; 4]> = Nkl([[0, 1], [2, 3], [4, 5], [6, 7]]);
+ const X_NKL: &Nkl<[[u8; 2]; 4]> = transmute_ref(&ARRAY_OF_NKL_U8S);
+ assert_eq!(*X_NKL, ARRAY_OF_NKL_ARRAYS);
+ }
+
+ // Test that `transmute_ref!` works on slice DSTs in and that memory is
+ // transmuted as expected.
+ let slice_dst_of_u8s =
+ SliceDst::<U16, [u8; 2]>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ let slice_dst_of_u16s =
+ SliceDst::<U16, U16>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ let x: &SliceDst<U16, U16> = transmute_ref!(slice_dst_of_u8s);
+ assert_eq!(x, slice_dst_of_u16s);
+
+ let slice_dst_of_u8s =
+ SliceDst::<U16, u8>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ let x: &[u8] = transmute_ref!(slice_dst_of_u8s);
+ assert_eq!(x, [0, 1, 2, 3, 4, 5]);
+
+ let x: &[u8] = transmute_ref!(slice_dst_of_u16s);
+ assert_eq!(x, [0, 1, 2, 3, 4, 5]);
+
+ let x: &[U16] = transmute_ref!(slice_dst_of_u16s);
+ let slice_of_u16s: &[U16] = <[U16]>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ assert_eq!(x, slice_of_u16s);
+
+ // Test that transmuting from a type with larger trailing slice offset
+ // and larger trailing slice element works.
+ let bytes = &[0, 1, 2, 3, 4, 5, 6, 7][..];
+ let slice_dst_big = SliceDst::<U32, U16>::ref_from_bytes(bytes).unwrap();
+ let slice_dst_small = SliceDst::<U16, u8>::ref_from_bytes(bytes).unwrap();
+ let x: &SliceDst<U16, u8> = transmute_ref!(slice_dst_big);
+ assert_eq!(x, slice_dst_small);
+
+ // Test that it's legal to transmute a reference while shrinking the
+ // lifetime (note that `X` has the lifetime `'static`).
+ let x: &[u8; 8] = transmute_ref!(X);
+ assert_eq!(*x, ARRAY_OF_U8S);
+
+ // Test that `transmute_ref!` supports decreasing alignment.
+ let u = AU64(0);
+ let array = [0, 0, 0, 0, 0, 0, 0, 0];
+ let x: &[u8; 8] = transmute_ref!(&u);
+ assert_eq!(*x, array);
+
+ // Test that a mutable reference can be turned into an immutable one.
+ let mut x = 0u8;
+ #[allow(clippy::useless_transmute)]
+ let y: &u8 = transmute_ref!(&mut x);
+ assert_eq!(*y, 0);
+ }
+
+ #[test]
+ fn test_try_transmute() {
+ // Test that memory is transmuted with `try_transmute` as expected.
+ let array_of_bools = [false, true, false, true, false, true, false, true];
+ let array_of_arrays = [[0, 1], [0, 1], [0, 1], [0, 1]];
+ let x: Result<[[u8; 2]; 4], _> = try_transmute!(array_of_bools);
+ assert_eq!(x, Ok(array_of_arrays));
+ let x: Result<[bool; 8], _> = try_transmute!(array_of_arrays);
+ assert_eq!(x, Ok(array_of_bools));
+
+ // Test that `try_transmute!` works with `!Immutable` types.
+ let x: Result<usize, _> = try_transmute!(UnsafeCell::new(1usize));
+ assert_eq!(x.unwrap(), 1);
+ let x: Result<UnsafeCell<usize>, _> = try_transmute!(1usize);
+ assert_eq!(x.unwrap().into_inner(), 1);
+ let x: Result<UnsafeCell<isize>, _> = try_transmute!(UnsafeCell::new(1usize));
+ assert_eq!(x.unwrap().into_inner(), 1);
+
+ #[derive(FromBytes, IntoBytes, Debug, PartialEq)]
+ #[repr(transparent)]
+ struct PanicOnDrop<T>(T);
+
+ impl<T> Drop for PanicOnDrop<T> {
+ fn drop(&mut self) {
+ panic!("PanicOnDrop dropped");
+ }
+ }
+
+ // Since `try_transmute!` semantically moves its argument on failure,
+ // the `PanicOnDrop` is not dropped, and thus this shouldn't panic.
+ let x: Result<usize, _> = try_transmute!(PanicOnDrop(1usize));
+ assert_eq!(x, Ok(1));
+
+ // Since `try_transmute!` semantically returns ownership of its argument
+ // on failure, the `PanicOnDrop` is returned rather than dropped, and
+ // thus this shouldn't panic.
+ let y: Result<bool, _> = try_transmute!(PanicOnDrop(2u8));
+ // We have to use `map_err` instead of comparing against
+ // `Err(PanicOnDrop(2u8))` because the latter would create and then drop
+ // its `PanicOnDrop` temporary, which would cause a panic.
+ assert_eq!(y.as_ref().map_err(|p| &p.src.0), Err::<&bool, _>(&2u8));
+ mem::forget(y);
+ }
+
+ #[test]
+ fn test_try_transmute_ref() {
+ // Test that memory is transmuted with `try_transmute_ref` as expected.
+ let array_of_bools = &[false, true, false, true, false, true, false, true];
+ let array_of_arrays = &[[0, 1], [0, 1], [0, 1], [0, 1]];
+ let x: Result<&[[u8; 2]; 4], _> = try_transmute_ref!(array_of_bools);
+ assert_eq!(x, Ok(array_of_arrays));
+ let x: Result<&[bool; 8], _> = try_transmute_ref!(array_of_arrays);
+ assert_eq!(x, Ok(array_of_bools));
+
+ // Test that it's legal to transmute a reference while shrinking the
+ // lifetime.
+ {
+ let x: Result<&[[u8; 2]; 4], _> = try_transmute_ref!(array_of_bools);
+ assert_eq!(x, Ok(array_of_arrays));
+ }
+
+ // Test that `try_transmute_ref!` supports decreasing alignment.
+ let u = AU64(0);
+ let array = [0u8, 0, 0, 0, 0, 0, 0, 0];
+ let x: Result<&[u8; 8], _> = try_transmute_ref!(&u);
+ assert_eq!(x, Ok(&array));
+
+ // Test that a mutable reference can be turned into an immutable one.
+ let mut x = 0u8;
+ #[allow(clippy::useless_transmute)]
+ let y: Result<&u8, _> = try_transmute_ref!(&mut x);
+ assert_eq!(y, Ok(&0));
+
+ // Test that sized types work which don't implement `KnownLayout`.
+ let array_of_nkl_u8s = Nkl([0u8, 1, 2, 3, 4, 5, 6, 7]);
+ let array_of_nkl_arrays = Nkl([[0, 1], [2, 3], [4, 5], [6, 7]]);
+ let x: Result<&Nkl<[[u8; 2]; 4]>, _> = try_transmute_ref!(&array_of_nkl_u8s);
+ assert_eq!(x, Ok(&array_of_nkl_arrays));
+
+ // Test sized -> unsized transmutation.
+ let array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let slice_of_arrays = &array_of_arrays[..];
+ let x: Result<&[[u8; 2]], _> = try_transmute_ref!(&array_of_u8s);
+ assert_eq!(x, Ok(slice_of_arrays));
+
+ // Test unsized -> unsized transmutation.
+ let slice_dst_of_u8s =
+ SliceDst::<U16, [u8; 2]>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ let slice_dst_of_u16s =
+ SliceDst::<U16, U16>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap();
+ let x: Result<&SliceDst<U16, U16>, _> = try_transmute_ref!(slice_dst_of_u8s);
+ assert_eq!(x, Ok(slice_dst_of_u16s));
+ }
+
+ #[test]
+ fn test_try_transmute_mut() {
+ // Test that memory is transmuted with `try_transmute_mut` as expected.
+ let array_of_u8s = &mut [0u8, 1, 0, 1, 0, 1, 0, 1];
+ let array_of_arrays = &mut [[0u8, 1], [0, 1], [0, 1], [0, 1]];
+ let x: Result<&mut [[u8; 2]; 4], _> = try_transmute_mut!(array_of_u8s);
+ assert_eq!(x, Ok(array_of_arrays));
+
+ let array_of_bools = &mut [false, true, false, true, false, true, false, true];
+ let array_of_arrays = &mut [[0u8, 1], [0, 1], [0, 1], [0, 1]];
+ let x: Result<&mut [bool; 8], _> = try_transmute_mut!(array_of_arrays);
+ assert_eq!(x, Ok(array_of_bools));
+
+ // Test that it's legal to transmute a reference while shrinking the
+ // lifetime.
+ let array_of_bools = &mut [false, true, false, true, false, true, false, true];
+ let array_of_arrays = &mut [[0u8, 1], [0, 1], [0, 1], [0, 1]];
+ {
+ let x: Result<&mut [bool; 8], _> = try_transmute_mut!(array_of_arrays);
+ assert_eq!(x, Ok(array_of_bools));
+ }
+
+ // Test that `try_transmute_mut!` supports decreasing alignment.
+ let u = &mut AU64(0);
+ let array = &mut [0u8, 0, 0, 0, 0, 0, 0, 0];
+ let x: Result<&mut [u8; 8], _> = try_transmute_mut!(u);
+ assert_eq!(x, Ok(array));
+
+ // Test that a mutable reference can be turned into an immutable one.
+ let mut x = 0u8;
+ #[allow(clippy::useless_transmute)]
+ let y: Result<&mut u8, _> = try_transmute_mut!(&mut x);
+ assert_eq!(y, Ok(&mut 0));
+
+ // Test that sized types work which don't implement `KnownLayout`.
+ let mut array_of_nkl_u8s = Nkl([0u8, 1, 2, 3, 4, 5, 6, 7]);
+ let mut array_of_nkl_arrays = Nkl([[0, 1], [2, 3], [4, 5], [6, 7]]);
+ let x: Result<&mut Nkl<[[u8; 2]; 4]>, _> = try_transmute_mut!(&mut array_of_nkl_u8s);
+ assert_eq!(x, Ok(&mut array_of_nkl_arrays));
+
+ // Test sized -> unsized transmutation.
+ let mut array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let mut array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let slice_of_arrays = &mut array_of_arrays[..];
+ let x: Result<&mut [[u8; 2]], _> = try_transmute_mut!(&mut array_of_u8s);
+ assert_eq!(x, Ok(slice_of_arrays));
+
+ // Test unsized -> unsized transmutation.
+ let mut bytes = [0, 1, 2, 3, 4, 5, 6];
+ let slice_dst_of_u8s = SliceDst::<u8, [u8; 2]>::mut_from_bytes(&mut bytes[..]).unwrap();
+ let mut bytes = [0, 1, 2, 3, 4, 5, 6];
+ let slice_dst_of_u16s = SliceDst::<u8, U16>::mut_from_bytes(&mut bytes[..]).unwrap();
+ let x: Result<&mut SliceDst<u8, U16>, _> = try_transmute_mut!(slice_dst_of_u8s);
+ assert_eq!(x, Ok(slice_dst_of_u16s));
+ }
+
+ #[test]
+ fn test_transmute_mut() {
+ // Test that memory is transmuted as expected.
+ let mut array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let mut array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let x: &mut [[u8; 2]; 4] = transmute_mut!(&mut array_of_u8s);
+ assert_eq!(*x, array_of_arrays);
+ let x: &mut [u8; 8] = transmute_mut!(&mut array_of_arrays);
+ assert_eq!(*x, array_of_u8s);
+
+ {
+ // Test that it's legal to transmute a reference while shrinking the
+ // lifetime.
+ let x: &mut [u8; 8] = transmute_mut!(&mut array_of_arrays);
+ assert_eq!(*x, array_of_u8s);
+ }
+
+ // Test that `transmute_mut!` supports non-`KnownLayout` types.
+ let mut array_of_u8s = Nkl([0u8, 1, 2, 3, 4, 5, 6, 7]);
+ let mut array_of_arrays = Nkl([[0, 1], [2, 3], [4, 5], [6, 7]]);
+ let x: &mut Nkl<[[u8; 2]; 4]> = transmute_mut!(&mut array_of_u8s);
+ assert_eq!(*x, array_of_arrays);
+ let x: &mut Nkl<[u8; 8]> = transmute_mut!(&mut array_of_arrays);
+ assert_eq!(*x, array_of_u8s);
+
+ // Test that `transmute_mut!` supports decreasing alignment.
+ let mut u = AU64(0);
+ let array = [0, 0, 0, 0, 0, 0, 0, 0];
+ let x: &[u8; 8] = transmute_mut!(&mut u);
+ assert_eq!(*x, array);
+
+ // Test that a mutable reference can be turned into an immutable one.
+ let mut x = 0u8;
+ #[allow(clippy::useless_transmute)]
+ let y: &u8 = transmute_mut!(&mut x);
+ assert_eq!(*y, 0);
+
+ // Test that `transmute_mut!` works on slice DSTs in and that memory is
+ // transmuted as expected.
+ let mut bytes = [0, 1, 2, 3, 4, 5, 6];
+ let slice_dst_of_u8s = SliceDst::<u8, [u8; 2]>::mut_from_bytes(&mut bytes[..]).unwrap();
+ let mut bytes = [0, 1, 2, 3, 4, 5, 6];
+ let slice_dst_of_u16s = SliceDst::<u8, U16>::mut_from_bytes(&mut bytes[..]).unwrap();
+ let x: &mut SliceDst<u8, U16> = transmute_mut!(slice_dst_of_u8s);
+ assert_eq!(x, slice_dst_of_u16s);
+
+ // Test that `transmute_mut!` works on slices that memory is transmuted
+ // as expected.
+ let array_of_u16s: &mut [u16] = &mut [0u16, 1, 2];
+ let array_of_i16s: &mut [i16] = &mut [0i16, 1, 2];
+ let x: &mut [i16] = transmute_mut!(array_of_u16s);
+ assert_eq!(x, array_of_i16s);
+
+ // Test that transmuting from a type with larger trailing slice offset
+ // and larger trailing slice element works.
+ let mut bytes = [0, 1, 2, 3, 4, 5, 6, 7];
+ let slice_dst_big = SliceDst::<U32, U16>::mut_from_bytes(&mut bytes[..]).unwrap();
+ let mut bytes = [0, 1, 2, 3, 4, 5, 6, 7];
+ let slice_dst_small = SliceDst::<U16, u8>::mut_from_bytes(&mut bytes[..]).unwrap();
+ let x: &mut SliceDst<U16, u8> = transmute_mut!(slice_dst_big);
+ assert_eq!(x, slice_dst_small);
+
+ // Test sized -> unsized transmutation.
+ let mut array_of_u8s = [0u8, 1, 2, 3, 4, 5, 6, 7];
+ let mut array_of_arrays = [[0, 1], [2, 3], [4, 5], [6, 7]];
+ let slice_of_arrays = &mut array_of_arrays[..];
+ let x: &mut [[u8; 2]] = transmute_mut!(&mut array_of_u8s);
+ assert_eq!(x, slice_of_arrays);
+ }
+
+ #[test]
+ fn test_macros_evaluate_args_once() {
+ let mut ctr = 0;
+ #[allow(clippy::useless_transmute)]
+ let _: usize = transmute!({
+ ctr += 1;
+ 0usize
+ });
+ assert_eq!(ctr, 1);
+
+ let mut ctr = 0;
+ let _: &usize = transmute_ref!({
+ ctr += 1;
+ &0usize
+ });
+ assert_eq!(ctr, 1);
+
+ let mut ctr: usize = 0;
+ let _: &mut usize = transmute_mut!({
+ ctr += 1;
+ &mut ctr
+ });
+ assert_eq!(ctr, 1);
+
+ let mut ctr = 0;
+ #[allow(clippy::useless_transmute)]
+ let _: usize = try_transmute!({
+ ctr += 1;
+ 0usize
+ })
+ .unwrap();
+ assert_eq!(ctr, 1);
+ }
+
+ #[test]
+ fn test_include_value() {
+ const AS_U32: u32 = include_value!("../testdata/include_value/data");
+ assert_eq!(AS_U32, u32::from_ne_bytes([b'a', b'b', b'c', b'd']));
+ const AS_I32: i32 = include_value!("../testdata/include_value/data");
+ assert_eq!(AS_I32, i32::from_ne_bytes([b'a', b'b', b'c', b'd']));
+ }
+
+ #[test]
+ #[allow(non_camel_case_types, unreachable_pub, dead_code)]
+ fn test_cryptocorrosion_derive_traits() {
+ // Test the set of invocations added in
+ // https://github.com/cryptocorrosion/cryptocorrosion/pull/85
+
+ fn assert_impls<T: FromBytes + IntoBytes + Immutable>() {}
+
+ cryptocorrosion_derive_traits! {
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ pub union vec128_storage {
+ d: [u32; 4],
+ q: [u64; 2],
+ }
+ }
+
+ assert_impls::<vec128_storage>();
+
+ cryptocorrosion_derive_traits! {
+ #[repr(transparent)]
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ pub struct u32x4_generic([u32; 4]);
+ }
+
+ assert_impls::<u32x4_generic>();
+
+ cryptocorrosion_derive_traits! {
+ #[repr(transparent)]
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ pub struct u64x2_generic([u64; 2]);
+ }
+
+ assert_impls::<u64x2_generic>();
+
+ cryptocorrosion_derive_traits! {
+ #[repr(transparent)]
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ pub struct u128x1_generic([u128; 1]);
+ }
+
+ assert_impls::<u128x1_generic>();
+
+ cryptocorrosion_derive_traits! {
+ #[repr(transparent)]
+ #[derive(Copy, Clone, Default)]
+ #[allow(non_camel_case_types)]
+ pub struct x2<W, G>(pub [W; 2], PhantomData<G>);
+ }
+
+ enum NotZerocopy {}
+ assert_impls::<x2<(), NotZerocopy>>();
+
+ cryptocorrosion_derive_traits! {
+ #[repr(transparent)]
+ #[derive(Copy, Clone, Default)]
+ #[allow(non_camel_case_types)]
+ pub struct x4<W>(pub [W; 4]);
+ }
+
+ assert_impls::<x4<()>>();
+
+ #[cfg(feature = "simd")]
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ {
+ #[cfg(target_arch = "x86")]
+ use core::arch::x86::{__m128i, __m256i};
+ #[cfg(target_arch = "x86_64")]
+ use core::arch::x86_64::{__m128i, __m256i};
+
+ cryptocorrosion_derive_traits! {
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ pub struct X4(__m128i, __m128i, __m128i, __m128i);
+ }
+
+ assert_impls::<X4>();
+
+ cryptocorrosion_derive_traits! {
+ #[repr(C)]
+ /// Generic wrapper for unparameterized storage of any of the
+ /// possible impls. Converting into and out of this type should
+ /// be essentially free, although it may be more aligned than a
+ /// particular impl requires.
+ #[allow(non_camel_case_types)]
+ #[derive(Copy, Clone)]
+ pub union vec128_storage {
+ u32x4: [u32; 4],
+ u64x2: [u64; 2],
+ u128x1: [u128; 1],
+ sse2: __m128i,
+ }
+ }
+
+ assert_impls::<vec128_storage>();
+
+ cryptocorrosion_derive_traits! {
+ #[repr(transparent)]
+ #[allow(non_camel_case_types)]
+ #[derive(Copy, Clone)]
+ pub struct vec<S3, S4, NI> {
+ x: __m128i,
+ s3: PhantomData<S3>,
+ s4: PhantomData<S4>,
+ ni: PhantomData<NI>,
+ }
+ }
+
+ assert_impls::<vec<NotZerocopy, NotZerocopy, NotZerocopy>>();
+
+ cryptocorrosion_derive_traits! {
+ #[repr(transparent)]
+ #[derive(Copy, Clone)]
+ pub struct u32x4x2_avx2<NI> {
+ x: __m256i,
+ ni: PhantomData<NI>,
+ }
+ }
+
+ assert_impls::<u32x4x2_avx2<NotZerocopy>>();
+ }
+
+ // Make sure that our derive works for `#[repr(C)]` structs even though
+ // cryptocorrosion doesn't currently have any.
+ cryptocorrosion_derive_traits! {
+ #[repr(C)]
+ #[derive(Copy, Clone, Debug, PartialEq)]
+ pub struct ReprC(u8, u8, u16);
+ }
+ }
+}