Repository: Lokathor/bytemuck
Pull request #59 (2021-03-26 22:56:10)

Implement Pod and Zeroable for arrays of any size
Starting with Rust 1.51 it is possible to use const generics in limited way.
But it's sufficient to implement traits for arrays of all sizes.

Lokathor: I agree that we should do this, but I think it should be via explicit opt-in (a feature such as `rustc_1_51`), not automatic detection.
zakarumych: Automatic detection would allow users to not bother about it in the future.

For instance, I wouldn't like to go and activate 'rust_1_26' in older crates to make them implement some traits for u128, especially if those are not my direct dependencies. I like to simply assume they all support it already.

This is how serde handles new API and it works great.


Patch:
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..6a6cadc
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,45 @@
+use std::{
+  env,
+  process::Command,
+  str::{self, FromStr},
+};
+
+fn main() {
+  let minor = match rustc_minor_version() {
+    Some(minor) => minor,
+    None => return,
+  };
+
+  if minor >= 51 {
+    println!("cargo:rustc-cfg=min_const_generics");
+  }
+}
+
+fn rustc_minor_version() -> Option<u32> {
+  let rustc = match env::var_os("RUSTC") {
+    Some(rustc) => rustc,
+    None => return None,
+  };
+
+  let output = match Command::new(rustc).arg("--version").output() {
+    Ok(output) => output,
+    Err(_) => return None,
+  };
+
+  let version = match str::from_utf8(&output.stdout) {
+    Ok(version) => version,
+    Err(_) => return None,
+  };
+
+  let mut pieces = version.split('.');
+  if pieces.next() != Some("rustc 1") {
+    return None;
+  }
+
+  let next = match pieces.next() {
+    Some(next) => next,
+    None => return None,
+  };
+
+  u32::from_str(next).ok()
+}
diff --git a/rustfmt.toml b/rustfmt.toml
index f214385..689ceb1 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -12,5 +12,5 @@ use_small_heuristics = "Max"
 
 # Unstable
 format_code_in_doc_comments = true
-merge_imports = true
+imports_granularity = "Crate"
 wrap_comments = true
diff --git a/src/contiguous.rs b/src/contiguous.rs
index 30709a7..b60f4a4 100644
--- a/src/contiguous.rs
+++ b/src/contiguous.rs
@@ -85,8 +85,8 @@ pub unsafe trait Contiguous: Copy + 'static {
   /// `#[repr(Int)]` or `#[repr(C)]` attribute, (if it does not, it is
   /// *unsound* to implement `Contiguous`!).
   ///
-  /// - For `#[repr(Int)]`, use the listed `Int`. e.g. `#[repr(u8)]` should
-  ///   use `type Int = u8`.
+  /// - For `#[repr(Int)]`, use the listed `Int`. e.g. `#[repr(u8)]` should use
+  ///   `type Int = u8`.
   ///
   /// - For `#[repr(C)]`, use whichever type the C compiler will use to
   ///   represent the given enum. This is usually `c_int` (from `std::os::raw`
diff --git a/src/lib.rs b/src/lib.rs
index c22daaa..1e783a5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -50,6 +50,7 @@ use core::{marker::*, mem::*, num::*, ptr::*};
 #[doc(hidden)]
 pub use ::core as __core;
 
+#[cfg(not(generic_const))]
 macro_rules! impl_unsafe_marker_for_array {
   ( $marker:ident , $( $n:expr ),* ) => {
     $(unsafe impl<T> $marker for [T; $n] where T: $marker {})*
diff --git a/src/offset_of.rs b/src/offset_of.rs
index 604379a..7e8aedf 100644
--- a/src/offset_of.rs
+++ b/src/offset_of.rs
@@ -25,7 +25,9 @@
 /// # use bytemuck::offset_of;
 /// // enums can't derive default, and for this example we don't pick one
 /// enum MyExampleEnum {
-///   A, B, C,
+///   A,
+///   B,
+///   C,
 /// }
 ///
 /// // so now our struct here doesn't have Default
@@ -65,11 +67,11 @@
 /// [rust-lang/rust#27060]: https://github.com/rust-lang/rust/issues/27060
 ///
 /// <p style="background:rgba(255,181,77,0.16);padding:0.75em;">
-/// <strong>Warning:</strong> This is only true for versions of bytemuck > 1.4.0.
-/// Previous versions of
+/// <strong>Warning:</strong> This is only true for versions of bytemuck >
+/// 1.4.0. Previous versions of
 /// <code style="background:rgba(41,24,0,0.1);">bytemuck::offset_of!</code>
-/// will only emit a warning when used on the field of a packed struct in safe code,
-/// which can lead to unsoundness.
+/// will only emit a warning when used on the field of a packed struct in safe
+/// code, which can lead to unsoundness.
 /// </p>
 ///
 /// For example, the following will fail to compile:
@@ -91,7 +93,8 @@
 /// ```compile_fail
 /// # #[repr(C, packed)] #[derive(Default)] struct Example { field: u32 }
 /// // Still doesn't compile:
-/// #[allow(safe_packed_borrows)] {
+/// #[allow(safe_packed_borrows)]
+/// {
 ///   let _offset = bytemuck::offset_of!(Example, field);
 /// }
 /// ```
diff --git a/src/pod.rs b/src/pod.rs
index e5cc693..194e9fb 100644
--- a/src/pod.rs
+++ b/src/pod.rs
@@ -66,6 +66,10 @@ unsafe impl<T: Pod> Pod for ManuallyDrop<T> {}
 
 // Note(Lokathor): MaybeUninit can NEVER be Pod.
 
+#[cfg(min_const_generics)]
+unsafe impl<T, const N: usize> Pod for [T; N] where T: Pod {}
+
+#[cfg(not(min_const_generics))]
 impl_unsafe_marker_for_array!(
   Pod, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
   20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 48, 64, 96, 128, 256,
diff --git a/src/transparent.rs b/src/transparent.rs
index fa6d479..6fb53bc 100644
--- a/src/transparent.rs
+++ b/src/transparent.rs
@@ -1,38 +1,42 @@
 use super::*;
 
-/// A trait which indicates that a type is a `repr(transparent)` wrapper around
-/// the `Wrapped` value.
+/// A trait which indicates that a type is a `#[repr(transparent)]` wrapper
+/// around the `Inner` value.
 ///
-/// This allows safely creating references to `T` from those to the `Wrapped`
-/// type, using the `wrap_ref` and `wrap_mut` functions.
+/// This allows safely copy transmuting between the `Inner` type and the
+/// `TransparentWrapper` type.  
+/// Functions like `wrap_{}` convert from the inner
+/// type to the wrapper type and `unwrap_{}` functions do the inverse conversion
+/// from the wrapper type to the inner type.
 ///
 /// # Safety
 ///
 /// The safety contract of `TransparentWrapper` is relatively simple:
 ///
-/// For a given `Wrapper` which implements `TransparentWrapper<Wrapped>`:
+/// For a given `Wrapper` which implements `TransparentWrapper<Inner>`:
 ///
-/// 1. Wrapper must be a `#[repr(transparent)]` wrapper around `Wrapped`. This
-///    either means that it must be a `#[repr(transparent)]` struct which
-///    contains a either a field of type `Wrapped` (or a field of some other
-///    transparent wrapper for `Wrapped`) as the only non-ZST field.
+/// 1. `Wrapper` must be a wrapper around `Inner` with an identical data
+/// representations. This    either means that it must be a
+/// `#[repr(transparent)]` struct which    contains a either a field of type
+/// `Inner` (or a field of some other    transparent wrapper for `Inner`) as the
+/// only non-ZST field.
 ///
-/// 2. Any fields *other* than the `Wrapped` field must be trivially
+/// 2. Any fields *other* than the `Inner` field must be trivially
 ///    constructable ZSTs, for example `PhantomData`, `PhantomPinned`, etc.
 ///
 /// 3. The `Wrapper` may not impose additional alignment requirements over
-///    `Wrapped`.
+///    `Inner`.
 ///     - Note: this is currently guaranteed by `repr(transparent)`, but there
 ///       have been discussions of lifting it, so it's stated here explicitly.
 ///
-/// 4. The `wrap_ref` and `wrap_mut` functions on `TransparentWrapper` may not
-///    be overridden.
+/// 4. All functions on
+///    `TransparentWrapper` **may not** be overridden.
 ///
 /// ## Caveats
 ///
-/// If the wrapper imposes additional constraints upon the wrapped type which
+/// If the wrapper imposes additional constraints upon the inner type which
 /// are required for safety, it's responsible for ensuring those still hold --
-/// this generally requires preventing access to instances of the wrapped type,
+/// this generally requires preventing access to instances of the inner type,
 /// as implementing `TransparentWrapper<U> for T` means anybody can call
 /// `T::cast_ref(any_instance_of_u)`.
 ///
@@ -55,13 +59,13 @@ use super::*;
 ///
 /// // interpret a reference to &SomeStruct as a &MyWrapper
 /// let thing = SomeStruct::default();
-/// let wrapped_ref: &MyWrapper = MyWrapper::wrap_ref(&thing);
+/// let inner_ref: &MyWrapper = MyWrapper::wrap_ref(&thing);
 ///
 /// // Works with &mut too.
 /// let mut mut_thing = SomeStruct::default();
-/// let wrapped_mut: &mut MyWrapper = MyWrapper::wrap_mut(&mut mut_thing);
+/// let inner_mut: &mut MyWrapper = MyWrapper::wrap_mut(&mut mut_thing);
 ///
-/// # let _ = (wrapped_ref, wrapped_mut); // silence warnings
+/// # let _ = (inner_ref, inner_mut); // silence warnings
 /// ```
 ///
 /// ## Use with dynamically sized types
@@ -80,56 +84,166 @@ use super::*;
 /// let mut buf = [1, 2, 3u8];
 /// let sm = Slice::wrap_mut(&mut buf);
 /// ```
-pub unsafe trait TransparentWrapper<Wrapped: ?Sized> {
-  /// Convert a reference to a wrapped type into a reference to the wrapper.
-  ///
-  /// This is a trait method so that you can write `MyType::wrap_ref(...)` in
-  /// your code. It is part of the safety contract for this trait that if you
-  /// implement `TransparentWrapper<_>` for your type you **must not** override
-  /// this method.
+pub unsafe trait TransparentWrapper<Inner: ?Sized> {
+  /// Convert the inner type into the wrapper type.
   #[inline]
-  fn wrap_ref(s: &Wrapped) -> &Self {
+  fn wrap(s: Inner) -> Self
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    // SAFETY: The unsafe contract requires that `Self` and `Inner` have
+    // identical representations.
+    unsafe { transmute_copy(&s) }
+  }
+
+  /// Convert a reference to the inner type into a reference to the wrapper
+  /// type.
+  #[inline]
+  fn wrap_ref(s: &Inner) -> &Self {
     unsafe {
-      assert!(size_of::<*const Wrapped>() == size_of::<*const Self>());
-      // Using a pointer cast doesn't work here because rustc can't tell that the
-      // vtables match (if we lifted the ?Sized restriction, this would go away),
-      // and transmute doesn't work for the usual reasons it doesn't work inside
-      // generic functions.
+      assert!(size_of::<*const Inner>() == size_of::<*const Self>());
+      // A pointer cast does't work here because rustc can't tell that
+      // the vtables match (because of the `?Sized` restriction relaxation).
+      // A `transmute` doesn't work because the sizes are unspecified.
       //
-      // SAFETY: The unsafe contract requires that these have identical
-      // representations. Using this transmute_copy instead of transmute here is
-      // annoying, but is required as `Self` and `Wrapped` have unspecified
-      // sizes still.
-      let wrapped_ptr = s as *const Wrapped;
-      let wrapper_ptr: *const Self = transmute_copy(&wrapped_ptr);
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations.
+      let inner_ptr = s as *const Inner;
+      let wrapper_ptr: *const Self = transmute_copy(&inner_ptr);
       &*wrapper_ptr
     }
   }
 
-  /// Convert a mut reference to a wrapped type into a mut reference to the
-  /// wrapper.
-  ///
-  /// This is a trait method so that you can write `MyType::wrap_mut(...)` in
-  /// your code. It is part of the safety contract for this trait that if you implement
-  /// `TransparentWrapper<_>` for your type you **must not** override this method.
+  /// Convert a mutable reference to the inner type into a mutable reference to
+  /// the wrapper type.
   #[inline]
-  fn wrap_mut(s: &mut Wrapped) -> &mut Self {
+  fn wrap_mut(s: &mut Inner) -> &mut Self {
     unsafe {
-      assert!(size_of::<*mut Wrapped>() == size_of::<*mut Self>());
-      // Using a pointer cast doesn't work here because rustc can't tell that the
-      // vtables match (if we lifted the ?Sized restriction, this would go away),
-      // and transmute doesn't work for the usual reasons it doesn't work inside
-      // generic functions.
+      assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
+      // A pointer cast does't work here because rustc can't tell that
+      // the vtables match (because of the `?Sized` restriction relaxation).
+      // A `transmute` doesn't work because the sizes are unspecified.
       //
-      // SAFETY: The unsafe contract requires that these have identical
-      // representations. Using this transmute_copy instead of transmute here is
-      // annoying, but is required as `Self` and `Wrapped` have unspecified
-      // sizes still.
-      let wrapped_ptr = s as *mut Wrapped;
-      let wrapper_ptr: *mut Self = transmute_copy(&wrapped_ptr);
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations.
+      let inner_ptr = s as *mut Inner;
+      let wrapper_ptr: *mut Self = transmute_copy(&inner_ptr);
       &mut *wrapper_ptr
     }
   }
+
+  /// Convert a slice to the inner type into a slice to the wrapper type.
+  #[inline]
+  fn wrap_slice(s: &[Inner]) -> &[Self]
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    unsafe {
+      assert!(size_of::<*const Inner>() == size_of::<*const Self>());
+      assert!(align_of::<*const Inner>() == align_of::<*const Self>());
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations (size and alignment).
+      core::slice::from_raw_parts(s.as_ptr() as *const Self, s.len())
+    }
+  }
+
+  /// Convert a mutable slice to the inner type into a mutable slice to the
+  /// wrapper type.
+  #[inline]
+  fn wrap_slice_mut(s: &mut [Inner]) -> &mut [Self]
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    unsafe {
+      assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
+      assert!(align_of::<*mut Inner>() == align_of::<*mut Self>());
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations (size and alignment).
+      core::slice::from_raw_parts_mut(s.as_mut_ptr() as *mut Self, s.len())
+    }
+  }
+
+  /// Convert the wrapper type into the inner type.
+  #[inline]
+  fn unwrap(s: Self) -> Inner
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    unsafe { transmute_copy(&s) }
+  }
+
+  /// Convert a reference to the wrapper type into a reference to the inner
+  /// type.
+  #[inline]
+  fn unwrap_ref(s: &Self) -> &Inner {
+    unsafe {
+      assert!(size_of::<*const Inner>() == size_of::<*const Self>());
+      // A pointer cast does't work here because rustc can't tell that
+      // the vtables match (because of the `?Sized` restriction relaxation).
+      // A `transmute` doesn't work because the sizes are unspecified.
+      //
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations.
+      let wrapper_ptr = s as *const Self;
+      let inner_ptr: *const Inner = transmute_copy(&wrapper_ptr);
+      &*inner_ptr
+    }
+  }
+
+  /// Convert a mutable reference to the wrapper type into a mutable reference
+  /// to the inner type.
+  #[inline]
+  fn unwrap_mut(s: &mut Self) -> &mut Inner {
+    unsafe {
+      assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
+      // A pointer cast does't work here because rustc can't tell that
+      // the vtables match (because of the `?Sized` restriction relaxation).
+      // A `transmute` doesn't work because the sizes are unspecified.
+      //
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations.
+      let wrapper_ptr = s as *mut Self;
+      let inner_ptr: *mut Inner = transmute_copy(&wrapper_ptr);
+      &mut *inner_ptr
+    }
+  }
+
+  /// Convert a slice to the wrapped type into a slice to the inner type.
+  #[inline]
+  fn unwrap_slice(s: &[Self]) -> &[Inner]
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    unsafe {
+      assert!(size_of::<*const Inner>() == size_of::<*const Self>());
+      assert!(align_of::<*const Inner>() == align_of::<*const Self>());
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations (size and alignment).
+      core::slice::from_raw_parts(s.as_ptr() as *const Inner, s.len())
+    }
+  }
+
+  /// Convert a mutable slice to the wrapped type into a mutable slice to the
+  /// inner type.
+  #[inline]
+  fn unwrap_slice_mut(s: &mut [Self]) -> &mut [Inner]
+  where
+    Self: Sized,
+    Inner: Sized,
+  {
+    unsafe {
+      assert!(size_of::<*mut Inner>() == size_of::<*mut Self>());
+      assert!(align_of::<*mut Inner>() == align_of::<*mut Self>());
+      // SAFETY: The unsafe contract requires that these two have
+      // identical representations (size and alignment).
+      core::slice::from_raw_parts_mut(s.as_mut_ptr() as *mut Inner, s.len())
+    }
+  }
 }
 
 unsafe impl<T> TransparentWrapper<T> for core::num::Wrapping<T> {}
diff --git a/src/zeroable.rs b/src/zeroable.rs
index ebd412f..3beba1c 100644
--- a/src/zeroable.rs
+++ b/src/zeroable.rs
@@ -109,6 +109,10 @@ unsafe impl<
 {
 }
 
+#[cfg(min_const_generics)]
+unsafe impl<T, const N: usize> Zeroable for [T; N] where T: Zeroable {}
+
+#[cfg(not(min_const_generics))]
 impl_unsafe_marker_for_array!(
   Zeroable, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
   19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 48, 64, 96, 128, 256,


Test patch:
diff --git a/tests/cast_slice_tests.rs b/tests/cast_slice_tests.rs
index 9eb08c5..515a628 100644
--- a/tests/cast_slice_tests.rs
+++ b/tests/cast_slice_tests.rs
@@ -25,7 +25,8 @@ fn test_try_cast_slice() {
     Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
   );
 
-  // by taking one byte off the end, we're aligned but would have slop bytes for u32
+  // by taking one byte off the end, we're aligned but would have slop bytes for
+  // u32
   let the_bytes_len_minus1 = the_bytes.len() - 1;
   let slop_bytes = &the_bytes[..the_bytes_len_minus1];
   assert_eq!(
@@ -62,7 +63,8 @@ fn test_try_cast_slice_mut() {
     Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
   );
 
-  // by taking one byte off the end, we're aligned but would have slop bytes for u32
+  // by taking one byte off the end, we're aligned but would have slop bytes for
+  // u32
   let the_bytes_len_minus1 = the_bytes.len() - 1;
   let slop_bytes = &mut the_bytes[..the_bytes_len_minus1];
   assert_eq!(
@@ -92,7 +94,10 @@ fn test_types() {
 #[test]
 fn test_bytes_of() {
   assert_eq!(bytes_of(&0xaabbccdd_u32), &0xaabbccdd_u32.to_ne_bytes());
-  assert_eq!(bytes_of_mut(&mut 0xaabbccdd_u32), &mut 0xaabbccdd_u32.to_ne_bytes());
+  assert_eq!(
+    bytes_of_mut(&mut 0xaabbccdd_u32),
+    &mut 0xaabbccdd_u32.to_ne_bytes()
+  );
   let mut a = 0xaabbccdd_u32;
   let a_addr = &a as *const _ as usize;
   // ensure addresses match.
@@ -105,9 +110,18 @@ fn test_try_from_bytes() {
   let u32s = [0xaabbccdd, 0x11223344_u32];
   let bytes = bytemuck::cast_slice::<u32, u8>(&u32s);
   assert_eq!(try_from_bytes::<u32>(&bytes[..4]), Ok(&u32s[0]));
-  assert_eq!(try_from_bytes::<u32>(&bytes[..5]), Err(PodCastError::SizeMismatch));
-  assert_eq!(try_from_bytes::<u32>(&bytes[..3]), Err(PodCastError::SizeMismatch));
-  assert_eq!(try_from_bytes::<u32>(&bytes[1..5]), Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned));
+  assert_eq!(
+    try_from_bytes::<u32>(&bytes[..5]),
+    Err(PodCastError::SizeMismatch)
+  );
+  assert_eq!(
+    try_from_bytes::<u32>(&bytes[..3]),
+    Err(PodCastError::SizeMismatch)
+  );
+  assert_eq!(
+    try_from_bytes::<u32>(&bytes[1..5]),
+    Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
+  );
 }
 
 #[test]
@@ -117,9 +131,18 @@ fn test_try_from_bytes_mut() {
   let bytes = bytemuck::cast_slice_mut::<u32, u8>(&mut u32s);
   assert_eq!(try_from_bytes_mut::<u32>(&mut bytes[..4]), Ok(&mut abcd));
   assert_eq!(try_from_bytes_mut::<u32>(&mut bytes[..4]), Ok(&mut abcd));
-  assert_eq!(try_from_bytes_mut::<u32>(&mut bytes[..5]), Err(PodCastError::SizeMismatch));
-  assert_eq!(try_from_bytes_mut::<u32>(&mut bytes[..3]), Err(PodCastError::SizeMismatch));
-  assert_eq!(try_from_bytes::<u32>(&mut bytes[1..5]), Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned));
+  assert_eq!(
+    try_from_bytes_mut::<u32>(&mut bytes[..5]),
+    Err(PodCastError::SizeMismatch)
+  );
+  assert_eq!(
+    try_from_bytes_mut::<u32>(&mut bytes[..3]),
+    Err(PodCastError::SizeMismatch)
+  );
+  assert_eq!(
+    try_from_bytes::<u32>(&mut bytes[1..5]),
+    Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned)
+  );
 }
 
 #[test]
@@ -136,7 +159,10 @@ fn test_from_bytes_mut() {
   let a_addr = &a as *const _ as usize;
   let aligned_bytes = bytemuck::bytes_of_mut(&mut a);
   assert_eq!(*from_bytes_mut::<u32>(aligned_bytes), 0xaabbccdd_u32);
-  assert_eq!(from_bytes_mut::<u32>(aligned_bytes) as *const u32 as usize, a_addr);
+  assert_eq!(
+    from_bytes_mut::<u32>(aligned_bytes) as *const u32 as usize,
+    a_addr
+  );
 }
 
 // like #[should_panic], but can be a part of another test, instead of requiring
@@ -144,7 +170,10 @@ fn test_from_bytes_mut() {
 macro_rules! should_panic {
   ($ex:expr) => {
     assert!(
-      std::panic::catch_unwind(|| { let _ = $ex; }).is_err(),
+      std::panic::catch_unwind(|| {
+        let _ = $ex;
+      })
+      .is_err(),
       concat!("should have panicked: `", stringify!($ex), "`")
     );
   };
diff --git a/tests/derive.rs b/tests/derive.rs
index 1e1deeb..2524e6d 100644
--- a/tests/derive.rs
+++ b/tests/derive.rs
@@ -1,7 +1,7 @@
 #![cfg(feature = "derive")]
 #![allow(dead_code)]
 
-use bytemuck::{Zeroable, Pod, TransparentWrapper};
+use bytemuck::{Pod, TransparentWrapper, Zeroable};
 
 #[derive(Copy, Clone, Pod, Zeroable)]
 #[repr(C)]
@@ -21,5 +21,5 @@ struct TransparentSingle {
 #[transparent(u16)]
 struct TransparentWithZeroSized {
   a: u16,
-  b: ()
-}
\ No newline at end of file
+  b: (),
+}
diff --git a/tests/transparent.rs b/tests/transparent.rs
new file mode 100644
index 0000000..f0f9da8
--- /dev/null
+++ b/tests/transparent.rs
@@ -0,0 +1,64 @@
+// Currently this test doesn't actually check the output of the functions.
+// It's only here for miri to check for any potential undefined behaviour.
+// TODO: check function results
+
+#[test]
+fn test_transparent_wrapper() {
+  // An external type defined in a different crate.
+  #[derive(Copy, Clone, Default)]
+  struct Foreign(u8);
+
+  use bytemuck::TransparentWrapper;
+
+  #[derive(Copy, Clone)]
+  #[repr(transparent)]
+  struct Wrapper(Foreign);
+
+  unsafe impl TransparentWrapper<Foreign> for Wrapper {}
+
+  // Traits can be implemented on crate-local wrapper.
+  unsafe impl bytemuck::Zeroable for Wrapper {}
+  unsafe impl bytemuck::Pod for Wrapper {}
+
+  let _: u8 = bytemuck::cast(Wrapper::wrap(Foreign::default()));
+  let _: Foreign = Wrapper::unwrap(bytemuck::cast(u8::default()));
+
+  let _: &u8 = bytemuck::cast_ref(Wrapper::wrap_ref(&Foreign::default()));
+  let _: &Foreign = Wrapper::unwrap_ref(bytemuck::cast_ref(&u8::default()));
+
+  let _: &mut u8 =
+    bytemuck::cast_mut(Wrapper::wrap_mut(&mut Foreign::default()));
+  let _: &mut Foreign =
+    Wrapper::unwrap_mut(bytemuck::cast_mut(&mut u8::default()));
+
+  let _: &[u8] =
+    bytemuck::cast_slice(Wrapper::wrap_slice(&[Foreign::default()]));
+  let _: &[Foreign] =
+    Wrapper::unwrap_slice(bytemuck::cast_slice(&[u8::default()]));
+
+  let _: &mut [u8] =
+    bytemuck::cast_slice_mut(Wrapper::wrap_slice_mut(
+      &mut [Foreign::default()],
+    ));
+  let _: &mut [Foreign] =
+    Wrapper::unwrap_slice_mut(bytemuck::cast_slice_mut(&mut [u8::default()]));
+
+  let _: &[u8] = bytemuck::bytes_of(Wrapper::wrap_ref(&Foreign::default()));
+  let _: &Foreign = Wrapper::unwrap_ref(bytemuck::from_bytes(&[u8::default()]));
+
+  let _: &mut [u8] =
+    bytemuck::bytes_of_mut(Wrapper::wrap_mut(&mut Foreign::default()));
+  let _: &mut Foreign =
+    Wrapper::unwrap_mut(bytemuck::from_bytes_mut(&mut [u8::default()]));
+
+  // not sure if this is the right usage
+  let _ =
+    bytemuck::pod_align_to::<_, u8>(Wrapper::wrap_slice(&[Foreign::default()]));
+  // counterpart?
+
+  // not sure if this is the right usage
+  let _ = bytemuck::pod_align_to_mut::<_, u8>(Wrapper::wrap_slice_mut(&mut [
+    Foreign::default(),
+  ]));
+  // counterpart?
+}