diff --git a/README.md b/README.md index 091e1fda21..099c1953b2 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Zerocopy provides three macros for safe, zero-cost casting between types: the same size - `transmute_mut` converts a mutable reference of one type to a mutable reference of another type of the same size -- `transmute_ref` converts transmutes a mutable or immutable reference +- `transmute_ref` converts a mutable or immutable reference of one type to an immutable reference of another type of the same size These macros perform *compile-time* alignment and size checks, but cannot be diff --git a/src/error.rs b/src/error.rs index 2c6c3eb148..f21781c337 100644 --- a/src/error.rs +++ b/src/error.rs @@ -239,7 +239,7 @@ impl From> for ConvertError { /// The source value involved in the conversion. - src: Src, + pub(crate) src: Src, /// The inner destination type inolved in the conversion. dst: PhantomData, } diff --git a/src/lib.rs b/src/lib.rs index c61a87af28..83efb7f20b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -58,7 +58,7 @@ //! the same size //! - [`transmute_mut`] converts a mutable reference of one type to a mutable //! reference of another type of the same size -//! - [`transmute_ref`] converts transmutes a mutable or immutable reference +//! - [`transmute_ref`] converts a mutable or immutable reference //! of one type to an immutable reference of another type of the same size //! //! These macros perform *compile-time* alignment and size checks, but cannot be @@ -4409,6 +4409,70 @@ macro_rules! 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 +/// const fn try_transmute(src: Src) -> Result> +/// where +/// Src: IntoBytes, +/// Dst: TryFromBytes, +/// size_of::() == size_of::(), +/// { +/// # /* +/// ... +/// # */ +/// } +/// ``` +/// +/// 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::try_transmute; +/// assert_eq!(try_transmute!(0u8), Ok(false)); +/// assert_eq!(try_transmute!(1u8), Ok(true)); +/// +/// let maybe_bool: Result = try_transmute!(255u8); +/// assert_eq!(maybe_bool.unwrap_err().into_src(), 255u8); +/// +/// ``` +#[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: It's okay to transmute a type to itself. + #[allow(clippy::useless_transmute, clippy::missing_transmute_annotations)] + $crate::macro_util::core_reexport::mem::transmute(e) + }) + } else { + $crate::macro_util::try_transmute::<_, _>(e) + } + }} +} + /// 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 @@ -5607,6 +5671,50 @@ mod tests { 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 = try_transmute!(UnsafeCell::new(1usize)); + assert_eq!(x.unwrap(), 1); + let x: Result, _> = try_transmute!(1usize); + assert_eq!(x.unwrap().into_inner(), 1); + let x: Result, _> = try_transmute!(UnsafeCell::new(1usize)); + assert_eq!(x.unwrap().into_inner(), 1); + + #[derive(FromBytes, IntoBytes, Debug, PartialEq)] + #[repr(transparent)] + struct PanicOnDrop(T); + + impl Drop for PanicOnDrop { + 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 = 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 = 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_transmute_mut() { // Test that memory is transmuted as expected. diff --git a/src/macro_util.rs b/src/macro_util.rs index fca70be7a4..4cf9585716 100644 --- a/src/macro_util.rs +++ b/src/macro_util.rs @@ -24,6 +24,14 @@ use core::{marker::PhantomData, mem::ManuallyDrop}; #[cfg(__INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)] use core::ptr::{self, NonNull}; +use crate::{ + pointer::{ + invariant::{self, AtLeast, Invariants}, + AliasingSafe, AliasingSafeReason, BecauseExclusive, + }, + IntoBytes, Ptr, TryFromBytes, ValidityError, +}; + /// A compile-time check that should be one particular value. pub trait ShouldBe {} @@ -409,6 +417,86 @@ pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>( unsafe { &mut *dst } } +/// Is a given source a valid instance of `Self`? +/// +/// # Safety +/// +/// Unsafe code may assume that, if `is_mut_src_valid(src)` returns true, `*src` +/// is a bit-valid instance of `Dst`, and that the size of `Src` is greater than +/// or equal to the size of `Dst`. +/// +/// # Panics +/// +/// `is_src_valid` may either produce a post-monomorphization error or a panic +/// if `Dst` is bigger than `Src`. Otherwise, `is_src_valid` panics under the +/// same circumstances as [`is_bit_valid`]. +/// +/// [`is_bit_valid`]: TryFromBytes::is_bit_valid +#[doc(hidden)] +#[inline] +fn is_src_valid(src: Ptr<'_, Src, I>) -> bool +where + Src: IntoBytes, + Dst: TryFromBytes + AliasingSafe, + I: Invariants, + I::Aliasing: AtLeast, + R: AliasingSafeReason, +{ + crate::util::assert_dst_not_bigger_than_src::(); + + // SAFETY: This is a pointer cast, satisfying the following properties: + // - `p as *mut Dst` addresses a subset of the `bytes` addressed by `src`, + // because we assert above that the size of `Dst` is less than or equal to + // the size of `Src`. + // - `p as *mut Dst` is a provenance-preserving cast + // - Because `Dst: AliasingSafe`, either: + // - `I::Aliasing` is `Exclusive` + // - `Src` and `Dst` are both `Immutable`, in which case they + // trivially contain `UnsafeCell`s at identical locations + #[allow(clippy::as_conversions)] + let c_ptr = unsafe { src.cast_unsized(|p| p as *mut Dst) }; + + // SAFETY: `c_ptr` is derived from `src` which is `IntoBytes`. By + // invariant on `IntoByte`s, `c_ptr`'s referent consists entirely of + // initialized bytes. + let c_ptr = unsafe { c_ptr.assume_initialized() }; + + Dst::is_bit_valid(c_ptr) +} + +/// Attempts to transmute `Src` into `Dst`. +/// +/// A helper for `try_transmute!`. +/// +/// # Panics +/// +/// `try_transmute` may either produce a post-monomorphization error or a panic +/// if `Dst` is bigger than `Src`. Otherwise, `try_transmute` panics under the +/// same circumstances as [`is_bit_valid`]. +/// +/// [`is_bit_valid`]: TryFromBytes::is_bit_valid +#[inline(always)] +pub fn try_transmute(mut src: Src) -> Result> +where + Src: IntoBytes, + Dst: TryFromBytes, +{ + if !is_src_valid::(Ptr::from_mut(&mut src)) { + return Err(ValidityError::new(src)); + } + + let src = ManuallyDrop::new(src); + + // SAFETY: By contract on `is_src_valid`, we have confirmed both that `Dst` + // is no larger than `Src`, and that `src` is a bit-valid instance of `Dst`. + // These conditions are preserved through the `ManuallyDrop` wrapper, + // which is documented to have identical and layout bit validity to its + // inner value [1]. + // + // [1]: https://doc.rust-lang.org/std/mem/struct.ManuallyDrop.html + Ok(unsafe { core::mem::transmute_copy(&*src) }) +} + /// A function which emits a warning if its return value is not used. #[must_use] #[inline(always)] diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs index db37d87ef8..1533eb9efd 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -11,7 +11,7 @@ mod aliasing_safety; mod ptr; -pub use aliasing_safety::{AliasingSafe, BecauseExclusive, BecauseImmutable}; +pub use aliasing_safety::{AliasingSafe, AliasingSafeReason, BecauseExclusive, BecauseImmutable}; pub use ptr::{invariant, Ptr}; use crate::Unaligned; diff --git a/src/util.rs b/src/util.rs index 083cd6a1d8..9463564614 100644 --- a/src/util.rs +++ b/src/util.rs @@ -607,6 +607,23 @@ where const_assert!(::DST_IS_NOT_ZST); } +/// Assert at compile time that the size of `Dst` <= the size of `Src`. +pub(crate) const fn assert_dst_not_bigger_than_src() { + trait ConstAssert { + const DST_NOT_BIGGER_THAN_SRC: bool; + } + + impl ConstAssert for (Src, Dst) { + const DST_NOT_BIGGER_THAN_SRC: bool = { + let dst_bigger_than_src = mem::size_of::() > mem::size_of::(); + const_assert!(!dst_bigger_than_src); + !dst_bigger_than_src + }; + } + + const_assert!(<(Src, Dst) as ConstAssert>::DST_NOT_BIGGER_THAN_SRC); +} + /// Since we support multiple versions of Rust, there are often features which /// have been stabilized in the most recent stable release which do not yet /// exist (stably) on our MSRV. This module provides polyfills for those diff --git a/tests/ui-msrv/try_transmute-dst-not-tryfrombytes.rs b/tests/ui-msrv/try_transmute-dst-not-tryfrombytes.rs new file mode 120000 index 0000000000..08c7216b1e --- /dev/null +++ b/tests/ui-msrv/try_transmute-dst-not-tryfrombytes.rs @@ -0,0 +1 @@ +../ui-nightly/try_transmute-dst-not-tryfrombytes.rs \ No newline at end of file diff --git a/tests/ui-msrv/try_transmute-dst-not-tryfrombytes.stderr b/tests/ui-msrv/try_transmute-dst-not-tryfrombytes.stderr new file mode 100644 index 0000000000..cb2b5857f4 --- /dev/null +++ b/tests/ui-msrv/try_transmute-dst-not-tryfrombytes.stderr @@ -0,0 +1,37 @@ +error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied + --> tests/ui-msrv/try_transmute-dst-not-tryfrombytes.rs:17:58 + | +17 | let dst_not_try_from_bytes: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotZerocopy` + | +note: required by a bound in `try_transmute` + --> src/macro_util.rs + | + | Dst: TryFromBytes, + | ^^^^^^^^^^^^ required by this bound in `try_transmute` + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied + --> tests/ui-msrv/try_transmute-dst-not-tryfrombytes.rs:17:33 + | +17 | let dst_not_try_from_bytes: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotZerocopy` + | +note: required by a bound in `ValidityError` + --> src/error.rs + | + | pub struct ValidityError { + | ^^^^^^^^^^^^ required by this bound in `ValidityError` + +error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied + --> tests/ui-msrv/try_transmute-dst-not-tryfrombytes.rs:17:58 + | +17 | let dst_not_try_from_bytes: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotZerocopy` + | +note: required by a bound in `ValidityError` + --> src/error.rs + | + | pub struct ValidityError { + | ^^^^^^^^^^^^ required by this bound in `ValidityError` + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/try_transmute-size-decrease.rs b/tests/ui-msrv/try_transmute-size-decrease.rs new file mode 120000 index 0000000000..b1c5fef2e1 --- /dev/null +++ b/tests/ui-msrv/try_transmute-size-decrease.rs @@ -0,0 +1 @@ +../ui-nightly/try_transmute-size-decrease.rs \ No newline at end of file diff --git a/tests/ui-msrv/try_transmute-size-decrease.stderr b/tests/ui-msrv/try_transmute-size-decrease.stderr new file mode 100644 index 0000000000..6817bc92cc --- /dev/null +++ b/tests/ui-msrv/try_transmute-size-decrease.stderr @@ -0,0 +1,17 @@ +warning: unused variable: `decrease_size` + --> tests/ui-msrv/try_transmute-size-decrease.rs:19:9 + | +19 | let decrease_size: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_decrease_size` + | + = note: `#[warn(unused_variables)]` on by default + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/try_transmute-size-decrease.rs:19:40 + | +19 | let decrease_size: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AU16` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/try_transmute-size-increase.rs b/tests/ui-msrv/try_transmute-size-increase.rs new file mode 120000 index 0000000000..0f06fd425c --- /dev/null +++ b/tests/ui-msrv/try_transmute-size-increase.rs @@ -0,0 +1 @@ +../ui-nightly/try_transmute-size-increase.rs \ No newline at end of file diff --git a/tests/ui-msrv/try_transmute-size-increase.stderr b/tests/ui-msrv/try_transmute-size-increase.stderr new file mode 100644 index 0000000000..c66289ff9d --- /dev/null +++ b/tests/ui-msrv/try_transmute-size-increase.stderr @@ -0,0 +1,17 @@ +warning: unused variable: `increase_size` + --> tests/ui-msrv/try_transmute-size-increase.rs:19:9 + | +19 | let increase_size: Result = try_transmute!(0u8); + | ^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_increase_size` + | + = note: `#[warn(unused_variables)]` on by default + +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/try_transmute-size-increase.rs:19:42 + | +19 | let increase_size: Result = try_transmute!(0u8); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `AU16` (16 bits) + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/try_transmute-src-not-intobytes.rs b/tests/ui-msrv/try_transmute-src-not-intobytes.rs new file mode 120000 index 0000000000..25697a9938 --- /dev/null +++ b/tests/ui-msrv/try_transmute-src-not-intobytes.rs @@ -0,0 +1 @@ +../ui-nightly/try_transmute-src-not-intobytes.rs \ No newline at end of file diff --git a/tests/ui-msrv/try_transmute-src-not-intobytes.stderr b/tests/ui-msrv/try_transmute-src-not-intobytes.stderr new file mode 100644 index 0000000000..ef90d9ff70 --- /dev/null +++ b/tests/ui-msrv/try_transmute-src-not-intobytes.stderr @@ -0,0 +1,12 @@ +error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not satisfied + --> tests/ui-msrv/try_transmute-src-not-intobytes.rs:18:47 + | +18 | let src_not_into_bytes: Result = try_transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::IntoBytes` is not implemented for `NotZerocopy` + | +note: required by a bound in `try_transmute` + --> src/macro_util.rs + | + | Src: IntoBytes, + | ^^^^^^^^^ required by this bound in `try_transmute` + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/try_transmute-dst-not-tryfrombytes.rs b/tests/ui-nightly/try_transmute-dst-not-tryfrombytes.rs new file mode 100644 index 0000000000..0658bccf15 --- /dev/null +++ b/tests/ui-nightly/try_transmute-dst-not-tryfrombytes.rs @@ -0,0 +1,18 @@ +// Copyright 2022 The Fuchsia Authors +// +// Licensed under a BSD-style license , Apache License, Version 2.0 +// , or the MIT +// license , at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/include.rs"); + +extern crate zerocopy; + +use util::{NotZerocopy, AU16}; +use zerocopy::try_transmute; + +fn main() { + let dst_not_try_from_bytes: Result = try_transmute!(AU16(0)); +} diff --git a/tests/ui-nightly/try_transmute-dst-not-tryfrombytes.stderr b/tests/ui-nightly/try_transmute-dst-not-tryfrombytes.stderr new file mode 100644 index 0000000000..f15626848c --- /dev/null +++ b/tests/ui-nightly/try_transmute-dst-not-tryfrombytes.stderr @@ -0,0 +1,70 @@ +error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied + --> tests/ui-nightly/try_transmute-dst-not-tryfrombytes.rs:17:33 + | +17 | let dst_not_try_from_bytes: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotZerocopy` + | + = help: the following other types implement trait `TryFromBytes`: + () + *const T + *mut T + AU16 + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + and $N others +note: required by a bound in `ValidityError` + --> src/error.rs + | + | pub struct ValidityError { + | ^^^^^^^^^^^^ required by this bound in `ValidityError` + +error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied + --> tests/ui-nightly/try_transmute-dst-not-tryfrombytes.rs:17:58 + | +17 | let dst_not_try_from_bytes: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotZerocopy` + | + = help: the following other types implement trait `TryFromBytes`: + () + *const T + *mut T + AU16 + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + and $N others +note: required by a bound in `try_transmute` + --> src/macro_util.rs + | + | pub fn try_transmute(mut src: Src) -> Result> + | ------------- required by a bound in this function +... + | Dst: TryFromBytes, + | ^^^^^^^^^^^^ required by this bound in `try_transmute` + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied + --> tests/ui-nightly/try_transmute-dst-not-tryfrombytes.rs:17:58 + | +17 | let dst_not_try_from_bytes: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotZerocopy` + | + = help: the following other types implement trait `TryFromBytes`: + () + *const T + *mut T + AU16 + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + and $N others +note: required by a bound in `ValidityError` + --> src/error.rs + | + | pub struct ValidityError { + | ^^^^^^^^^^^^ required by this bound in `ValidityError` + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/try_transmute-size-decrease.rs b/tests/ui-nightly/try_transmute-size-decrease.rs new file mode 100644 index 0000000000..f40f15a11b --- /dev/null +++ b/tests/ui-nightly/try_transmute-size-decrease.rs @@ -0,0 +1,20 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license , Apache License, Version 2.0 +// , or the MIT +// license , at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/include.rs"); + +extern crate zerocopy; + +use util::AU16; +use zerocopy::try_transmute; + +// Although this is not a soundness requirement, we currently require that the +// size of the destination type is not smaller than the size of the source type. +fn main() { + let decrease_size: Result = try_transmute!(AU16(0)); +} diff --git a/tests/ui-nightly/try_transmute-size-decrease.stderr b/tests/ui-nightly/try_transmute-size-decrease.stderr new file mode 100644 index 0000000000..e415e6abe7 --- /dev/null +++ b/tests/ui-nightly/try_transmute-size-decrease.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/try_transmute-size-decrease.rs:19:40 + | +19 | let decrease_size: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AU16` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/try_transmute-size-increase.rs b/tests/ui-nightly/try_transmute-size-increase.rs new file mode 100644 index 0000000000..524fdb8d4d --- /dev/null +++ b/tests/ui-nightly/try_transmute-size-increase.rs @@ -0,0 +1,20 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license , Apache License, Version 2.0 +// , or the MIT +// license , at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/include.rs"); + +extern crate zerocopy; + +use util::AU16; +use zerocopy::try_transmute; + +// `try_transmute!` does not support transmuting from a smaller type to a larger +// one. +fn main() { + let increase_size: Result = try_transmute!(0u8); +} diff --git a/tests/ui-nightly/try_transmute-size-increase.stderr b/tests/ui-nightly/try_transmute-size-increase.stderr new file mode 100644 index 0000000000..34bf863a56 --- /dev/null +++ b/tests/ui-nightly/try_transmute-size-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/try_transmute-size-increase.rs:19:42 + | +19 | let increase_size: Result = try_transmute!(0u8); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `AU16` (16 bits) + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/try_transmute-src-not-intobytes.rs b/tests/ui-nightly/try_transmute-src-not-intobytes.rs new file mode 100644 index 0000000000..c2a7b41732 --- /dev/null +++ b/tests/ui-nightly/try_transmute-src-not-intobytes.rs @@ -0,0 +1,19 @@ +// Copyright 2023 The Fuchsia Authors +// +// Licensed under a BSD-style license , Apache License, Version 2.0 +// , or the MIT +// license , at your option. +// This file may not be copied, modified, or distributed except according to +// those terms. + +include!("../../zerocopy-derive/tests/include.rs"); + +extern crate zerocopy; + +use util::{NotZerocopy, AU16}; +use zerocopy::try_transmute; + +fn main() { + // `try_transmute` requires that the source type implements `IntoBytes` + let src_not_into_bytes: Result = try_transmute!(NotZerocopy(AU16(0))); +} diff --git a/tests/ui-nightly/try_transmute-src-not-intobytes.stderr b/tests/ui-nightly/try_transmute-src-not-intobytes.stderr new file mode 100644 index 0000000000..aef2978b84 --- /dev/null +++ b/tests/ui-nightly/try_transmute-src-not-intobytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not satisfied + --> tests/ui-nightly/try_transmute-src-not-intobytes.rs:18:47 + | +18 | let src_not_into_bytes: Result = try_transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::IntoBytes` is not implemented for `NotZerocopy` + | + = help: the following other types implement trait `zerocopy::IntoBytes`: + () + AU16 + AtomicBool + AtomicI16 + AtomicI32 + AtomicI8 + AtomicIsize + AtomicU16 + and $N others +note: required by a bound in `try_transmute` + --> src/macro_util.rs + | + | pub fn try_transmute(mut src: Src) -> Result> + | ------------- required by a bound in this function + | where + | Src: IntoBytes, + | ^^^^^^^^^ required by this bound in `try_transmute` + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/try_transmute-dst-not-tryfrombytes.rs b/tests/ui-stable/try_transmute-dst-not-tryfrombytes.rs new file mode 120000 index 0000000000..08c7216b1e --- /dev/null +++ b/tests/ui-stable/try_transmute-dst-not-tryfrombytes.rs @@ -0,0 +1 @@ +../ui-nightly/try_transmute-dst-not-tryfrombytes.rs \ No newline at end of file diff --git a/tests/ui-stable/try_transmute-dst-not-tryfrombytes.stderr b/tests/ui-stable/try_transmute-dst-not-tryfrombytes.stderr new file mode 100644 index 0000000000..d27b28894c --- /dev/null +++ b/tests/ui-stable/try_transmute-dst-not-tryfrombytes.stderr @@ -0,0 +1,70 @@ +error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied + --> tests/ui-stable/try_transmute-dst-not-tryfrombytes.rs:17:33 + | +17 | let dst_not_try_from_bytes: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotZerocopy` + | + = help: the following other types implement trait `TryFromBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `ValidityError` + --> src/error.rs + | + | pub struct ValidityError { + | ^^^^^^^^^^^^ required by this bound in `ValidityError` + +error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied + --> tests/ui-stable/try_transmute-dst-not-tryfrombytes.rs:17:58 + | +17 | let dst_not_try_from_bytes: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotZerocopy` + | + = help: the following other types implement trait `TryFromBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `try_transmute` + --> src/macro_util.rs + | + | pub fn try_transmute(mut src: Src) -> Result> + | ------------- required by a bound in this function +... + | Dst: TryFromBytes, + | ^^^^^^^^^^^^ required by this bound in `try_transmute` + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the trait bound `NotZerocopy: TryFromBytes` is not satisfied + --> tests/ui-stable/try_transmute-dst-not-tryfrombytes.rs:17:58 + | +17 | let dst_not_try_from_bytes: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `TryFromBytes` is not implemented for `NotZerocopy` + | + = help: the following other types implement trait `TryFromBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `ValidityError` + --> src/error.rs + | + | pub struct ValidityError { + | ^^^^^^^^^^^^ required by this bound in `ValidityError` + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/try_transmute-size-decrease.rs b/tests/ui-stable/try_transmute-size-decrease.rs new file mode 120000 index 0000000000..b1c5fef2e1 --- /dev/null +++ b/tests/ui-stable/try_transmute-size-decrease.rs @@ -0,0 +1 @@ +../ui-nightly/try_transmute-size-decrease.rs \ No newline at end of file diff --git a/tests/ui-stable/try_transmute-size-decrease.stderr b/tests/ui-stable/try_transmute-size-decrease.stderr new file mode 100644 index 0000000000..891598f154 --- /dev/null +++ b/tests/ui-stable/try_transmute-size-decrease.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/try_transmute-size-decrease.rs:19:40 + | +19 | let decrease_size: Result = try_transmute!(AU16(0)); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `AU16` (16 bits) + = note: target type: `u8` (8 bits) + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/try_transmute-size-increase.rs b/tests/ui-stable/try_transmute-size-increase.rs new file mode 120000 index 0000000000..0f06fd425c --- /dev/null +++ b/tests/ui-stable/try_transmute-size-increase.rs @@ -0,0 +1 @@ +../ui-nightly/try_transmute-size-increase.rs \ No newline at end of file diff --git a/tests/ui-stable/try_transmute-size-increase.stderr b/tests/ui-stable/try_transmute-size-increase.stderr new file mode 100644 index 0000000000..ca628ed12a --- /dev/null +++ b/tests/ui-stable/try_transmute-size-increase.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/try_transmute-size-increase.rs:19:42 + | +19 | let increase_size: Result = try_transmute!(0u8); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `AU16` (16 bits) + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/try_transmute-src-not-intobytes.rs b/tests/ui-stable/try_transmute-src-not-intobytes.rs new file mode 120000 index 0000000000..25697a9938 --- /dev/null +++ b/tests/ui-stable/try_transmute-src-not-intobytes.rs @@ -0,0 +1 @@ +../ui-nightly/try_transmute-src-not-intobytes.rs \ No newline at end of file diff --git a/tests/ui-stable/try_transmute-src-not-intobytes.stderr b/tests/ui-stable/try_transmute-src-not-intobytes.stderr new file mode 100644 index 0000000000..11040235fa --- /dev/null +++ b/tests/ui-stable/try_transmute-src-not-intobytes.stderr @@ -0,0 +1,25 @@ +error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not satisfied + --> tests/ui-stable/try_transmute-src-not-intobytes.rs:18:47 + | +18 | let src_not_into_bytes: Result = try_transmute!(NotZerocopy(AU16(0))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::IntoBytes` is not implemented for `NotZerocopy` + | + = help: the following other types implement trait `zerocopy::IntoBytes`: + bool + char + isize + i8 + i16 + i32 + i64 + i128 + and $N others +note: required by a bound in `try_transmute` + --> src/macro_util.rs + | + | pub fn try_transmute(mut src: Src) -> Result> + | ------------- required by a bound in this function + | where + | Src: IntoBytes, + | ^^^^^^^^^ required by this bound in `try_transmute` + = note: this error originates in the macro `try_transmute` (in Nightly builds, run with -Z macro-backtrace for more info)