diff --git a/src/layout.rs b/src/layout.rs index 367edbae9e..59f59cfb89 100644 --- a/src/layout.rs +++ b/src/layout.rs @@ -77,6 +77,29 @@ impl SizeInfo { } } +#[cfg(kani)] +impl TrailingSliceLayout { + fn is_valid_metadata(self, align: NonZeroUsize, meta: usize) -> bool { + let Some(trailing_slice_size) = self.elem_size.checked_mul(meta) else { + return false; + }; + + let Some(unpadded_size) = self.offset.checked_add(trailing_slice_size) else { + return false; + }; + + if unpadded_size >= isize::MAX as usize { + return false; + } + + let trailing_padding = util::padding_needed_for(unpadded_size, align); + + let Some(size) = unpadded_size.checked_add(trailing_padding) else { return false }; + + size <= isize::MAX as usize + } +} + #[doc(hidden)] #[derive(Copy, Clone)] #[cfg_attr(test, derive(Debug))] @@ -612,6 +635,34 @@ pub(crate) use cast_from_raw::cast_from_raw; mod cast_from_raw { use crate::{pointer::PtrInner, *}; + #[cfg_attr(kani, kani::ensures(|&gcd| + offset_delta % gcd == 0 + && src_elem_size % gcd == 0 + && dst_elem_size.get() % gcd == 0 + ))] + #[cfg_attr(kani, kani::recursion)] + const fn gcd( + offset_delta: usize, + src_elem_size: usize, + dst_elem_size: NonZeroUsize, + ) -> NonZeroUsize { + const fn gcd(a: usize, b: usize) -> usize { + if a == 0 { + b + } else { + #[allow(clippy::arithmetic_side_effects)] + gcd(b % a, a) + } + } + + let gcd = gcd(gcd(offset_delta, src_elem_size), dst_elem_size.get()); + + match NonZeroUsize::new(gcd) { + Some(gcd) => gcd, + None => const_panic!("gcd should be non-zero, because dst_elem_size is non-zero"), + } + } + /// Implements [`>::cast_from_raw`][cast_from_raw]. /// /// # PME @@ -623,11 +674,15 @@ mod 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> + pub(crate) fn cast_from_raw( + src: PtrInner<'_, Src>, + ) -> PtrInner<'_, Dst> where Src: KnownLayout + ?Sized, Dst: KnownLayout + ?Sized, { + // TODO: Update this comment. + // // At compile time (specifically, post-monomorphization time), we need // to compute two things: // - Whether, given *any* `*Src`, it is possible to construct a `*Dst` @@ -693,13 +748,20 @@ mod cast_from_raw { /// /// `Src`'s alignment must not be smaller than `Dst`'s alignment. #[derive(Copy, Clone)] - struct CastParams { - offset_delta_elems: usize, - elem_multiple: usize, + pub(super) struct CastParams { + // `offset_delta / dst.elem_size = offset_delta_elems_num / denom` + offset_delta_elems_num: usize, + // `src.elem_size / dst.elem_size = elem_multiple_num / denom` + elem_multiple_num: usize, + denom: NonZeroUsize, } impl CastParams { - const fn try_compute(src: &DstLayout, dst: &DstLayout) -> Option { + const fn try_compute( + src: &DstLayout, + dst: &DstLayout, + allow_shrink: bool, + ) -> Option { if src.align.get() < dst.align.get() { return None; } @@ -724,33 +786,35 @@ mod cast_from_raw { return None; }; - // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero. - #[allow(clippy::arithmetic_side_effects)] - let delta_mod_other_elem = offset_delta % dst_elem_size.get(); + let gcd = gcd(offset_delta, src.elem_size, dst_elem_size).get(); - // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero. + // PANICS: `gcd` is non-zero. #[allow(clippy::arithmetic_side_effects)] - let elem_remainder = src.elem_size % dst_elem_size.get(); - - if delta_mod_other_elem != 0 || src.elem_size < dst.elem_size || elem_remainder != 0 - { - return None; - } + let offset_delta_elems_num = offset_delta / gcd; - // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero. + // PANICS: `gcd` is non-zero. #[allow(clippy::arithmetic_side_effects)] - let offset_delta_elems = offset_delta / dst_elem_size.get(); + let elem_multiple_num = src.elem_size / gcd; - // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero. + // PANICS: `dst_elem_size` is non-zero, and `gcd` is no greater + // than it by construction. Thus, this should be at least 1. #[allow(clippy::arithmetic_side_effects)] - let elem_multiple = src.elem_size / dst_elem_size.get(); + let denom = match NonZeroUsize::new(dst_elem_size.get() / gcd) { + Some(d) => d, + None => const_panic!("CastParams::try_compute: denom should be non-zero"), + }; + + if denom.get() != 1 && !allow_shrink { + return None; + } // SAFETY: We checked above that `src.align >= dst.align`. Some(CastParams { // SAFETY: We checked above that this is an exact ratio. - offset_delta_elems, + offset_delta_elems_num, // SAFETY: We checked above that this is an exact ratio. - elem_multiple, + elem_multiple_num, + denom, }) } @@ -759,12 +823,17 @@ mod cast_from_raw { /// `src_meta` describes a `Src` whose size is no larger than /// `isize::MAX`. /// - /// The returned metadata describes a `Dst` of the same size as the - /// original `Src`. + /// If `self.denom == 1`, then the returned metadata describes a + /// `Dst` of the same size as the original `Src`. Otherwise, the + /// returned metadata describes a `Dst` whose size is no greater + /// than the size of the original `Src`. unsafe fn cast_metadata(self, src_meta: usize) -> usize { #[allow(unused)] use crate::util::polyfills::*; + // TODO: Update this safety comment. Make sure that even if + // `denom > 1`, these arithmetic operations will not overflow. + // // SAFETY: `self` is a witness that the following equation // holds: // @@ -774,24 +843,61 @@ mod cast_from_raw { // metadata, this math will not overflow, and the returned value // will describe a `Dst` of the same size. #[allow(unstable_name_collisions)] - unsafe { - self.offset_delta_elems - .unchecked_add(src_meta.unchecked_mul(self.elem_multiple)) - } + let num = unsafe { + self.offset_delta_elems_num + .unchecked_add(src_meta.unchecked_mul(self.elem_multiple_num)) + }; + num / self.denom + } + } + + // Prove that `CastParams::try_compute` does not panic, and that + // `CastParams::cast_metadata` does not exhibit UB. Ideally, we would + // also prove that that a `dst` with `dst_metadata` is no larger than a + // `src` with `src_metadata`, but this is beyond kani's ability to prove + // in a reasonable time. + #[cfg_attr(kani, kani::proof)] + #[cfg_attr(kani, kani::stub_verified(gcd))] + fn proof() { + let src: DstLayout = kani::any(); + let dst: DstLayout = kani::any(); + let allow_shrink: bool = true; + + let SizeInfo::SliceDst(src_size_info) = src.size_info else { + kani::assume(false); + loop {} + }; + + let SizeInfo::SliceDst(_) = dst.size_info else { + kani::assume(false); + loop {} + }; + + let params = CastParams::try_compute(&src, &dst, allow_shrink); + + if let Some(params) = params { + let src_meta = { + let meta: usize = kani::any(); + kani::assume(src_size_info.is_valid_metadata(src.align, meta)); + meta + }; + + // SAFETY: Kani will reject the proof if this invocation is unsound. + let _dst_meta = unsafe { params.cast_metadata(src_meta) }; } } - trait Params { + trait Params { const CAST_PARAMS: CastParams; } - impl Params for Dst + impl Params for Dst where Src: KnownLayout + ?Sized, Dst: KnownLayout + ?Sized, { const CAST_PARAMS: CastParams = - match CastParams::try_compute(&Src::LAYOUT, &Dst::LAYOUT) { + match CastParams::try_compute(&Src::LAYOUT, &Dst::LAYOUT, ALLOW_SHRINK) { Some(params) => params, None => const_panic!( "cannot `transmute_ref!` or `transmute_mut!` between incompatible types" @@ -800,7 +906,7 @@ mod cast_from_raw { } let src_meta = ::pointer_to_metadata(src.as_non_null().as_ptr()); - let params = >::CAST_PARAMS; + let params = >::CAST_PARAMS; // SAFETY: `src: PtrInner`, and so by invariant on `PtrInner`, `src`'s // referent is no larger than `isize::MAX`. @@ -809,11 +915,11 @@ mod cast_from_raw { let dst = ::raw_from_ptr_len(src.as_non_null().cast(), dst_meta); // SAFETY: By post-condition on `params.cast_metadata`, `dst` addresses - // the same number of bytes as `src`. Since `src: PtrInner`, `src` has - // provenance for its entire referent, which lives inside of a single - // allocation. Since `dst` has the same address as `src` and was - // constructed using provenance-preserving operations, it addresses a - // subset of those bytes, and has provenance for those bytes. + // no more bytes than `src`. Since `src: PtrInner`, `src` has provenance + // for its entire referent, which lives inside of a single allocation. + // Since `dst` has the same address as `src` and was constructed using + // provenance-preserving operations, it addresses a subset of those + // bytes, and has provenance for those bytes. unsafe { PtrInner::new(dst) } } } @@ -823,7 +929,6 @@ mod cast_from_raw { // putting it here works. Once our MSRV is high enough that this bug has been // fixed, remove this `allow`. #[allow(unknown_lints)] -#[cfg(test)] mod tests { use super::*; diff --git a/src/macros.rs b/src/macros.rs index 2f502ebc81..a01681444a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -227,6 +227,24 @@ macro_rules! transmute { /// assert_eq!(size_of_val(src), size_of_val(dst)); /// ``` /// +/// ## `#![allow(shrink)]` +/// +/// If `#![allow(shrink)]` is provided, `transmute_ref!` additionally supports +/// transmutations that shrink the size of the referent; e.g.: +/// +/// ``` +/// # use zerocopy::transmute_ref; +/// # use core::mem::size_of_val; // Not in the prelude on our MSRV +/// let src: &[[u8; 3]] = &[[0, 1, 2], [3, 4, 5], [6, 7, 8]][..]; +/// let dst: &[[u8; 2]] = transmute_ref!(#![allow(shrink)] src); +/// +/// assert_eq!(src.len(), 3); +/// assert_eq!(dst.len(), 4); +/// assert_eq!(size_of_val(src), 9); +/// assert_eq!(size_of_val(dst), 8); +/// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]); +/// ``` +/// /// # Errors /// /// Violations of the alignment and size compatibility checks are detected @@ -313,7 +331,18 @@ macro_rules! transmute { /// `Dst: Sized`. #[macro_export] macro_rules! transmute_ref { - ($e:expr) => {{ + (#![allow(shrink)] $e:expr) => { + $crate::__transmute_ref_inner!(true, $e) + }; + ($e:expr) => { + $crate::__transmute_ref_inner!(false, $e) + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __transmute_ref_inner { + ($allow_shrink:literal, $e:expr) => {{ // NOTE: This must be a macro (rather than a function with trait bounds) // because there's no way, in a generic context, to enforce that two // types have the same size or alignment. @@ -352,10 +381,10 @@ macro_rules! transmute_ref { // - `Src: IntoBytes + Immutable` // - `Dst: FromBytes + Immutable` unsafe { - t.transmute_ref() + t.transmute_ref::<$allow_shrink>() } } - }} + }}; } /// Safely transmutes a mutable reference of one type to a mutable reference of @@ -401,6 +430,29 @@ macro_rules! transmute_ref { /// assert_eq!(size_of_val(src), dst_size); /// ``` /// +/// ## `#![allow(shrink)]` +/// +/// If `#![allow(shrink)]` is provided, `transmute_mut!` additionally supports +/// transmutations that shrink the size of the referent; e.g.: +/// +/// ``` +/// # use zerocopy::transmute_mut; +/// # use core::mem::size_of_val; // Not in the prelude on our MSRV +/// let src: &mut [[u8; 3]] = &mut [[0, 1, 2], [3, 4, 5], [6, 7, 8]][..]; +/// let dst: &mut [[u8; 2]] = transmute_mut!(#![allow(shrink)] src); +/// +/// +/// let dst_len = dst.len(); +/// let dst_size = size_of_val(dst); +/// assert_eq!(dst, [[0, 1], [2, 3], [4, 5], [6, 7]]); +/// +/// assert_eq!(src.len(), 3); +/// assert_eq!(dst_len, 4); +/// +/// assert_eq!(size_of_val(src), 9); +/// assert_eq!(dst_size, 8); +/// ``` +/// /// # Errors /// /// Violations of the alignment and size compatibility checks are detected @@ -489,7 +541,18 @@ macro_rules! transmute_ref { /// ``` #[macro_export] macro_rules! transmute_mut { - ($e:expr) => {{ + (#![allow(shrink)] $e:expr) => { + $crate::__transmute_mut_inner!(true, $e) + }; + ($e:expr) => { + $crate::__transmute_mut_inner!(false, $e) + }; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __transmute_mut_inner { + ($allow_shrink:literal, $e:expr) => {{ // NOTE: This must be a macro (rather than a function with trait bounds) // because, for backwards-compatibility on v0.8.x, we use the autoref // specialization trick to dispatch to different `transmute_mut` @@ -503,7 +566,7 @@ macro_rules! transmute_mut { #[allow(unused)] use $crate::util::macro_util::TransmuteMutDst as _; let t = $crate::util::macro_util::Wrap::new(e); - t.transmute_mut() + t.transmute_mut::<$allow_shrink>() }} } @@ -1251,6 +1314,11 @@ mod tests { let slice_of_u16s: &[U16] = <[U16]>::ref_from_bytes(&[0, 1, 2, 3, 4, 5][..]).unwrap(); assert_eq!(x, slice_of_u16s); + // Test that transmuting from a larger sized type to a smaller sized + // type works. + let x: &u8 = transmute_ref!(#![allow(shrink)] &0u16); + assert_eq!(*x, 0); + // Test that transmuting from a type with larger trailing slice offset // and larger trailing slice element works. let bytes = &[0, 1, 2, 3, 4, 5, 6, 7][..]; @@ -1259,6 +1327,15 @@ mod tests { let x: &SliceDst = transmute_ref!(slice_dst_big); assert_eq!(x, slice_dst_small); + let bytes = &[0, 1, 2, 3, 4, 5, 6, 7][..]; + let slice_dst_big = SliceDst::<[u8; 4], [u8; 4]>::ref_from_bytes(bytes).unwrap(); + let slice_dst_small = SliceDst::<[u8; 3], [u8; 3]>::ref_from_bytes(&bytes[..6]).unwrap(); + let x: &SliceDst<[u8; 3], [u8; 3]> = transmute_ref!( + #![allow(shrink)] + slice_dst_big + ); + assert_eq!(x, slice_dst_small); + // Test that it's legal to transmute a reference while shrinking the // lifetime (note that `X` has the lifetime `'static`). let x: &[u8; 8] = transmute_ref!(X); @@ -1439,6 +1516,14 @@ mod tests { let x: &mut [i16] = transmute_mut!(array_of_u16s); assert_eq!(x, array_of_i16s); + // Test that transmuting from a larger sized type to a smaller sized + // type works. + let mut large: [u8; 2] = [1, 1]; + let x: &mut u8 = transmute_mut!(#![allow(shrink)] &mut large); + assert_eq!(*x, 1); + *x = 0; + assert_eq!(large, [0, 1]); + // Test that transmuting from a type with larger trailing slice offset // and larger trailing slice element works. let mut bytes = [0, 1, 2, 3, 4, 5, 6, 7]; @@ -1447,6 +1532,16 @@ mod tests { let slice_dst_small = SliceDst::::mut_from_bytes(&mut bytes[..]).unwrap(); let x: &mut SliceDst = transmute_mut!(slice_dst_big); assert_eq!(x, slice_dst_small); + + let mut bytes = [0, 1, 2, 3, 4, 5, 6, 7]; + let slice_dst_big = SliceDst::<[u8; 4], [u8; 4]>::mut_from_bytes(&mut bytes[..]).unwrap(); + let mut bytes = [0, 1, 2, 3, 4, 5]; + let slice_dst_small = SliceDst::<[u8; 3], [u8; 3]>::mut_from_bytes(&mut bytes[..]).unwrap(); + let x: &mut SliceDst<[u8; 3], [u8; 3]> = transmute_mut!( + #![allow(shrink)] + slice_dst_big + ); + assert_eq!(x, slice_dst_small); } #[test] diff --git a/src/util/macro_util.rs b/src/util/macro_util.rs index b2326e1ff5..3ed96ee3dc 100644 --- a/src/util/macro_util.rs +++ b/src/util/macro_util.rs @@ -711,8 +711,12 @@ impl<'a, Src, Dst> Wrap<&'a Src, &'a Dst> { /// - `mem::align_of::() <= mem::align_of::()` #[inline(always)] #[must_use] - pub const unsafe fn transmute_ref(self) -> &'a Dst { - static_assert!(Src, Dst => mem::size_of::() == mem::size_of::()); + pub const unsafe fn transmute_ref(self) -> &'a Dst { + static_assert!(Src, Dst, @const ALLOW_SHRINK: bool => if ALLOW_SHRINK { + mem::size_of::() <= mem::size_of::() + } else { + mem::size_of::() == mem::size_of::() + }); static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); let src: *const Src = self.0; @@ -748,12 +752,16 @@ impl<'a, Src, Dst> Wrap<&'a mut Src, &'a mut Dst> { /// - `mem::align_of::() <= mem::align_of::()` #[inline(always)] #[must_use] - pub fn transmute_mut(self) -> &'a mut Dst + pub fn transmute_mut(self) -> &'a mut Dst where Src: FromBytes + IntoBytes, Dst: FromBytes + IntoBytes, { - static_assert!(Src, Dst => mem::size_of::() == mem::size_of::()); + static_assert!(Src, Dst, @const ALLOW_SHRINK: bool => if ALLOW_SHRINK { + mem::size_of::() <= mem::size_of::() + } else { + mem::size_of::() == mem::size_of::() + }); static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); let src: *mut Src = self.0; @@ -775,7 +783,7 @@ pub trait TransmuteRefDst<'a> { type Dst: ?Sized; #[must_use] - fn transmute_ref(self) -> &'a Self::Dst; + fn transmute_ref(self) -> &'a Self::Dst; } impl<'a, Src: ?Sized, Dst: ?Sized> TransmuteRefDst<'a> for Wrap<&'a Src, &'a Dst> @@ -786,18 +794,18 @@ where type Dst = Dst; #[inline(always)] - fn transmute_ref(self) -> &'a Dst { + fn transmute_ref(self) -> &'a Dst { static_assert!(Src: ?Sized + KnownLayout, Dst: ?Sized + KnownLayout => { Src::LAYOUT.align.get() >= Dst::LAYOUT.align.get() }, "cannot transmute reference when destination type has higher alignment than source type"); // SAFETY: We only use `S` as `S` and `D` as `D`. unsafe { - unsafe_with_size_from!(, D> { + unsafe_with_size_from!(, D>, { let ptr = Ptr::from_ref(self.0) .transmute::, invariant::Valid, _>() .recall_validity::() - .transmute::, invariant::Initialized, _>() + .transmute::, invariant::Initialized, _>() .recall_validity::(); #[allow(unused_unsafe)] @@ -815,7 +823,7 @@ where pub trait TransmuteMutDst<'a> { type Dst: ?Sized; #[must_use] - fn transmute_mut(self) -> &'a mut Self::Dst; + fn transmute_mut(self) -> &'a mut Self::Dst; } impl<'a, Src: ?Sized, Dst: ?Sized> TransmuteMutDst<'a> for Wrap<&'a mut Src, &'a mut Dst> @@ -826,18 +834,18 @@ where type Dst = Dst; #[inline(always)] - fn transmute_mut(self) -> &'a mut Dst { + fn transmute_mut(self) -> &'a mut Dst { static_assert!(Src: ?Sized + KnownLayout, Dst: ?Sized + KnownLayout => { Src::LAYOUT.align.get() >= Dst::LAYOUT.align.get() }, "cannot transmute reference when destination type has higher alignment than source type"); // SAFETY: We only use `S` as `S` and `D` as `D`. unsafe { - unsafe_with_size_from!(, D> { + unsafe_with_size_from!(, D>, { let ptr = Ptr::from_mut(self.0) .transmute::, invariant::Valid, _>() .recall_validity::() - .transmute::, invariant::Initialized, _>() + .transmute::, invariant::Initialized, _>() .recall_validity::(); #[allow(unused_unsafe)] diff --git a/src/util/macros.rs b/src/util/macros.rs index c3da74127e..deaf5f5c9f 100644 --- a/src/util/macros.rs +++ b/src/util/macros.rs @@ -163,6 +163,7 @@ macro_rules! unsafe_impl { macro_rules! impl_for_transmute_from { ( $(#[$attr:meta])* + $(@const $constname:ident : $constty:ident $(,)?)? $($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?)? => $trait:ident for $ty:ty [$($unsafe_cell:ident)? <$repr:ty>] ) => { @@ -178,7 +179,7 @@ macro_rules! impl_for_transmute_from { // and `IntoBytes` - are defined only in terms of the bit validity // of a type. Therefore, `$repr: $trait` ensures that `$ty: $trait` // is sound. - unsafe impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?)?> $trait for $ty { + unsafe impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?)? $(, const $constname: $constty)?> $trait for $ty { #[allow(dead_code, clippy::missing_inline_in_public_items)] #[cfg_attr(all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), coverage(off))] fn only_derive_is_allowed_to_implement_this_trait() { @@ -195,7 +196,7 @@ macro_rules! impl_for_transmute_from { } #[cfg_attr(all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), coverage(off))] - fn f<$($tyvar $(: $(? $optbound +)* $($bound +)*)?)?>() { + fn f<$($tyvar $(: $(? $optbound +)* $($bound +)*)?)? $(, const $constname: $constty)?>() { is_trait::<$ty, $repr>(); } } @@ -233,7 +234,7 @@ macro_rules! impl_for_transmute_from { TryFromBytes for $ty:ty [<$repr:ty>] ) => { #[inline] - fn is_bit_valid(candidate: $crate::Maybe<'_, Self, A>) -> bool { + fn is_bit_valid<__A: crate::pointer::invariant::Reference>(candidate: $crate::Maybe<'_, Self, __A>) -> bool { // 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`. @@ -646,20 +647,23 @@ macro_rules! static_assert { const_assert!(::ASSERT); }}; - ($($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),* => $condition:expr $(, $args:tt)*) => {{ + ($($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?),* $(,)? $(@const $constname:ident : bool)? => $condition:expr $(, $args:tt)*) => {{ trait StaticAssert { const ASSERT: bool; } + #[allow(dead_code)] + struct ConstBool; + // NOTE: We use `PhantomData` so we can support unsized types. - impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?,)*> StaticAssert for ($(core::marker::PhantomData<$tyvar>,)*) { + impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?,)* $(const $constname : bool)?> StaticAssert for ($(core::marker::PhantomData<$tyvar>,)* $(ConstBool<$constname>)?) { const ASSERT: bool = { const_assert!($condition $(, $args)*); $condition }; } - const_assert!(<($(core::marker::PhantomData<$tyvar>,)*) as StaticAssert>::ASSERT); + const_assert!(<($(core::marker::PhantomData<$tyvar>,)* $(ConstBool<$constname>)?) as StaticAssert>::ASSERT); }}; } @@ -714,7 +718,7 @@ macro_rules! cast { /// 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) => {{ + ($(@const $constname:ident : $constty:ident,)? T $(: ?$optbound:ident)? => $wrapper:ident) => {{ crate::util::macros::__unsafe(); use crate::pointer::{TransmuteFrom, TransmuteOverwrite, PtrInner, SizeFrom, invariant::Valid}; @@ -722,27 +726,27 @@ macro_rules! unsafe_impl_for_transparent_wrapper { // SAFETY: The caller promises that `T` and `$wrapper` have the same // bit validity, and that both implementations of // `SizeFrom::cast_from_raw` preserve referent size exactly. - unsafe impl TransmuteFrom for $wrapper {} + unsafe impl TransmuteFrom for $wrapper {} // SAFETY: See previous safety comment. - unsafe impl TransmuteFrom<$wrapper, Valid, Valid> for T {} + unsafe impl TransmuteFrom<$wrapper, Valid, Valid> for T {} // SAFETY: See previous safety comment. - unsafe impl TransmuteOverwrite for $wrapper {} + unsafe impl TransmuteOverwrite for $wrapper {} // SAFETY: See previous safety comment. - unsafe impl TransmuteOverwrite<$wrapper, Valid, Valid> for T {} + unsafe impl TransmuteOverwrite<$wrapper, Valid, Valid> for T {} // SAFETY: The caller promises that `T` and `$wrapper` satisfy // `cast_from_raw`'s safety post-condition. - unsafe impl SizeFrom for $wrapper { + unsafe impl SizeFrom for $wrapper { #[inline(always)] - fn cast_from_raw(t: PtrInner<'_, T>) -> PtrInner<'_, $wrapper> { + fn cast_from_raw(t: PtrInner<'_, T>) -> PtrInner<'_, $wrapper> { // 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 SizeFrom<$wrapper> for T { + unsafe impl SizeFrom<$wrapper> for T { #[inline(always)] - fn cast_from_raw(t: PtrInner<'_, $wrapper>) -> PtrInner<'_, T> { + fn cast_from_raw(t: PtrInner<'_, $wrapper>) -> PtrInner<'_, T> { // SAFETY: See previous safety comment. unsafe { cast!(t) } } @@ -837,7 +841,7 @@ macro_rules! impl_size_from { /// and `$dst<$u>`. The caller must not use `$src` or `$dst` to wrap any other /// types. macro_rules! unsafe_with_size_from { - (<$src:ident<$t:ident>, $dst:ident<$u:ident>> $blk:expr) => {{ + (<$src:ident<$t:ident>, $dst:ident<$u:ident, {$allow_shrink:expr}>>, $blk:expr) => {{ crate::util::macros::__unsafe(); use crate::{KnownLayout, pointer::PtrInner}; @@ -846,7 +850,7 @@ macro_rules! unsafe_with_size_from { struct $src(T); #[repr(transparent)] - struct $dst(U); + struct $dst(U); // SAFETY: Since `$src` is a `#[repr(transparent)]` wrapper around // `T`, it has the same bit validity and size as `T`. @@ -854,7 +858,7 @@ macro_rules! unsafe_with_size_from { // SAFETY: Since `$dst` is a `#[repr(transparent)]` wrapper around // `T`, it has the same bit validity and size as `T`. - unsafe_impl_for_transparent_wrapper!(T: ?Sized => $dst); + unsafe_impl_for_transparent_wrapper!(@const A: bool, T: ?Sized => $dst); // SAFETY: `$src` is a `#[repr(transparent)]` wrapper around `T` with // no added semantics. @@ -862,7 +866,7 @@ macro_rules! unsafe_with_size_from { // SAFETY: `$dst` is a `#[repr(transparent)]` wrapper around `T` with // no added semantics. - unsafe impl InvariantsEq<$dst> for T {} + unsafe impl InvariantsEq<$dst> for T {} // SAFETY: See inline for the soundness of this impl when // `cast_from_raw` is actually instantiated (otherwise, PMEs may not be @@ -871,7 +875,7 @@ macro_rules! unsafe_with_size_from { // 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 SizeFrom<$src> for $dst + unsafe impl SizeFrom<$src> for $dst where T: KnownLayout, U: KnownLayout, @@ -889,7 +893,7 @@ macro_rules! unsafe_with_size_from { // SAFETY: By the preceding safety comment, this cast preserves // referent size. let src: PtrInner<'_, T> = unsafe { cast!(src) }; - let dst: PtrInner<'_, U> = crate::layout::cast_from_raw(src); + let dst: PtrInner<'_, U> = crate::layout::cast_from_raw::<_, _, ALLOW_SHRINK>(src); // SAFETY: By the preceding safety comment, this cast preserves // referent size. unsafe { cast!(dst) } @@ -904,7 +908,7 @@ macro_rules! unsafe_with_size_from { #[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); + let _ = <$dst<$u, $allow_shrink> as SizeFrom<$src<$t>>>::cast_from_raw(ptr); } impl_for_transmute_from!(T: ?Sized + TryFromBytes => TryFromBytes for $src[]); @@ -912,10 +916,10 @@ macro_rules! unsafe_with_size_from { impl_for_transmute_from!(T: ?Sized + FromZeros => FromZeros for $src[]); impl_for_transmute_from!(T: ?Sized + IntoBytes => IntoBytes for $src[]); - impl_for_transmute_from!(U: ?Sized + TryFromBytes => TryFromBytes for $dst[]); - impl_for_transmute_from!(U: ?Sized + FromBytes => FromBytes for $dst[]); - impl_for_transmute_from!(U: ?Sized + FromZeros => FromZeros for $dst[]); - impl_for_transmute_from!(U: ?Sized + IntoBytes => IntoBytes for $dst[]); + impl_for_transmute_from!(@const A: bool, U: ?Sized + TryFromBytes => TryFromBytes for $dst[]); + impl_for_transmute_from!(@const A: bool, U: ?Sized + FromBytes => FromBytes for $dst[]); + impl_for_transmute_from!(@const A: bool, U: ?Sized + FromZeros => FromZeros for $dst[]); + impl_for_transmute_from!(@const A: bool, U: ?Sized + IntoBytes => IntoBytes for $dst[]); // SAFETY: `$src` is a `#[repr(transparent)]` wrapper around `T`, and // so permits interior mutation exactly when `T` does. @@ -923,7 +927,7 @@ macro_rules! unsafe_with_size_from { // SAFETY: `$dst` is a `#[repr(transparent)]` wrapper around `T`, and // so permits interior mutation exactly when `T` does. - unsafe_impl!(T: ?Sized + Immutable => Immutable for $dst); + unsafe_impl!(const A: bool, T: ?Sized + Immutable => Immutable for $dst); $blk }}; diff --git a/src/util/mod.rs b/src/util/mod.rs index 3fba7cc760..972e9d08bf 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -119,7 +119,6 @@ pub(crate) fn validate_aligned_to(t: T) -> Result<(), Alignment /// on the answer it gives if this is not the case. #[cfg_attr( kani, - kani::requires(len <= isize::MAX as usize), kani::requires(align.is_power_of_two()), kani::ensures(|&p| (len + p) % align.get() == 0), // Ensures that we add the minimum required padding.