diff --git a/src/impls.rs b/src/impls.rs index a040a79df7..faae852f1c 100644 --- a/src/impls.rs +++ b/src/impls.rs @@ -105,11 +105,13 @@ assert_unaligned!(bool); // pattern 0x01. const _: () = unsafe { unsafe_impl!(=> TryFromBytes for bool; |byte| { - let byte = byte.transmute::(); + let mut byte = byte; + let byte = byte.reborrow().into_shared().transmute::(); *byte.unaligned_as_ref() < 2 }) }; -impl_size_eq!(bool, u8); + +impl_size_from!(bool, u8); // SAFETY: // - `Immutable`: `char` self-evidently does not contain any `UnsafeCell`s. @@ -134,13 +136,14 @@ const _: () = unsafe { unsafe_impl!(char: Immutable, FromZeros, IntoBytes) }; // `char`. const _: () = unsafe { unsafe_impl!(=> TryFromBytes for char; |c| { - let c = c.transmute::, invariant::Valid, _>(); - let c = c.read_unaligned().into_inner(); + let mut c = c; + let c = c.reborrow().into_shared().transmute::, invariant::Valid, _>(); + let c = c.read_unaligned::().into_inner(); char::from_u32(c).is_some() }); }; -impl_size_eq!(char, Unalign); +impl_size_from!(char, Unalign); // SAFETY: Per the Reference [1], `str` has the same layout as `[u8]`. // - `Immutable`: `[u8]` does not contain any `UnsafeCell`s. @@ -167,22 +170,23 @@ const _: () = unsafe { unsafe_impl!(str: Immutable, FromZeros, IntoBytes, Unalig // Returns `Err` if the slice is not UTF-8. const _: () = unsafe { unsafe_impl!(=> TryFromBytes for str; |c| { - let c = c.transmute::<[u8], invariant::Valid, _>(); + let mut c = c; + let c = c.reborrow().into_shared().transmute::<[u8], invariant::Valid, _>(); let c = c.unaligned_as_ref(); core::str::from_utf8(c).is_ok() }) }; -impl_size_eq!(str, [u8]); +impl_size_from!(str, [u8]); macro_rules! unsafe_impl_try_from_bytes_for_nonzero { ($($nonzero:ident[$prim:ty]),*) => { $( unsafe_impl!(=> TryFromBytes for $nonzero; |n| { - impl_size_eq!($nonzero, Unalign<$prim>); - - let n = n.transmute::, invariant::Valid, _>(); - $nonzero::new(n.read_unaligned().into_inner()).is_some() + impl_size_from!($nonzero, Unalign<$prim>); + let mut n = n; + let n = n.reborrow().into_shared().transmute::, invariant::Valid, _>(); + $nonzero::new(n.read_unaligned::().into_inner()).is_some() }); )* } @@ -396,45 +400,51 @@ mod atomics { ($($($tyvar:ident)? => $atomic:ty [$prim:ty]),*) => {{ crate::util::macros::__unsafe(); - use core::cell::UnsafeCell; - use crate::pointer::{PtrInner, SizeEq, TransmuteFrom, invariant::Valid}; + use core::{cell::UnsafeCell}; + use crate::pointer::{TransmuteFrom, PtrInner, SizeFrom, invariant::Valid}; $( // SAFETY: The caller promised that `$atomic` and `$prim` have - // the same size and bit validity. + // the same size and bit validity. As a result of size equality, + // both impls of `SizeFrom::cast_from_raw` preserve referent + // size exactly. unsafe impl<$($tyvar)?> TransmuteFrom<$atomic, Valid, Valid> for $prim {} // SAFETY: The caller promised that `$atomic` and `$prim` have - // the same size and bit validity. + // the same size and bit validity. As a result of size equality, + // both impls of `SizeFrom::cast_from_raw` preserve referent + // size exactly. unsafe impl<$($tyvar)?> TransmuteFrom<$prim, Valid, Valid> for $atomic {} - // SAFETY: The caller promised that `$atomic` and `$prim` have - // the same size. - unsafe impl<$($tyvar)?> SizeEq<$atomic> for $prim { + // SAFETY: See inline safety comment. + unsafe impl<$($tyvar)?> SizeFrom<$atomic> for $prim { #[inline] fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, $prim> { - // SAFETY: The caller promised that `$atomic` and - // `$prim` have the same size. Thus, this cast preserves + // SAFETY: The caller promised that `$atomic` and `$prim` + // have the same size. Thus, this cast preserves // address, referent size, and provenance. unsafe { cast!(a) } } } // SAFETY: See previous safety comment. - unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic { + unsafe impl<$($tyvar)?> SizeFrom<$prim> for $atomic { #[inline] fn cast_from_raw(p: PtrInner<'_, $prim>) -> PtrInner<'_, $atomic> { // SAFETY: See previous safety comment. unsafe { cast!(p) } } } - // SAFETY: The caller promised that `$atomic` and `$prim` have - // the same size. `UnsafeCell` has the same size as `T` [1]. + + // SAFETY: The caller promised that `$atomic` and `$prim` + // have the same size. `UnsafeCell` has the same size as + // `T` [1]. Thus, this cast preserves address, referent + // size, and provenance. // // [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout: // // `UnsafeCell` has the same in-memory representation as // its inner type `T`. A consequence of this guarantee is that // it is possible to convert between `T` and `UnsafeCell`. - unsafe impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> { + unsafe impl<$($tyvar)?> SizeFrom<$atomic> for UnsafeCell<$prim> { #[inline] fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, UnsafeCell<$prim>> { // SAFETY: See previous safety comment. @@ -442,7 +452,7 @@ mod atomics { } } // SAFETY: See previous safety comment. - unsafe impl<$($tyvar)?> SizeEq> for $atomic { + unsafe impl<$($tyvar)?> SizeFrom> for $atomic { #[inline] fn cast_from_raw(p: PtrInner<'_, UnsafeCell<$prim>>) -> PtrInner<'_, $atomic> { // SAFETY: See previous safety comment. @@ -452,7 +462,9 @@ mod atomics { // SAFETY: The caller promised that `$atomic` and `$prim` have // the same bit validity. `UnsafeCell` has the same bit - // validity as `T` [1]. + // validity as `T` [1]. `UnsafeCell` also has the same size + // as `T` [1], and so both impls of `SizeFrom::cast_from_raw` + // preserve referent size exactly. // // [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout: // diff --git a/src/layout.rs b/src/layout.rs index 54cb21ad58..55d5c962b7 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -612,15 +612,15 @@ pub(crate) use cast_from_raw::cast_from_raw; mod cast_from_raw { use crate::{pointer::PtrInner, *}; - /// Implements [`>::cast_from_raw`][cast_from_raw]. + /// Implements [`>::cast_from_raw`][cast_from_raw]. /// /// # PME /// /// Generates a post-monomorphization error if it is not possible to satisfy - /// the soundness conditions of [`SizeEq::cast_from_raw`][cast_from_raw] + /// the soundness conditions of [`SizeFrom::cast_from_raw`][cast_from_raw] /// for `Src` and `Dst`. /// - /// [cast_from_raw]: crate::pointer::SizeEq::cast_from_raw + /// [cast_from_raw]: crate::pointer::SizeFrom::cast_from_raw // // FIXME(#1817): Support Sized->Unsized and Unsized->Sized casts pub(crate) fn cast_from_raw(src: PtrInner<'_, Src>) -> PtrInner<'_, Dst> diff --git a/src/lib.rs b/src/lib.rs index 2ae80906a8..7ae5de4d17 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -381,7 +381,7 @@ use core::{ #[cfg(feature = "std")] use std::io; -use crate::pointer::invariant::{self, BecauseExclusive}; +use crate::pointer::{invariant, BecauseBidirectional}; #[cfg(any(feature = "alloc", test, kani))] extern crate alloc; @@ -1840,7 +1840,7 @@ pub unsafe trait TryFromBytes { Self: KnownLayout + IntoBytes, { static_assert_dst_is_not_zst!(Self); - match Ptr::from_mut(bytes).try_cast_into_no_leftover::(None) { + match Ptr::from_mut(bytes).try_cast_into_no_leftover(None) { Ok(source) => { // This call may panic. If that happens, it doesn't cause any soundness // issues, as we have not generated any invalid state which we need to @@ -1852,9 +1852,7 @@ pub unsafe trait TryFromBytes { // condition will not happen. match source.try_into_valid() { Ok(source) => Ok(source.as_mut()), - Err(e) => { - Err(e.map_src(|src| src.as_bytes::().as_mut()).into()) - } + Err(e) => Err(e.map_src(|src| src.as_bytes().as_mut()).into()), } } Err(e) => Err(e.map_src(Ptr::as_mut).into()), @@ -2421,8 +2419,7 @@ pub unsafe trait TryFromBytes { where Self: KnownLayout + IntoBytes, { - match Ptr::from_mut(source).try_cast_into_no_leftover::(Some(count)) - { + match Ptr::from_mut(source).try_cast_into_no_leftover(Some(count)) { Ok(source) => { // This call may panic. If that happens, it doesn't cause any soundness // issues, as we have not generated any invalid state which we need to @@ -2434,9 +2431,7 @@ pub unsafe trait TryFromBytes { // condition will not happen. match source.try_into_valid() { Ok(source) => Ok(source.as_mut()), - Err(e) => { - Err(e.map_src(|src| src.as_bytes::().as_mut()).into()) - } + Err(e) => Err(e.map_src(|src| src.as_bytes().as_mut()).into()), } } Err(e) => Err(e.map_src(Ptr::as_mut).into()), @@ -2844,7 +2839,7 @@ fn try_mut_from_prefix_suffix, ) -> Result<(&mut T, &mut [u8]), TryCastError<&mut [u8], T>> { - match Ptr::from_mut(candidate).try_cast_into::(cast_type, meta) { + match Ptr::from_mut(candidate).try_cast_into(cast_type, meta) { Ok((candidate, prefix_suffix)) => { // This call may panic. If that happens, it doesn't cause any soundness // issues, as we have not generated any invalid state which we need to @@ -2856,7 +2851,7 @@ fn try_mut_from_prefix_suffix Ok((valid.as_mut(), prefix_suffix.as_mut())), - Err(e) => Err(e.map_src(|src| src.as_bytes::().as_mut()).into()), + Err(e) => Err(e.map_src(|src| src.as_bytes().as_mut()).into()), } } Err(e) => Err(e.map_src(Ptr::as_mut).into()), @@ -3832,8 +3827,8 @@ pub unsafe trait FromBytes: FromZeros { Self: IntoBytes + KnownLayout, { static_assert_dst_is_not_zst!(Self); - match Ptr::from_mut(source).try_cast_into_no_leftover::<_, BecauseExclusive>(None) { - Ok(ptr) => Ok(ptr.recall_validity::<_, (_, (_, _))>().as_mut()), + match Ptr::from_mut(source).try_cast_into_no_leftover(None) { + Ok(ptr) => Ok(ptr.recall_validity::<_, BecauseBidirectional>().as_mut()), Err(err) => Err(err.map_src(|src| src.as_mut())), } } @@ -4301,11 +4296,9 @@ pub unsafe trait FromBytes: FromZeros { Self: IntoBytes + KnownLayout + Immutable, { let source = Ptr::from_mut(source); - let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count)); + let maybe_slf = source.try_cast_into_no_leftover(Some(count)); match maybe_slf { - Ok(slf) => Ok(slf - .recall_validity::<_, (_, (_, (BecauseExclusive, BecauseExclusive)))>() - .as_mut()), + Ok(slf) => Ok(slf.recall_validity::<_, BecauseBidirectional>().as_mut()), Err(err) => Err(err.map_src(|s| s.as_mut())), } } @@ -4662,7 +4655,7 @@ pub unsafe trait FromBytes: FromZeros { // cannot be violated even though `buf` may have more permissive bit // validity than `ptr`. let ptr = unsafe { ptr.assume_validity::() }; - let ptr = ptr.as_bytes::(); + let ptr = ptr.as_bytes(); src.read_exact(ptr.as_mut())?; // SAFETY: `buf` entirely consists of initialized bytes, and `Self` is // `FromBytes`. @@ -4781,9 +4774,9 @@ fn mut_from_prefix_suffix( cast_type: CastType, ) -> Result<(&mut T, &mut [u8]), CastError<&mut [u8], T>> { let (slf, prefix_suffix) = Ptr::from_mut(source) - .try_cast_into::<_, BecauseExclusive>(cast_type, meta) + .try_cast_into(cast_type, meta) .map_err(|err| err.map_src(|s| s.as_mut()))?; - Ok((slf.recall_validity::<_, (_, (_, _))>().as_mut(), prefix_suffix.as_mut())) + Ok((slf.recall_validity::<_, BecauseBidirectional>().as_mut(), prefix_suffix.as_mut())) } /// Analyzes whether a type is [`IntoBytes`]. diff --git a/src/macros.rs b/src/macros.rs index 6de6eea01f..c14907ecbf 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -35,6 +35,17 @@ /// 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 referent; e.g.: +/// +/// ``` +/// # use zerocopy::transmute; +/// let u: u32 = transmute!(#![allow(shrink)] 0u64); +/// assert_eq!(u, 0u32); +/// ``` +/// /// # Examples /// /// ``` @@ -51,39 +62,117 @@ /// This macro can be invoked in `const` contexts. #[macro_export] macro_rules! 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. + // 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: 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 accomodate `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` 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: ManuallyDrop, + dst: ManuallyDrop, + } + // SAFETY: `Transmute` is a `reper(C)` union whose `src` field has + // type `ManuallyDrop`. Thus, the `src` field starts at byte + // offset 0 within `Transmute` [1]. `ManuallyDrop` 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` 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` above using + // `core::mem::transmute` ensures that `size_of::() == + // size_of::>()`. A `#[repr(C, packed)]` + // union has the maximum size of all of its fields [1], so this + // is equivalent to `size_of::() >= size_of::()`. + // + // 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 this macro invocation expression - // is `FromBytes`. - - struct AssertIsIntoBytes(T); - let _ = AssertIsIntoBytes(e); + // `IntoBytes` and that the type of the outer macro invocation + // expression is `FromBytes`. - struct AssertIsFromBytes(U); - #[allow(unused, unreachable_code)] - let u = AssertIsFromBytes(loop {}); - u.0 + fn transmute(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. - // - // We use this reexport of `core::mem::transmute` because we know it - // will always be available for crates which are using the 2015 - // edition of Rust. By contrast, if we were to use - // `std::mem::transmute`, this macro would not work for such crates - // in `no_std` contexts, and if we were to use - // `core::mem::transmute`, this macro would not work in `std` - // contexts in which `core` was not manually imported. This is not a - // problem for 2018 edition crates. let u = unsafe { // Clippy: We can't annotate the types; this macro is designed // to infer the types from the calling context. @@ -92,7 +181,7 @@ macro_rules! transmute { }; $crate::util::macro_util::must_use(u) } - }} + }}; } /// Safely transmutes a mutable or immutable reference of one type to an @@ -1046,6 +1135,10 @@ mod tests { 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)] @@ -1058,12 +1151,16 @@ mod tests { } #[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)); diff --git a/src/pointer/invariant.rs b/src/pointer/invariant.rs index 90ab1a8734..a913c94659 100644 --- a/src/pointer/invariant.rs +++ b/src/pointer/invariant.rs @@ -198,25 +198,6 @@ unsafe impl CastableFrom for DT {} // SAFETY: `SV = DV = Initialized`. unsafe impl CastableFrom for DT {} -/// [`Ptr`](crate::Ptr) referents that permit unsynchronized read operations. -/// -/// `T: Read` implies that a pointer to `T` with aliasing `A` permits -/// unsynchronized read operations. This can be because `A` is [`Exclusive`] or -/// because `T` does not permit interior mutation. -/// -/// # Safety -/// -/// `T: Read` if either of the following conditions holds: -/// - `A` is [`Exclusive`] -/// - `T` implements [`Immutable`](crate::Immutable) -/// -/// As a consequence, if `T: Read`, then any `Ptr` is -/// permitted to perform unsynchronized reads from its referent. -pub trait Read {} - -impl Read for T {} -impl Read for T {} - /// Unsynchronized reads are permitted because only one live [`Ptr`](crate::Ptr) /// or reference may exist to the referent bytes at a time. #[derive(Copy, Clone, Debug)] diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs index 6005bc78f7..76c6539906 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -17,10 +17,7 @@ mod transmute; #[doc(hidden)] pub use {inner::PtrInner, transmute::*}; #[doc(hidden)] -pub use { - invariant::{BecauseExclusive, BecauseImmutable, Read}, - ptr::Ptr, -}; +pub use {invariant::BecauseExclusive, ptr::Ptr}; /// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument /// to [`TryFromBytes::is_bit_valid`]. @@ -30,11 +27,11 @@ pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unali Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>; /// Checks if the referent is zeroed. -pub(crate) fn is_zeroed(ptr: Ptr<'_, T, I>) -> bool +pub(crate) fn is_zeroed(mut ptr: Ptr<'_, T, I>) -> bool where T: crate::Immutable + crate::KnownLayout, I: invariant::Invariants, I::Aliasing: invariant::Reference, { - ptr.as_bytes::().as_ref().iter().all(|&byte| byte == 0) + ptr.reborrow().into_shared().as_bytes().as_ref().iter().all(|&byte| byte == 0) } diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 254d4f1c6f..1774eecc69 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -15,7 +15,7 @@ use crate::{ pointer::{ inner::PtrInner, invariant::*, - transmute::{MutationCompatible, SizeEq, TransmuteFromPtr}, + transmute::{MutationCompatible, SizeFrom, TransmuteFromPtr}, }, AlignmentError, CastError, CastType, KnownLayout, SizeError, TryFromBytes, ValidityError, }; @@ -270,9 +270,8 @@ mod _conversions { { /// Reborrows `self`, producing another `Ptr`. /// - /// Since `self` is borrowed immutably, this prevents any mutable - /// methods from being called on `self` as long as the returned `Ptr` - /// exists. + /// Since `self` is borrowed mutably, this prevents `self` from being + /// used as long as the returned `Ptr` exists. #[doc(hidden)] #[inline] #[allow(clippy::needless_lifetimes)] // Allows us to name the lifetime in the safety comment below. @@ -313,6 +312,24 @@ mod _conversions { // memory may be live. unsafe { Ptr::from_inner(self.as_inner()) } } + + /// Converts `self` to a shared `Ptr`. + #[doc(hidden)] + #[inline] + #[must_use] + #[allow(clippy::needless_lifetimes)] // Allows us to name the lifetime in the safety comment below. + pub fn into_shared(self) -> Ptr<'a, T, (Shared, I::Alignment, I::Validity)> { + // SAFETY: Since `I::Aliasing: Reference`, there are two cases for + // `I::Aliasing`: + // - For `invariant::Shared`, the returned `Ptr` is identical, so no + // new proof obligations are introduced. + // - For `invariant::Exclusive`: Since `self` has `Exclusive` + // aliasing, it is guaranteed that no other `Ptr`s or references + // permit concurrent access to the referent. Thus, the returned + // `Ptr` is the only reference to the referent which may read or + // write the referent during `'a`. + unsafe { self.assume_aliasing() } + } } /// `Ptr<'a, T>` → `&'a mut T` @@ -362,11 +379,11 @@ mod _conversions { pub(crate) fn transmute(self) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)> where V: Validity, - U: TransmuteFromPtr + SizeEq + ?Sized, + U: TransmuteFromPtr + SizeFrom + ?Sized, { // SAFETY: - // - `SizeEq::cast_from_raw` promises to preserve address, - // provenance, and the number of bytes in the referent + // - `SizeFrom::cast_from_raw` promises to preserve address and to + // address a prefix of the bytes addressed by its argument // - If aliasing is `Shared`, then by `U: TransmuteFromPtr`, at // least one of the following holds: // - `T: Immutable` and `U: Immutable`, in which case it is @@ -376,7 +393,7 @@ mod _conversions { // operate on these references simultaneously // - By `U: TransmuteFromPtr`, it is // sound to perform this transmute. - unsafe { self.transmute_unchecked(SizeEq::cast_from_raw) } + unsafe { self.transmute_unchecked(SizeFrom::cast_from_raw) } } #[doc(hidden)] @@ -394,7 +411,7 @@ mod _conversions { // referent simultaneously // - By `T: TransmuteFromPtr`, it is // sound to perform this transmute. - let ptr = unsafe { self.transmute_unchecked(SizeEq::cast_from_raw) }; + let ptr = unsafe { self.transmute_unchecked(SizeFrom::cast_from_raw) }; // SAFETY: `self` and `ptr` have the same address and referent type. // Therefore, if `self` satisfies `I::Alignment`, then so does // `ptr`. @@ -412,8 +429,7 @@ mod _conversions { /// /// The caller promises that `u = cast(p)` is a pointer cast with the /// following properties: - /// - `u` addresses a subset of the bytes addressed by `p` - /// - `u` has the same provenance as `p` + /// - `u` addresses a prefix of the bytes addressed by `p` /// - If `I::Aliasing` is [`Shared`], it must not be possible for safe /// code, operating on a `&T` and `&U` with the same referent /// simultaneously, to cause undefined behavior @@ -511,7 +527,6 @@ mod _conversions { pub fn read_unaligned(self) -> T where T: Copy, - T: Read, { (*self.into_unalign().as_ref()).into_inner() } @@ -799,13 +814,11 @@ mod _transitions { /// On error, unsafe code may rely on this method's returned /// `ValidityError` containing `self`. #[inline] - pub(crate) fn try_into_valid( + pub(crate) fn try_into_valid( mut self, ) -> Result, ValidityError> where - T: TryFromBytes - + Read - + TryTransmuteFromPtr, + T: TryFromBytes + TryTransmuteFromPtr, I::Aliasing: Reference, I: Invariants, { @@ -816,9 +829,13 @@ mod _transitions { // SAFETY: If `T::is_bit_valid`, code may assume that `self` // contains a bit-valid instance of `T`. By `T: // TryTransmuteFromPtr`, so - // long as `self`'s referent conforms to the `Valid` validity - // for `T` (which we just confired), then this transmute is - // sound. + // long as `SizeFrom::cast_from_raw(self.into_inner)`'s referent + // conforms to the `Valid` validity for `T`, then this transmute + // is sound. Since `>::cast_from_raw` is + // guaranteed to be the identity function, this is equivalent to + // the statement that `self`'s referent conforms to the `Valid` + // validity for `T`, which we just confirmed using + // `T::is_bit_valid`. Ok(unsafe { self.assume_valid() }) } else { Err(ValidityError::new(self)) @@ -915,16 +932,16 @@ mod _casts { cast: F, ) -> Ptr<'a, U, (I::Aliasing, Unaligned, I::Validity)> where - T: MutationCompatible, - U: 'a + ?Sized + CastableFrom, + U: 'a + + MutationCompatible + + CastableFrom + + ?Sized, F: FnOnce(PtrInner<'a, T>) -> PtrInner<'a, U>, { // SAFETY: Because `T: MutationCompatible`, one // of the following holds: - // - `T: Read` and `U: Read`, in which - // case one of the following holds: - // - `I::Aliasing` is `Exclusive` - // - `T` and `U` are both `Immutable` + // - `I::Aliasing` is `Exclusive` + // - `T` and `U` are both `Immutable` // - It is sound for safe code to operate on `&T` and `&U` with the // same referent simultaneously // @@ -944,14 +961,14 @@ mod _casts { #[inline] pub fn as_bytes(self) -> Ptr<'a, [u8], (I::Aliasing, Aligned, Valid)> where - T: Read, + [u8]: MutationCompatible, I::Aliasing: Reference, { // SAFETY: `PtrInner::as_bytes` returns a pointer which addresses // the same byte range as its argument, and which has the same // provenance. let ptr = unsafe { self.cast_unsized(PtrInner::as_bytes) }; - ptr.bikeshed_recall_aligned().recall_validity::() + ptr.bikeshed_recall_aligned().recall_validity() } } @@ -1036,7 +1053,8 @@ mod _casts { > where I::Aliasing: Reference, - U: 'a + ?Sized + KnownLayout + Read, + U: 'a + ?Sized + KnownLayout, + [u8]: MutationCompatible, { let (inner, remainder) = self.as_inner().try_cast_into(cast_type, meta).map_err(|err| { @@ -1049,13 +1067,16 @@ mod _casts { })?; // SAFETY: - // 0. Since `U: Read`, either: - // - `I::Aliasing` is `Exclusive`, in which case both `src` and - // `ptr` conform to `Exclusive` + // 0. Since `[u8]: MutationCompatible`, either: + // - `I::Aliasing` is `Exclusive`, in which case both `self` and + // `inner` conform to `Exclusive` // - `I::Aliasing` is `Shared` and `U` is `Immutable` (we already // know that `[u8]: Immutable`). In this case, neither `U` nor // `[u8]` permit mutation, and so `Shared` aliasing is // satisfied. + // - `I::Aliasing` is `Shared` and `[u8]: InvariantsEq`, in + // which case it is sound for `self` and `inner` to exist at + // the same time. // 1. `ptr` conforms to the alignment invariant of `Aligned` because // it is derived from `try_cast_into`, which promises that the // object described by `target` is validly aligned for `U`. @@ -1099,7 +1120,8 @@ mod _casts { ) -> Result, CastError> where I::Aliasing: Reference, - U: 'a + ?Sized + KnownLayout + Read, + U: 'a + ?Sized + KnownLayout, + [u8]: MutationCompatible, { // FIXME(#67): Remove this allow. See NonNulSlicelExt for more // details. @@ -1231,7 +1253,7 @@ mod tests { use super::*; #[allow(unused)] // Needed on our MSRV, but considered unused on later toolchains. use crate::util::AsAddress; - use crate::{pointer::BecauseImmutable, util::testutil::AU64, FromBytes, Immutable}; + use crate::{pointer::invariant::BecauseImmutable, util::testutil::AU64, FromBytes, Immutable}; mod test_ptr_try_cast_into_soundness { use super::*; diff --git a/src/pointer/transmute.rs b/src/pointer/transmute.rs index 1fa0540526..0ac8a6becb 100644 --- a/src/pointer/transmute.rs +++ b/src/pointer/transmute.rs @@ -13,7 +13,7 @@ use core::{ }; use crate::{ - pointer::{invariant::*, PtrInner}, + pointer::{inner::PtrInner, invariant::*}, FromBytes, Immutable, IntoBytes, Unalign, }; @@ -33,42 +33,43 @@ use crate::{ /// Given `Dst: TryTransmuteFromPtr`, callers may assume the /// following: /// -/// Given `src: Ptr<'a, Src, (A, _, SV)>`, if the referent of `src` is -/// `DV`-valid for `Dst`, then it is sound to transmute `src` into `dst: Ptr<'a, -/// Dst, (A, Unaligned, DV)>` by preserving pointer address and metadata. +/// Given `src: Ptr<'a, Src, (A, _, SV)>` and `dst_inner = +/// SizeFrom::cast_from_raw(src.into_inner())`, if the referent of `dst_inner` +/// is `DV`-valid for `Dst`, then it is sound to construct `dst: Ptr<'a, Dst, +/// (A, Unaligned, DV)>` from `dst_inner`. Equivalently, it is sound to +/// transmute `src` into `dst` using [`SizeFrom::cast_from_raw`]. /// /// ## Pre-conditions /// -/// Given `src: Ptr` and `dst: Ptr`, -/// `Dst: TryTransmuteFromPtr` is sound if all of the -/// following hold: +/// Given `src: Ptr` and `dst: Ptr` +/// constructed from `SizeFrom::cast_from_raw(src.into_inner())`, `Dst: +/// TryTransmuteFromPtr` is sound if all of the following +/// hold: /// - Forwards transmutation: Either of the following hold: /// - So long as `dst` is active, no mutation of `dst`'s referent is allowed /// except via `dst` itself -/// - The set of `DV`-valid `Dst`s is a superset of the set of `SV`-valid -/// `Src`s +/// - `Dst: TransmuteFrom` /// - Reverse transmutation: Either of the following hold: /// - `dst` does not permit mutation of its referent -/// - The set of `DV`-valid `Dst`s is a subset of the set of `SV`-valid `Src`s +/// - `Src: TransmuteFromOverwrite` /// - No safe code, given access to `src` and `dst`, can cause undefined /// behavior: Any of the following hold: /// - `A` is `Exclusive` /// - `Src: Immutable` and `Dst: Immutable` -/// - It is sound for shared code to operate on a `&Src` and `&Dst` which -/// reference the same byte range at the same time +/// - It is sound for safe code to operate on `src.as_ref(): &Src` and +/// `dst.as_ref(): &Dst` at the same time /// /// ## Proof /// /// Given: /// - `src: Ptr<'a, Src, (A, _, SV)>` -/// - `src`'s referent is `DV`-valid for `Dst` -/// - `Dst: SizeEq` +/// - The leading `N` bytes of `src`'s referent are a `DV`-valid `Dst`, where +/// `N` is the referent size of `SizeFrom::cast_from_raw(src)` /// -/// We are trying to prove that it is sound to perform a pointer address- and -/// metadata-preserving transmute from `src` to a `dst: Ptr<'a, Dst, (A, -/// Unaligned, DV)>`. We need to prove that such a transmute does not violate -/// any of `src`'s invariants, and that it satisfies all invariants of the -/// destination `Ptr` type. +/// We are trying to prove that it is sound to use `SizeFrom::cast_from_raw` to +/// transmute from `src` to a `dst: Ptr<'a, Dst, (A, Unaligned, DV)>`. We need +/// to prove that such a transmute does not violate any of `src`'s invariants, +/// and that it satisfies all invariants of the destination `Ptr` type. /// /// First, all of `src`'s `PtrInner` invariants are upheld. `src`'s address and /// metadata are unchanged, so: @@ -78,16 +79,16 @@ use crate::{ /// - If its referent is not zero sized, `A` is guaranteed to live for at least /// `'a` /// -/// Since `Dst: SizeEq`, and since `dst` has the same address and metadata -/// as `src`, `dst` addresses the same byte range as `src`. `dst` also has the -/// same lifetime as `src`. Therefore, all of the `PtrInner` invariants -/// mentioned above also hold for `dst`. +/// By post-condition on `SizeFrom::cast_from_raw`, `dst` addresses a prefix of +/// the bytes addressed by `src`. `dst` also has the same lifetime as `src`. +/// Therefore, all of the `PtrInner` invariants mentioned above also hold for +/// `dst`. /// /// Second, since `src`'s address is unchanged, it still satisfies its /// alignment. Since `dst`'s alignment is `Unaligned`, it trivially satisfies /// its alignment. /// -/// Third, aliasing is either `Exclusive` or `Shared`: +/// Third, aliasing (`A`) is either `Exclusive` or `Shared`: /// - If it is `Exclusive`, then both `src` and `dst` satisfy `Exclusive` /// aliasing trivially: since `src` and `dst` have the same lifetime, `src` is /// inaccessible so long as `dst` is alive, and no other live `Ptr`s or @@ -95,79 +96,63 @@ use crate::{ /// - If it is `Shared`, then either: /// - `Src: Immutable` and `Dst: Immutable`, and so `UnsafeCell`s trivially /// cover the same byte ranges in both types. -/// - It is explicitly sound for safe code to operate on a `&Src` and a `&Dst` -/// pointing to the same byte range at the same time. +/// - It is sound for safe code to operate on a `src.as_ref()` and +/// `dst.as_ref()` at the same time. /// /// Fourth, `src`'s validity is satisfied. By invariant, `src`'s referent began /// as an `SV`-valid `Src`. It is guaranteed to remain so, as either of the /// following hold: /// - `dst` does not permit mutation of its referent. -/// - The set of `DV`-valid `Dst`s is a superset of the set of `SV`-valid -/// `Src`s. Thus, any value written via `dst` is guaranteed to be `SV`-valid -/// for `Src`. +/// - `Src: TransmuteFromOverwrite`. Since `Dst: SizeFrom`, +/// and since `dst` is produced using `SizeFrom::cast_from_raw`, given `src'` +/// composed by concatenating any `DV`-valid `Dst` of size `size_of_val(dst)` +/// with the trailing `size_of_val(src) - size_of_val(dst)` bytes of `src`, +/// `src'` is an `SV`-valid `Src`. Thus, any value written to `dst` is +/// guaranteed not to violate the `SV`-validity of `Src`. /// /// Fifth, `dst`'s validity is satisfied. It is a given of this proof that the -/// referent is `DV`-valid for `Dst`. It is guaranteed to remain so, as either -/// of the following hold: +/// leading bytes of the referent are `DV`-valid for `Dst`. It is guaranteed to +/// remain so, as either of the following hold: /// - So long as `dst` is active, no mutation of the referent is allowed except /// via `dst` itself. -/// - The set of `DV`-valid `Dst`s is a superset of the set of `SV`-valid -/// `Src`s. Thus, any value written via `src` is guaranteed to be a `DV`-valid -/// `Dst`. +/// - `Dst: TransmuteFrom`. Since `Dst: SizeFrom`, and since +/// `dst` is produced using `SizeFrom::cast_from_raw`, the leading +/// `size_of_val(dst)` bytes of any `SV`-valid `Src` constitute a `DV`-valid +/// `Dst`. Thus, any value written via `src` is guaranteed not to violate the +/// `DV`-validity of `Dst`. pub unsafe trait TryTransmuteFromPtr: - SizeEq + SizeFrom { } -#[allow(missing_copy_implementations, missing_debug_implementations)] -pub enum BecauseMutationCompatible {} - // SAFETY: -// - Forwards transmutation: By `Dst: MutationCompatible`, we -// know that at least one of the following holds: -// - So long as `dst: Ptr` is active, no mutation of its referent is -// allowed except via `dst` itself if either of the following hold: -// - Aliasing is `Exclusive`, in which case, so long as the `Dst` `Ptr` -// exists, no mutation is permitted except via that `Ptr` -// - Aliasing is `Shared`, `Src: Immutable`, and `Dst: Immutable`, in which -// case no mutation is possible via either `Ptr` -// - `Dst: TransmuteFrom`. Since `Dst: SizeEq`, this bound -// guarantees that the set of `DV`-valid `Dst`s is a supserset of the set of -// `SV`-valid `Src`s. -// - Reverse transmutation: `Src: TransmuteFrom`. Since `Dst: -// SizeEq`, this guarantees that the set of `DV`-valid `Dst`s is a subset -// of the set of `SV`-valid `Src`s. -// - No safe code, given access to `src` and `dst`, can cause undefined -// behavior: By `Dst: MutationCompatible`, at least one of +// - Forwards transmutation: By `Dst: MutationCompatible`, one +// of the following holds: +// - So long as `dst` is active, no mutation of `dst`'s referent is allowed +// except via `dst` itself because: +// - `A = Exclusive`, or +// - `A = Shared`, `Src: Immutable`, and `Dst: Immutable`, in which case no +// mutation is permitted whatsoever. +// - `Dst: TransmuteFrom` +// - Reverse transmutation: By `Dst: MutationCompatible`, one of // the following holds: +// - `dst` does not permit mutation of its referent because `A = Shared`, +// `Src: Immutable`, and `Dst: Immutable` +// - `Src: TransmuteFromOverwrite` +// - No safe code, given access to `src` and `dst`, can cause undefined +// behavior: By `Dst: MutationCompatible`, one of the +// following holds: // - `A` is `Exclusive` // - `Src: Immutable` and `Dst: Immutable` -// - `Dst: InvariantsEq`, which guarantees that `Src` and `Dst` have the -// same invariants, and have `UnsafeCell`s covering the same byte ranges -unsafe impl - TryTransmuteFromPtr for Dst +// - `Dst: InvariantsEq`, in which case it is sound for safe code to +// operate on `src.as_ref(): &Src` and `dst.as_ref(): &Dst` at the same time +unsafe impl TryTransmuteFromPtr for Dst where A: Aliasing, SV: Validity, DV: Validity, - Src: TransmuteFrom + ?Sized, - Dst: MutationCompatible + SizeEq + ?Sized, -{ -} - -// SAFETY: -// - Forwards transmutation: Since aliasing is `Shared` and `Src: Immutable`, -// `src` does not permit mutation of its referent. -// - Reverse transmutation: Since aliasing is `Shared` and `Dst: Immutable`, -// `dst` does not permit mutation of its referent. -// - No safe code, given access to `src` and `dst`, can cause undefined -// behavior: `Src: Immutable` and `Dst: Immutable` -unsafe impl TryTransmuteFromPtr for Dst -where - SV: Validity, - DV: Validity, - Src: Immutable + ?Sized, - Dst: Immutable + SizeEq + ?Sized, + Src: ?Sized, + Dst: MutationCompatible + SizeFrom + ?Sized, { } @@ -178,21 +163,42 @@ where /// # Safety /// /// At least one of the following must hold: -/// - `Src: Read` and `Self: Read` -/// - `Self: InvariantsEq`, and, for some `V`: -/// - `Dst: TransmuteFrom` -/// - `Src: TransmuteFrom` +/// - `A = Exclusive` and `Src: TransmuteOverwrite` +/// - `A = Shared`, `Src: Immutable`, and `Dst: Immutable` +/// - `Dst: TransmuteFrom`, `Src: TransmuteOverwrite`, +/// and `Dst: InvariantsEq` pub unsafe trait MutationCompatible {} #[allow(missing_copy_implementations, missing_debug_implementations)] -pub enum BecauseRead {} +pub enum BecauseReversible {} -// SAFETY: `Src: Read` and `Dst: Read`. -unsafe impl - MutationCompatible for Dst +#[allow(missing_copy_implementations, missing_debug_implementations)] +pub enum BecauseBidirectional {} + +// SAFETY: Aliasing is `Exclusive` and `Src: TransmuteOverwrite`. +unsafe impl + MutationCompatible for Dst where - Src: Read, - Dst: Read, + Src: TransmuteOverwrite, +{ +} + +// SAFETY: Aliasing is `Shared`, `Src: Immutable`, and `Dst: Immutable`. +unsafe impl + MutationCompatible for Dst +where + Src: Immutable, + Dst: Immutable, +{ +} + +// SAFETY: `Dst: TransmuteFrom`, `Src: TransmuteFromOverwrite`, and `Dst: InvariantsEq`. +unsafe impl + MutationCompatible for Dst +where + Dst: TransmuteFrom + InvariantsEq, + Src: TransmuteOverwrite, { } @@ -208,18 +214,6 @@ pub unsafe trait InvariantsEq {} // SAFETY: Trivially sound to have multiple `&T` pointing to the same referent. unsafe impl InvariantsEq for T {} -// SAFETY: `Dst: InvariantsEq + TransmuteFrom`, and `Src: -// TransmuteFrom`. -unsafe impl - MutationCompatible for Dst -where - Src: TransmuteFrom, - Dst: TransmuteFrom + InvariantsEq, -{ -} - -pub(crate) enum BecauseInvariantsEq {} - macro_rules! unsafe_impl_invariants_eq { ($tyvar:ident => $t:ty, $u:ty) => {{ crate::util::macros::__unsafe(); @@ -250,13 +244,45 @@ unsafe impl InvariantsEq> for T {} /// Transmutations which are always sound. /// -/// `TransmuteFromPtr` is a shorthand for [`TryTransmuteFromPtr`] and -/// [`TransmuteFrom`]. +/// `TransmuteFromPtr` is a shorthand for the conjuction of +/// [`TryTransmuteFromPtr`] and [`TransmuteFrom`]. /// /// # Safety /// /// `Dst: TransmuteFromPtr` is equivalent to `Dst: /// TryTransmuteFromPtr + TransmuteFrom`. +/// +/// Further, if `Dst: TransmuteFromPtr`, then given `src: +/// Ptr<'_, (A, _, SV)>`, it is sound to transmute `src` to `dst: Ptr<'_, (A, _, +/// DV)>` using [`SizeFrom::cast_from_raw`] to perform the raw pointer +/// transmute. +/// +/// ## Proof +/// +/// By `Dst: TryTransmuteFromPtr`: +/// +/// > Given `src: Ptr<'a, Src, (A, _, SV)>` and `dst_inner = +/// > SizeFrom::cast_from_raw(src.into_inner())`, if the referent of `dst_inner` +/// > is `DV`-valid for `Dst`, then it is sound to construct `dst: Ptr<'a, Dst, +/// > (A, Unaligned, DV)>` from `dst_inner`. Equivalently, it is sound to +/// > transmute `src` into `dst` using [`SizeFrom::cast_from_raw`]. +/// +/// Thus, we just need to prove that `dst_inner`'s referent is `DV`-valid for +/// `Dst`. +/// +/// By `Dst: TransmuteFrom`: +/// +/// > If `Dst: SizeFrom`, then the following must hold: For all [valid +/// > sizes] of `Src`, `ssize`, let `s: PtrInner<'_, Src>` with referent size +/// > `ssize`. Let `dsize` be the referent size of `SizeFrom::cast_from_raw(s)`. +/// > Note that, by invariant on `cast_from_raw`, `ssize >= dsize`. For all +/// > `SV`-valid values of `Src` with size `ssize`, `src`, it must be the case +/// > that the leading `dsize` bytes of `src` constitute a `DV`-valid `Dst`. +/// +/// `dst_inner = SizeFrom::cast_from_raw(src.into_inner())`, and so its referent +/// size is equal to `dsize` in the `TransmuteFrom` safety invariant. Thus, for +/// all possible referents of `src`, `dst_inner`'s referent constitutes a +/// `DV`-valid `Dst`. pub unsafe trait TransmuteFromPtr: TryTransmuteFromPtr + TransmuteFrom { @@ -276,43 +302,76 @@ where /// /// # Safety /// -/// Given `src: Ptr` and `dst: Ptr`, if the -/// referents of `src` and `dst` are the same size, then the set of bit patterns -/// allowed to appear in `src`'s referent must be a subset of the set allowed to -/// appear in `dst`'s referent. +/// *In this section, we refer to `Self` as `Dst`.* /// -/// If the referents are not the same size, then `Dst: TransmuteFrom` conveys no safety guarantee. +/// If `Dst: SizeFrom` (or `Dst: Sized` and `Src: Sized` where +/// `size_of::() <= size_of::()`), then the following must hold: For +/// all [valid sizes] of `Src` (or for `size_of::()`), `ssize`, let `s: +/// PtrInner<'_, Src>` with referent size `ssize`. Let `dsize` be the referent +/// size of `SizeFrom::cast_from_raw(s)` (or `size_of::()`). Note that, by +/// invariant on `cast_from_raw` (or by `size_of::() <= size_of::()`), +/// `ssize >= dsize`. For all `SV`-valid values of `Src` with size `ssize`, +/// `src`, it must be the case that the leading `dsize` bytes of `src` +/// constitute a `DV`-valid `Dst`. +/// +/// [valid sizes]: crate::KnownLayout#what-is-a-valid-size pub unsafe trait TransmuteFrom {} +/// Denotes that any `BV`-valid `Self` may have its prefix overwritten with any +/// `OV`-valid `Overlay` and remain `BV`-valid. +/// /// # Safety /// -/// `T` and `Self` must have the same vtable kind (`Sized`, slice DST, `dyn`, -/// etc) and have the same size. In particular: -/// - If `T: Sized` and `Self: Sized`, then their sizes must be equal -/// - If `T: ?Sized` and `Self: ?Sized`, then it must be the case that, given -/// any `t: PtrInner<'_, T>`, `>::cast_from_raw(t)` produces -/// a pointer which addresses the same number of bytes as `t`. *Note that it -/// is **not** guaranteed that an `as` cast preserves referent size: it may be -/// the case that `cast_from_raw` modifies the pointer's metadata in order to -/// preserve referent size, which an `as` cast does not do.* -pub unsafe trait SizeEq { - fn cast_from_raw(t: PtrInner<'_, T>) -> PtrInner<'_, Self>; +/// *In this section, we refer to `Self` as `Base`.* +/// +/// If `Overlay: SizeFrom` (or `Overlay: Sized` and `Base: Sized` where +/// `size_of::() <= size_of::()`), then the following must hold: +/// For all [valid sizes] of `Base` (or for `size_of::()`), `bsize`, let +/// `b: PtrInner<'_, Base>` with referent size `bsize`. Let `osize` be the +/// referent size of `SizeFrom::cast_from_raw(b)` (or `size_of::()`). +/// Note that, by invariant on `cast_from_raw` (or by `size_of::() <= +/// size_of::()`), `bsize >= osize`. For all `BV`-valid values of `Base` +/// with size `bsize`, `base`, and for all `OV`-valid values of `Overlay` with +/// size `osize`, `overlay`, let `base'` be constructed by concatenating +/// `overlay` with the trailing `bsize - osize` bytes of `base`. It must be the +/// case that `base'` is a `BV`-valid `Base`. +/// +/// [valid sizes]: crate::KnownLayout#what-is-a-valid-size +pub unsafe trait TransmuteOverwrite {} + +/// # Safety +/// +/// Implementations of `cast_from_raw` must satisfy that method's safety +/// post-condition. +pub unsafe trait SizeFrom { + /// # Safety + /// + /// Given `src: PtrInner<'_, Src>`, `let dst = Self::cast_from_raw(src)` + /// produces a pointer with the same address as `src`, and referring to at + /// most as many bytes. + /// + /// The size of `dst` must only be a function of the size of `src`. It must + /// not be a function of `src`'s address. + /// + /// `>::cast_from_raw` is guaranteed to be the + /// identity function. + fn cast_from_raw(src: PtrInner<'_, Src>) -> PtrInner<'_, Self>; } // SAFETY: `T` trivially has the same size and vtable kind as `T`, and since // pointer `*mut T -> *mut T` pointer casts are no-ops, this cast trivially // preserves referent size (when `T: ?Sized`). -unsafe impl SizeEq for T { +unsafe impl SizeFrom for T { #[inline(always)] - fn cast_from_raw(t: PtrInner<'_, T>) -> PtrInner<'_, T> { + fn cast_from_raw<'a>(t: PtrInner<'a, T>) -> PtrInner<'a, T> { t } } +/// `Valid → Initialized` // SAFETY: Since `Src: IntoBytes`, the set of valid `Src`'s is the set of // initialized bit patterns, which is exactly the set allowed in the referent of -// any `Initialized` `Ptr`. +// any `Initialized` `Ptr`. This holds even for shrinking transmutes. unsafe impl TransmuteFrom for Dst where Src: IntoBytes + ?Sized, @@ -320,9 +379,24 @@ where { } -// SAFETY: Since `Dst: FromBytes`, any initialized bit pattern may appear in the -// referent of a `Ptr`. This is exactly equal to the set of -// bit patterns which may appear in the referent of any `Initialized` `Ptr`. +/// `Valid → Initialized` +// SAFETY: Let `overlay` be a `Valid` `Overlay` and `base` be an `Initialized` +// `Base`. The trailing bytes of `base` have bit validity `[u8; N]`. By +// `Overlay: IntoBytes`, `overlay`'s bit validity is at least as restrictive as +// `[u8; M]` (some `[u8; M]` values may not be valid `Overlay` values). Thus, +// `base' = overlay + trailing_bytes_of(base)` is a valid `[u8; N + M]`, which +// is a valid `Initialized` value. +unsafe impl TransmuteOverwrite for Base +where + Overlay: IntoBytes + ?Sized, + Base: ?Sized, +{ +} + +/// `Initialized → Valid` +// SAFETY: Since `Dst: FromBytes`, any initialized bit pattern is a valid `Dst`. +// An `Initialized` is guaranteed to have all its bytes initialized, so any +// (prefix of an) `Initialized` is a valid `Dst`. unsafe impl TransmuteFrom for Dst where Src: ?Sized, @@ -330,12 +404,44 @@ where { } +/// `Initialized → Valid` +// SAFETY: The bit validity of `Initialized` is equivalent to that of +// `[u8]`. By `Base: FromBytes + IntoBytes`, the validity of `Valid` is +// also equivalent to that of `[u8]`. Any two `[u8]`s concatenated together are +// a valid `[u8]`. +// +// It might be tempting to remove the `Base: IntoBytes` bound. Unfortunately, +// that would be unsound. Consider the following type: +// +// #[repr(u8)] +// enum Base { +// V0(u8), +// V1(MaybeUninit), +// ... +// V255(MaybeUninit), +// } +// +// Consider combining this with `Overlay` type `u8`. Now consider the following +// sequence: +// - Start with `base = Base::V1(MaybeUninit::uninit())` +// - Overwrite with overlay `0` +// - This results in the bit pattern `0x00` followed by an uninit byte, which is +// invalid for `Base` +unsafe impl TransmuteOverwrite for Base +where + Overlay: ?Sized, + Base: FromBytes + IntoBytes + ?Sized, +{ +} + // FIXME(#2354): This seems like a smell - the soundness of this bound has // nothing to do with `Src` or `Dst` - we're basically just saying `[u8; N]` is // transmutable into `[u8; N]`. -// SAFETY: The set of allowed bit patterns in the referent of any `Initialized` -// `Ptr` is the same regardless of referent type. +/// `Initialized → Initialized` +// SAFETY: The validity of `Initialized` is equal to that of `[u8]`. `[u8]`'s +// validity does not depend on a value's length, so any prefix of an +// `Initialized` is a valid `Initialized`. unsafe impl TransmuteFrom for Dst where Src: ?Sized, @@ -343,12 +449,25 @@ where { } +/// `Initialized → Initialized` +// SAFETY: The validity of `Initialized` is equal to that of `[u8]`. `[u8]`'s +// validity does not depend on a value's length, so two `[u8]`s concatenated +// together are also a valid `[u8]`. Thus, an `Initialized` +// concatenated with the `[u8]` suffix of an `Initialized` is a valid +// `Initialized`. +unsafe impl TransmuteOverwrite for Base +where + Overlay: ?Sized, + Base: ?Sized, +{ +} + // FIXME(#2354): This seems like a smell - the soundness of this bound has // nothing to do with `Dst` - we're basically just saying that any type is // transmutable into `MaybeUninit<[u8; N]>`. -// SAFETY: A `Dst` with validity `Uninit` permits any byte sequence, and -// therefore can be transmuted from any value. +/// `V → Uninit` +// SAFETY: A `Dst` with validity `Uninit` permits any byte sequence. unsafe impl TransmuteFrom for Dst where Src: ?Sized, @@ -357,6 +476,16 @@ where { } +/// `V → Uninit` +// SAFETY: A `Base` with validity `Uninit` permits any byte sequence. +unsafe impl TransmuteOverwrite for Base +where + Overlay: ?Sized, + Base: ?Sized, + V: Validity, +{ +} + // SAFETY: // - `ManuallyDrop` has the same size as `T` [1] // - `ManuallyDrop` has the same validity as `T` [1] @@ -447,19 +576,25 @@ const _: () = unsafe { unsafe_impl_for_transparent_wrapper!(T: ?Sized => Cell impl_transitive_transmute_from!(T: ?Sized => Cell => T => UnsafeCell); impl_transitive_transmute_from!(T: ?Sized => UnsafeCell => T => Cell); +/// `Uninit → Valid>` // SAFETY: `MaybeUninit` has no validity requirements. Currently this is not // explicitly guaranteed, but it's obvious from `MaybeUninit`'s documentation // that this is the intention: // https://doc.rust-lang.org/1.85.0/core/mem/union.MaybeUninit.html -unsafe impl TransmuteFrom for MaybeUninit {} +unsafe impl TransmuteFrom for MaybeUninit {} + +/// `Uninit → Valid>` +// SAFETY: See previous safety comment. +unsafe impl TransmuteOverwrite for MaybeUninit {} -// SAFETY: `MaybeUninit` has the same size as `T` [1]. +// SAFETY: `MaybeUninit` has the same size as `T` [1]. Thus, a pointer cast +// preserves address and referent size. // // [1] Per https://doc.rust-lang.org/1.81.0/std/mem/union.MaybeUninit.html#layout-1: // // `MaybeUninit` is guaranteed to have the same size, alignment, and ABI as // `T` -unsafe impl SizeEq for MaybeUninit { +unsafe impl SizeFrom for MaybeUninit { #[inline(always)] fn cast_from_raw(t: PtrInner<'_, T>) -> PtrInner<'_, MaybeUninit> { // SAFETY: Per preceding safety comment, `MaybeUninit` and `T` have @@ -469,7 +604,7 @@ unsafe impl SizeEq for MaybeUninit { } // SAFETY: See previous safety comment. -unsafe impl SizeEq> for T { +unsafe impl SizeFrom> for T { #[inline(always)] fn cast_from_raw(t: PtrInner<'_, MaybeUninit>) -> PtrInner<'_, T> { // SAFETY: Per preceding safety comment, `MaybeUninit` and `T` have diff --git a/src/ref.rs b/src/ref.rs index 423bf84988..a428168ff6 100644 --- a/src/ref.rs +++ b/src/ref.rs @@ -656,9 +656,9 @@ where // `b.into_byte_slice_mut()` produces a byte slice with identical // address and length to that produced by `b.deref_mut()`. let ptr = Ptr::from_mut(b.into_byte_slice_mut()) - .try_cast_into_no_leftover::(None) + .try_cast_into_no_leftover(None) .expect("zerocopy internal error: into_ref should be infallible"); - let ptr = ptr.recall_validity::<_, (_, (_, _))>(); + let ptr = ptr.recall_validity::<_, BecauseBidirectional>(); ptr.as_mut() } } @@ -797,9 +797,9 @@ where // are preserved through `.deref_mut()`, so this `unwrap` will not // panic. let ptr = Ptr::from_mut(b.deref_mut()) - .try_cast_into_no_leftover::(None) + .try_cast_into_no_leftover(None) .expect("zerocopy internal error: DerefMut::deref_mut should be infallible"); - let ptr = ptr.recall_validity::<_, (_, (_, (BecauseExclusive, BecauseExclusive)))>(); + let ptr = ptr.recall_validity::<_, BecauseBidirectional>(); ptr.as_mut() } } diff --git a/src/util/macro_util.rs b/src/util/macro_util.rs index affa65c044..81ac0a0f26 100644 --- a/src/util/macro_util.rs +++ b/src/util/macro_util.rs @@ -30,8 +30,8 @@ use core::ptr::{self, NonNull}; use crate::{ pointer::{ - invariant::{self, BecauseExclusive, BecauseImmutable, Invariants}, - BecauseInvariantsEq, InvariantsEq, SizeEq, TryTransmuteFromPtr, + invariant::{self, BecauseImmutable, Invariants}, + BecauseBidirectional, InvariantsEq, MutationCompatible, SizeFrom, TryTransmuteFromPtr, }, FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout, Ptr, TryFromBytes, ValidityError, }; @@ -493,7 +493,7 @@ macro_rules! assert_size_eq { /// [`is_bit_valid`]: TryFromBytes::is_bit_valid #[doc(hidden)] #[inline] -fn try_cast_or_pme( +fn try_cast_or_pme( src: Ptr<'_, Src, I>, ) -> Result< Ptr<'_, Dst, (I::Aliasing, invariant::Unaligned, invariant::Valid)>, @@ -502,10 +502,10 @@ fn try_cast_or_pme( where // FIXME(#2226): There should be a `Src: FromBytes` bound here, but doing so // requires deeper surgery. - Src: invariant::Read, + Src: MutationCompatible, Dst: TryFromBytes - + invariant::Read - + TryTransmuteFromPtr, + + MutationCompatible + + TryTransmuteFromPtr, I: Invariants, I::Aliasing: invariant::Reference, { @@ -620,7 +620,7 @@ where { let ptr = Ptr::from_ref(src); let ptr = ptr.bikeshed_recall_initialized_immutable(); - match try_cast_or_pme::(ptr) { + match try_cast_or_pme::(ptr) { Ok(ptr) => { static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); // SAFETY: We have checked that `Dst` does not have a stricter @@ -664,7 +664,7 @@ where { let ptr = Ptr::from_mut(src); let ptr = ptr.bikeshed_recall_initialized_from_bytes(); - match try_cast_or_pme::(ptr) { + match try_cast_or_pme::(ptr) { Ok(ptr) => { static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); // SAFETY: We have checked that `Dst` does not have a stricter @@ -673,7 +673,7 @@ where Ok(ptr.as_mut()) } Err(err) => { - Err(err.map_src(|ptr| ptr.recall_validity::<_, (_, BecauseInvariantsEq)>().as_mut())) + Err(err.map_src(|ptr| ptr.recall_validity::<_, BecauseBidirectional>().as_mut())) } } } @@ -793,11 +793,11 @@ where // SAFETY: We only use `S` as `S` and `D` as `D`. unsafe { - unsafe_with_size_eq!(, D> { + unsafe_with_size_from!(, D> { let ptr = Ptr::from_ref(self.0) - .transmute::, invariant::Valid, BecauseImmutable>() + .transmute::, invariant::Valid, _>() .recall_validity::() - .transmute::, invariant::Initialized, (crate::pointer::BecauseMutationCompatible, _)>() + .transmute::, invariant::Initialized, _>() .recall_validity::(); #[allow(unused_unsafe)] @@ -833,12 +833,12 @@ where // SAFETY: We only use `S` as `S` and `D` as `D`. unsafe { - unsafe_with_size_eq!(, D> { + unsafe_with_size_from!(, D> { let ptr = Ptr::from_mut(self.0) .transmute::, invariant::Valid, _>() - .recall_validity::() + .recall_validity::() .transmute::, invariant::Initialized, _>() - .recall_validity::(); + .recall_validity::(); #[allow(unused_unsafe)] // SAFETY: The preceding `static_assert!` ensures that diff --git a/src/util/macros.rs b/src/util/macros.rs index 35211bdb74..a790262c22 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -220,7 +220,7 @@ macro_rules! impl_for_transmute_from { #[inline] fn is_bit_valid(candidate: Maybe<'_, Self, A>) -> bool { let c: Maybe<'_, Self, crate::pointer::invariant::Exclusive> = candidate.into_exclusive_or_pme(); - let c: Maybe<'_, $repr, _> = c.transmute::<_, _, (_, (_, (BecauseExclusive, BecauseExclusive)))>(); + let c: Maybe<'_, $repr, _> = c.transmute(); // SAFETY: This macro ensures that `$repr` and `Self` have the same // size and bit validity. Thus, a bit-valid instance of `$repr` is // also a bit-valid instance of `Self`. @@ -702,34 +702,42 @@ macro_rules! cast { }}; } -/// Implements `TransmuteFrom` and `SizeEq` for `T` and `$wrapper`. +/// Implements `TransmuteFrom` and `SizeFrom` for `T` and `$wrapper`. /// /// # Safety /// -/// `T` and `$wrapper` must have the same bit validity, and must have the -/// same size in the sense of `SizeEq`. +/// `T` and `$wrapper` must have the same bit validity, and must satisfy the +/// following property: given `t: *mut T`, `let w = t as *mut $wrapper` +/// produces a pointer which address the same bytes as `t`. The inverse must +/// also hold. macro_rules! unsafe_impl_for_transparent_wrapper { (T $(: ?$optbound:ident)? => $wrapper:ident) => {{ crate::util::macros::__unsafe(); - use crate::pointer::{TransmuteFrom, PtrInner, SizeEq, invariant::Valid}; + use crate::pointer::{TransmuteFrom, TransmuteOverwrite, PtrInner, SizeFrom, invariant::Valid}; // SAFETY: The caller promises that `T` and `$wrapper` have the same - // bit validity. + // bit validity, and that both implementations of + // `SizeFrom::cast_from_raw` preserve referent size exactly. unsafe impl TransmuteFrom for $wrapper {} // SAFETY: See previous safety comment. unsafe impl TransmuteFrom<$wrapper, Valid, Valid> for T {} + // SAFETY: See previous safety comment. + unsafe impl TransmuteOverwrite for $wrapper {} + // SAFETY: See previous safety comment. + unsafe impl TransmuteOverwrite<$wrapper, Valid, Valid> for T {} // SAFETY: The caller promises that `T` and `$wrapper` satisfy - // `SizeEq`. - unsafe impl SizeEq for $wrapper { + // `cast_from_raw`'s safety post-condition. + unsafe impl SizeFrom for $wrapper { #[inline(always)] fn cast_from_raw(t: PtrInner<'_, T>) -> PtrInner<'_, $wrapper> { - // SAFETY: See previous safety comment. + // SAFETY: The caller promises that this cast produces a pointer + // which addresses the same bytes as `t`. unsafe { cast!(t) } } } // SAFETY: See previous safety comment. - unsafe impl SizeEq<$wrapper> for T { + unsafe impl SizeFrom<$wrapper> for T { #[inline(always)] fn cast_from_raw(t: PtrInner<'_, $wrapper>) -> PtrInner<'_, T> { // SAFETY: See previous safety comment. @@ -742,19 +750,20 @@ macro_rules! unsafe_impl_for_transparent_wrapper { macro_rules! impl_transitive_transmute_from { ($($tyvar:ident $(: ?$optbound:ident)?)? => $t:ty => $u:ty => $v:ty) => { const _: () = { - use crate::pointer::{TransmuteFrom, PtrInner, SizeEq, invariant::Valid}; + use crate::pointer::{TransmuteFrom, SizeFrom, invariant::Valid}; - // SAFETY: Since `$u: SizeEq<$t>` and `$v: SizeEq`, this impl is - // transitively sound. - unsafe impl<$($tyvar $(: ?$optbound)?)?> SizeEq<$t> for $v + // SAFETY: See safety comment on `cast_from_raw`. + unsafe impl<$($tyvar $(: ?$optbound)?)?> SizeFrom<$t> for $v where - $u: SizeEq<$t>, - $v: SizeEq<$u>, + $u: SizeFrom<$t>, + $v: SizeFrom<$u>, { + // SAFETY: Each inner call to `cast_from_raw` preserves + // provenance and addressed byte range. #[inline(always)] fn cast_from_raw(t: PtrInner<'_, $t>) -> PtrInner<'_, $v> { - let u = <$u as SizeEq<_>>::cast_from_raw(t); - <$v as SizeEq<_>>::cast_from_raw(u) + let u = SizeFrom::cast_from_raw(t); + SizeFrom::cast_from_raw(u) } } @@ -772,10 +781,10 @@ macro_rules! impl_transitive_transmute_from { } #[rustfmt::skip] -macro_rules! impl_size_eq { +macro_rules! impl_size_from { ($t:ty, $u:ty) => { const _: () = { - use crate::{KnownLayout, pointer::{PtrInner, SizeEq}}; + use crate::{KnownLayout, pointer::{PtrInner, SizeFrom}}; static_assert!(=> { let t = <$t as KnownLayout>::LAYOUT; @@ -791,7 +800,7 @@ macro_rules! impl_size_eq { }); // SAFETY: See inline. - unsafe impl SizeEq<$t> for $u { + unsafe impl SizeFrom<$t> for $u { #[inline(always)] fn cast_from_raw(t: PtrInner<'_, $t>) -> PtrInner<'_, $u> { // SAFETY: We've asserted that their @@ -802,7 +811,7 @@ macro_rules! impl_size_eq { } } // SAFETY: See previous safety comment. - unsafe impl SizeEq<$u> for $t { + unsafe impl SizeFrom<$u> for $t { #[inline(always)] fn cast_from_raw(u: PtrInner<'_, $u>) -> PtrInner<'_, $t> { // SAFETY: See previous safety comment. @@ -814,17 +823,17 @@ macro_rules! impl_size_eq { } /// Invokes `$blk` in a context in which `$src<$t>` and `$dst<$u>` implement -/// `SizeEq`. +/// `SizeFrom`. /// -/// This macro emits code which implements `SizeEq`, and ensures that the impl -/// is sound via PME. +/// This macro emits code which implements `SizeComapt`, and ensures that the +/// impl is sound via PME. /// /// # Safety /// /// Inside of `$blk`, the caller must only use `$src` and `$dst` as `$src<$t>` /// and `$dst<$u>`. The caller must not use `$src` or `$dst` to wrap any other /// types. -macro_rules! unsafe_with_size_eq { +macro_rules! unsafe_with_size_from { (<$src:ident<$t:ident>, $dst:ident<$u:ident>> $blk:expr) => {{ crate::util::macros::__unsafe(); @@ -859,14 +868,14 @@ macro_rules! unsafe_with_size_eq { // We manually instantiate `cast_from_raw` below to ensure that this PME // can be triggered, and the caller promises not to use `$src` and // `$dst` with any wrapped types other than `$t` and `$u` respectively. - unsafe impl SizeEq<$src> for $dst + unsafe impl SizeFrom<$src> for $dst where T: KnownLayout, U: KnownLayout, { fn cast_from_raw(src: PtrInner<'_, $src>) -> PtrInner<'_, Self> { // SAFETY: `crate::layout::cast_from_raw` promises to satisfy - // the safety invariants of `SizeEq::cast_from_raw`, or to + // the safety invariants of `SizeFrom::cast_from_raw`, or to // generate a PME. Since `$src` and `$dst` are // `#[repr(transparent)]` wrappers around `T` and `U` // respectively, a `cast_from_raw` impl which satisfies the @@ -887,14 +896,12 @@ macro_rules! unsafe_with_size_eq { // See safety comment on the preceding `unsafe impl` block for an // explanation of why we need this block. if 1 == 0 { - let ptr = <$t as KnownLayout>::raw_dangling(); - #[allow(unused_unsafe)] - // SAFETY: This call is never executed. - let ptr = unsafe { crate::pointer::PtrInner::new(ptr) }; - #[allow(unused_unsafe)] - // SAFETY: This call is never executed. - let ptr = unsafe { cast!(ptr) }; - let _ = <$dst<$u> as SizeEq<$src<$t>>>::cast_from_raw(ptr); + use crate::pointer::PtrInner; + + #[allow(invalid_value, unused_unsafe)] + // SAFETY: This code is never executed. + let ptr: PtrInner<'_, $src<$t>> = unsafe { core::mem::MaybeUninit::uninit().assume_init() }; + let _ = <$dst<$u> as SizeFrom<$src<$t>>>::cast_from_raw(ptr); } impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for $src[]); diff --git a/src/wrappers.rs b/src/wrappers.rs index 6d7891354a..9d3a7457be 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -200,7 +200,7 @@ impl Unalign { /// callers may prefer [`DerefMut::deref_mut`], which is infallible. #[inline(always)] pub fn try_deref_mut(&mut self) -> Result<&mut T, AlignmentError<&mut Self, T>> { - let inner = Ptr::from_mut(self).transmute::<_, _, (_, (_, _))>(); + let inner = Ptr::from_mut(self).transmute::<_, _, BecauseBidirectional>(); match inner.try_into_aligned() { Ok(aligned) => Ok(aligned.as_mut()), Err(err) => Err(err.map_src(|src| src.into_unalign().as_mut())), @@ -396,7 +396,10 @@ impl Deref for Unalign { impl DerefMut for Unalign { #[inline(always)] fn deref_mut(&mut self) -> &mut T { - Ptr::from_mut(self).transmute::<_, _, (_, (_, _))>().bikeshed_recall_aligned().as_mut() + Ptr::from_mut(self) + .transmute::<_, _, BecauseBidirectional>() + .bikeshed_recall_aligned() + .as_mut() } } diff --git a/tests/ui-msrv/include_value_not_from_bytes.stderr b/tests/ui-msrv/include_value_not_from_bytes.stderr index 14dd22a71a..dea5dd1fe4 100644 --- a/tests/ui-msrv/include_value_not_from_bytes.stderr +++ b/tests/ui-msrv/include_value_not_from_bytes.stderr @@ -4,9 +4,12 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not sat 19 | const NOT_FROM_BYTES: NotZerocopy = include_value!("../../testdata/include_value/data"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `NotZerocopy` | -note: required by `AssertIsFromBytes` +note: required by a bound in `NOT_FROM_BYTES::transmute` --> tests/ui-msrv/include_value_not_from_bytes.rs:19:42 | 19 | const NOT_FROM_BYTES: NotZerocopy = include_value!("../../testdata/include_value/data"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | required by a bound in this + | required by this bound in `NOT_FROM_BYTES::transmute` = note: this error originates in the macro `$crate::transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-dst-not-frombytes.stderr b/tests/ui-msrv/transmute-dst-not-frombytes.stderr index 744cb48da0..e3f7f5d630 100644 --- a/tests/ui-msrv/transmute-dst-not-frombytes.stderr +++ b/tests/ui-msrv/transmute-dst-not-frombytes.stderr @@ -4,9 +4,12 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not satisfie 19 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); | ^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `NotZerocopy` | -note: required by `AssertIsFromBytes` +note: required by a bound in `DST_NOT_FROM_BYTES::transmute` --> tests/ui-msrv/transmute-dst-not-frombytes.rs:19:41 | 19 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); | ^^^^^^^^^^^^^^^^^^^ + | | + | required by a bound in this + | required by this bound in `DST_NOT_FROM_BYTES::transmute` = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-ptr-to-usize.stderr b/tests/ui-msrv/transmute-ptr-to-usize.stderr index 81d60c71a1..c57f7cdce3 100644 --- a/tests/ui-msrv/transmute-ptr-to-usize.stderr +++ b/tests/ui-msrv/transmute-ptr-to-usize.stderr @@ -4,22 +4,12 @@ error[E0277]: the trait bound `*const usize: IntoBytes` is not satisfied 20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoBytes` is not implemented for `*const usize` | -note: required by `AssertIsIntoBytes` +note: required by a bound in `POINTER_VALUE::transmute` --> tests/ui-msrv/transmute-ptr-to-usize.rs:20:30 | 20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `*const usize: IntoBytes` is not satisfied - --> tests/ui-msrv/transmute-ptr-to-usize.rs:20:30 - | -20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoBytes` is not implemented for `*const usize` - | -note: required by a bound in `AssertIsIntoBytes` - --> tests/ui-msrv/transmute-ptr-to-usize.rs:20:30 - | -20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsIntoBytes` + | | + | required by a bound in this + | required by this bound in `POINTER_VALUE::transmute` = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-size-increase-allow-shrink.rs b/tests/ui-msrv/transmute-size-increase-allow-shrink.rs new file mode 120000 index 0000000000..93b5e6db67 --- /dev/null +++ b/tests/ui-msrv/transmute-size-increase-allow-shrink.rs @@ -0,0 +1 @@ +../ui-nightly/transmute-size-increase-allow-shrink.rs \ No newline at end of file diff --git a/tests/ui-msrv/transmute-size-increase-allow-shrink.stderr b/tests/ui-msrv/transmute-size-increase-allow-shrink.stderr new file mode 100644 index 0000000000..9d03a36302 --- /dev/null +++ b/tests/ui-msrv/transmute-size-increase-allow-shrink.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-msrv/transmute-size-increase-allow-shrink.rs:20:29 + | +20 | const INCREASE_SIZE: AU16 = transmute!(#![allow(shrink)] 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `Transmute` (16 bits) + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-msrv/transmute-src-not-intobytes.stderr b/tests/ui-msrv/transmute-src-not-intobytes.stderr index 6382be909c..1a96a1364c 100644 --- a/tests/ui-msrv/transmute-src-not-intobytes.stderr +++ b/tests/ui-msrv/transmute-src-not-intobytes.stderr @@ -4,22 +4,12 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not sa 19 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::IntoBytes` is not implemented for `NotZerocopy` | -note: required by `AssertIsIntoBytes` +note: required by a bound in `SRC_NOT_AS_BYTES::transmute` --> tests/ui-msrv/transmute-src-not-intobytes.rs:19:32 | 19 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not satisfied - --> tests/ui-msrv/transmute-src-not-intobytes.rs:19:32 - | -19 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::IntoBytes` is not implemented for `NotZerocopy` - | -note: required by a bound in `AssertIsIntoBytes` - --> tests/ui-msrv/transmute-src-not-intobytes.rs:19:32 - | -19 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsIntoBytes` + | | + | required by a bound in this + | required by this bound in `SRC_NOT_AS_BYTES::transmute` = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/include_value_not_from_bytes.stderr b/tests/ui-nightly/include_value_not_from_bytes.stderr index a990f1fa49..a84b34961a 100644 --- a/tests/ui-nightly/include_value_not_from_bytes.stderr +++ b/tests/ui-nightly/include_value_not_from_bytes.stderr @@ -2,10 +2,7 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not sat --> tests/ui-nightly/include_value_not_from_bytes.rs:19:42 | 19 | const NOT_FROM_BYTES: NotZerocopy = include_value!("../../testdata/include_value/data"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | the trait `zerocopy::FromBytes` is not implemented for `NotZerocopy` - | required by a bound introduced by this call + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `NotZerocopy` | = note: Consider adding `#[derive(FromBytes)]` to `NotZerocopy` = help: the following other types implement trait `zerocopy::FromBytes`: @@ -18,9 +15,12 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not sat AtomicIsize AtomicU16 and $N others -note: required by a bound in `AssertIsFromBytes` +note: required by a bound in `NOT_FROM_BYTES::transmute` --> tests/ui-nightly/include_value_not_from_bytes.rs:19:42 | 19 | const NOT_FROM_BYTES: NotZerocopy = include_value!("../../testdata/include_value/data"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | required by a bound in this function + | required by this bound in `transmute` = note: this error originates in the macro `$crate::transmute` which comes from the expansion of the macro `include_value` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-dst-not-frombytes.stderr b/tests/ui-nightly/transmute-dst-not-frombytes.stderr index dfb2ea2b23..c5214a7986 100644 --- a/tests/ui-nightly/transmute-dst-not-frombytes.stderr +++ b/tests/ui-nightly/transmute-dst-not-frombytes.stderr @@ -2,10 +2,7 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not satisfie --> tests/ui-nightly/transmute-dst-not-frombytes.rs:19:41 | 19 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); - | ^^^^^^^^^^^^^^^^^^^ - | | - | the trait `zerocopy::FromBytes` is not implemented for `NotZerocopy` - | required by a bound introduced by this call + | ^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `NotZerocopy` | = note: Consider adding `#[derive(FromBytes)]` to `NotZerocopy` = help: the following other types implement trait `zerocopy::FromBytes`: @@ -18,9 +15,12 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not satisfie AtomicIsize AtomicU16 and $N others -note: required by a bound in `AssertIsFromBytes` +note: required by a bound in `DST_NOT_FROM_BYTES::transmute` --> tests/ui-nightly/transmute-dst-not-frombytes.rs:19:41 | 19 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); - | ^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + | ^^^^^^^^^^^^^^^^^^^ + | | + | required by a bound in this function + | required by this bound in `transmute` = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-ptr-to-usize.stderr b/tests/ui-nightly/transmute-ptr-to-usize.stderr index 6dd1a0deab..783b45ff92 100644 --- a/tests/ui-nightly/transmute-ptr-to-usize.stderr +++ b/tests/ui-nightly/transmute-ptr-to-usize.stderr @@ -9,24 +9,12 @@ error[E0277]: the trait bound `*const usize: IntoBytes` is not satisfied | = note: Consider adding `#[derive(IntoBytes)]` to `*const usize` = help: the trait `IntoBytes` is implemented for `usize` -note: required by a bound in `AssertIsIntoBytes` +note: required by a bound in `POINTER_VALUE::transmute` --> tests/ui-nightly/transmute-ptr-to-usize.rs:20:30 | 20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsIntoBytes` - = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `*const usize: IntoBytes` is not satisfied - --> tests/ui-nightly/transmute-ptr-to-usize.rs:20:30 - | -20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoBytes` is not implemented for `*const usize` - | - = note: Consider adding `#[derive(IntoBytes)]` to `*const usize` - = help: the trait `IntoBytes` is implemented for `usize` -note: required by a bound in `AssertIsIntoBytes` - --> tests/ui-nightly/transmute-ptr-to-usize.rs:20:30 - | -20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsIntoBytes` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | required by a bound in this function + | required by this bound in `transmute` = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-size-increase-allow-shrink.rs b/tests/ui-nightly/transmute-size-increase-allow-shrink.rs new file mode 100644 index 0000000000..4922373ff9 --- /dev/null +++ b/tests/ui-nightly/transmute-size-increase-allow-shrink.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::transmute; + +fn main() {} + +// `transmute!` does not support transmuting from a smaller type to a larger +// one. +const INCREASE_SIZE: AU16 = transmute!(#![allow(shrink)] 0u8); diff --git a/tests/ui-nightly/transmute-size-increase-allow-shrink.stderr b/tests/ui-nightly/transmute-size-increase-allow-shrink.stderr new file mode 100644 index 0000000000..48b51ab814 --- /dev/null +++ b/tests/ui-nightly/transmute-size-increase-allow-shrink.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-nightly/transmute-size-increase-allow-shrink.rs:20:29 + | +20 | const INCREASE_SIZE: AU16 = transmute!(#![allow(shrink)] 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `Transmute` (16 bits) + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-nightly/transmute-src-not-intobytes.stderr b/tests/ui-nightly/transmute-src-not-intobytes.stderr index 67398fc732..5633dad6ca 100644 --- a/tests/ui-nightly/transmute-src-not-intobytes.stderr +++ b/tests/ui-nightly/transmute-src-not-intobytes.stderr @@ -18,33 +18,12 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not sa AtomicI8 AtomicIsize and $N others -note: required by a bound in `AssertIsIntoBytes` +note: required by a bound in `SRC_NOT_AS_BYTES::transmute` --> tests/ui-nightly/transmute-src-not-intobytes.rs:19:32 | 19 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsIntoBytes` - = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not satisfied - --> tests/ui-nightly/transmute-src-not-intobytes.rs:19:32 - | -19 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::IntoBytes` is not implemented for `NotZerocopy` - | - = note: Consider adding `#[derive(IntoBytes)]` to `NotZerocopy` - = help: the following other types implement trait `zerocopy::IntoBytes`: - () - AU16 - AtomicBool - AtomicI16 - AtomicI32 - AtomicI64 - AtomicI8 - AtomicIsize - and $N others -note: required by a bound in `AssertIsIntoBytes` - --> tests/ui-nightly/transmute-src-not-intobytes.rs:19:32 - | -19 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsIntoBytes` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | required by a bound in this function + | required by this bound in `transmute` = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/include_value_not_from_bytes.stderr b/tests/ui-stable/include_value_not_from_bytes.stderr index 3f569ac971..dce1560f39 100644 --- a/tests/ui-stable/include_value_not_from_bytes.stderr +++ b/tests/ui-stable/include_value_not_from_bytes.stderr @@ -2,10 +2,7 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not sat --> tests/ui-stable/include_value_not_from_bytes.rs:19:42 | 19 | const NOT_FROM_BYTES: NotZerocopy = include_value!("../../testdata/include_value/data"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | | - | the trait `zerocopy::FromBytes` is not implemented for `NotZerocopy` - | required by a bound introduced by this call + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `NotZerocopy` | = note: Consider adding `#[derive(FromBytes)]` to `NotZerocopy` = help: the following other types implement trait `zerocopy::FromBytes`: @@ -18,9 +15,12 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not sat AtomicIsize AtomicU16 and $N others -note: required by a bound in `AssertIsFromBytes` +note: required by a bound in `NOT_FROM_BYTES::transmute` --> tests/ui-stable/include_value_not_from_bytes.rs:19:42 | 19 | const NOT_FROM_BYTES: NotZerocopy = include_value!("../../testdata/include_value/data"); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | required by a bound in this function + | required by this bound in `transmute` = note: this error originates in the macro `$crate::transmute` which comes from the expansion of the macro `include_value` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-dst-not-frombytes.stderr b/tests/ui-stable/transmute-dst-not-frombytes.stderr index bc3eac2da8..d5335ceeda 100644 --- a/tests/ui-stable/transmute-dst-not-frombytes.stderr +++ b/tests/ui-stable/transmute-dst-not-frombytes.stderr @@ -2,10 +2,7 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not satisfie --> tests/ui-stable/transmute-dst-not-frombytes.rs:19:41 | 19 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); - | ^^^^^^^^^^^^^^^^^^^ - | | - | the trait `zerocopy::FromBytes` is not implemented for `NotZerocopy` - | required by a bound introduced by this call + | ^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::FromBytes` is not implemented for `NotZerocopy` | = note: Consider adding `#[derive(FromBytes)]` to `NotZerocopy` = help: the following other types implement trait `zerocopy::FromBytes`: @@ -18,9 +15,12 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::FromBytes` is not satisfie AtomicIsize AtomicU16 and $N others -note: required by a bound in `AssertIsFromBytes` +note: required by a bound in `DST_NOT_FROM_BYTES::transmute` --> tests/ui-stable/transmute-dst-not-frombytes.rs:19:41 | 19 | const DST_NOT_FROM_BYTES: NotZerocopy = transmute!(AU16(0)); - | ^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes` + | ^^^^^^^^^^^^^^^^^^^ + | | + | required by a bound in this function + | required by this bound in `transmute` = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-ptr-to-usize.stderr b/tests/ui-stable/transmute-ptr-to-usize.stderr index 3a1c79d804..a3b9f995d1 100644 --- a/tests/ui-stable/transmute-ptr-to-usize.stderr +++ b/tests/ui-stable/transmute-ptr-to-usize.stderr @@ -9,24 +9,12 @@ error[E0277]: the trait bound `*const usize: IntoBytes` is not satisfied | = note: Consider adding `#[derive(IntoBytes)]` to `*const usize` = help: the trait `IntoBytes` is implemented for `usize` -note: required by a bound in `AssertIsIntoBytes` +note: required by a bound in `POINTER_VALUE::transmute` --> tests/ui-stable/transmute-ptr-to-usize.rs:20:30 | 20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsIntoBytes` - = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `*const usize: IntoBytes` is not satisfied - --> tests/ui-stable/transmute-ptr-to-usize.rs:20:30 - | -20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoBytes` is not implemented for `*const usize` - | - = note: Consider adding `#[derive(IntoBytes)]` to `*const usize` - = help: the trait `IntoBytes` is implemented for `usize` -note: required by a bound in `AssertIsIntoBytes` - --> tests/ui-stable/transmute-ptr-to-usize.rs:20:30 - | -20 | const POINTER_VALUE: usize = transmute!(&0usize as *const usize); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsIntoBytes` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | required by a bound in this function + | required by this bound in `transmute` = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-size-increase-allow-shrink.rs b/tests/ui-stable/transmute-size-increase-allow-shrink.rs new file mode 120000 index 0000000000..93b5e6db67 --- /dev/null +++ b/tests/ui-stable/transmute-size-increase-allow-shrink.rs @@ -0,0 +1 @@ +../ui-nightly/transmute-size-increase-allow-shrink.rs \ No newline at end of file diff --git a/tests/ui-stable/transmute-size-increase-allow-shrink.stderr b/tests/ui-stable/transmute-size-increase-allow-shrink.stderr new file mode 100644 index 0000000000..9ad2760b91 --- /dev/null +++ b/tests/ui-stable/transmute-size-increase-allow-shrink.stderr @@ -0,0 +1,9 @@ +error[E0512]: cannot transmute between types of different sizes, or dependently-sized types + --> tests/ui-stable/transmute-size-increase-allow-shrink.rs:20:29 + | +20 | const INCREASE_SIZE: AU16 = transmute!(#![allow(shrink)] 0u8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: source type: `u8` (8 bits) + = note: target type: `Transmute` (16 bits) + = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-stable/transmute-src-not-intobytes.stderr b/tests/ui-stable/transmute-src-not-intobytes.stderr index 41fc51f767..76af81eece 100644 --- a/tests/ui-stable/transmute-src-not-intobytes.stderr +++ b/tests/ui-stable/transmute-src-not-intobytes.stderr @@ -18,33 +18,12 @@ error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not sa AtomicI8 AtomicIsize and $N others -note: required by a bound in `AssertIsIntoBytes` +note: required by a bound in `SRC_NOT_AS_BYTES::transmute` --> tests/ui-stable/transmute-src-not-intobytes.rs:19:32 | 19 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsIntoBytes` - = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the trait bound `NotZerocopy: zerocopy::IntoBytes` is not satisfied - --> tests/ui-stable/transmute-src-not-intobytes.rs:19:32 - | -19 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `zerocopy::IntoBytes` is not implemented for `NotZerocopy` - | - = note: Consider adding `#[derive(IntoBytes)]` to `NotZerocopy` - = help: the following other types implement trait `zerocopy::IntoBytes`: - () - AU16 - AtomicBool - AtomicI16 - AtomicI32 - AtomicI64 - AtomicI8 - AtomicIsize - and $N others -note: required by a bound in `AssertIsIntoBytes` - --> tests/ui-stable/transmute-src-not-intobytes.rs:19:32 - | -19 | const SRC_NOT_AS_BYTES: AU16 = transmute!(NotZerocopy(AU16(0))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsIntoBytes` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | required by a bound in this function + | required by this bound in `transmute` = note: this error originates in the macro `transmute` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/zerocopy-derive/src/enum.rs b/zerocopy-derive/src/enum.rs index 50e632d383..8de833602d 100644 --- a/zerocopy-derive/src/enum.rs +++ b/zerocopy-derive/src/enum.rs @@ -332,7 +332,7 @@ pub(crate) fn derive_is_bit_valid( // is `Initialized`. Since we have not written uninitialized // bytes into the referent, `tag_ptr` is also `Initialized`. let tag_ptr = unsafe { tag_ptr.assume_initialized() }; - tag_ptr.recall_validity::<_, (_, (_, _))>().read_unaligned::<#zerocopy_crate::BecauseImmutable>() + tag_ptr.recall_validity().read_unaligned::<#zerocopy_crate::BecauseImmutable>() }; // SAFETY: diff --git a/zerocopy-derive/src/output_tests.rs b/zerocopy-derive/src/output_tests.rs index eb991c5b23..a6ffe89606 100644 --- a/zerocopy-derive/src/output_tests.rs +++ b/zerocopy-derive/src/output_tests.rs @@ -799,7 +799,7 @@ fn test_try_from_bytes_enum() { candidate.reborrow().cast_unsized_unchecked(|p: ::zerocopy::pointer::PtrInner<'_, Self>| { p.cast_sized::<___ZerocopyTagPrimitive>() }) }; let tag_ptr = unsafe { tag_ptr.assume_initialized() }; - tag_ptr.recall_validity::<_, (_, (_, _))>().read_unaligned::<::zerocopy::BecauseImmutable>() + tag_ptr.recall_validity().read_unaligned::<::zerocopy::BecauseImmutable>() }; let raw_enum = unsafe { candidate.cast_unsized_unchecked(|p: ::zerocopy::pointer::PtrInner<'_, Self>| { p.cast_sized::<___ZerocopyRawEnum<'a, N, X, Y>>() }) @@ -1140,7 +1140,7 @@ fn test_try_from_bytes_enum() { candidate.reborrow().cast_unsized_unchecked(|p: ::zerocopy::pointer::PtrInner<'_, Self>| { p.cast_sized::<___ZerocopyTagPrimitive> ()}) }; let tag_ptr = unsafe { tag_ptr.assume_initialized() }; - tag_ptr.recall_validity::<_, (_, (_, _))>().read_unaligned::<::zerocopy::BecauseImmutable>() + tag_ptr.recall_validity().read_unaligned::<::zerocopy::BecauseImmutable>() }; let raw_enum = unsafe { candidate.cast_unsized_unchecked(|p: ::zerocopy::pointer::PtrInner<'_, Self>| { p.cast_sized::<___ZerocopyRawEnum<'a, N, X, Y>> ()}) @@ -1481,7 +1481,7 @@ fn test_try_from_bytes_enum() { candidate.reborrow().cast_unsized_unchecked(|p: ::zerocopy::pointer::PtrInner<'_, Self>| { p.cast_sized::<___ZerocopyTagPrimitive> ()}) }; let tag_ptr = unsafe { tag_ptr.assume_initialized() }; - tag_ptr.recall_validity::<_, (_, (_, _))>().read_unaligned::<::zerocopy::BecauseImmutable>() + tag_ptr.recall_validity().read_unaligned::<::zerocopy::BecauseImmutable>() }; let raw_enum = unsafe { candidate.cast_unsized_unchecked(|p: ::zerocopy::pointer::PtrInner<'_, Self>| { p.cast_sized::<___ZerocopyRawEnum<'a, N, X, Y>> ()})