diff --git a/src/error.rs b/src/error.rs index 30339d777c..1719abf048 100644 --- a/src/error.rs +++ b/src/error.rs @@ -653,6 +653,20 @@ impl Clone for ValidityError { } } +// SAFETY: `ValidityError` contains a single `Self::Inner = Src`, and no other +// non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner` to `f`. +unsafe impl crate::pointer::TryWithError + for crate::ValidityError +where + Dst: TryFromBytes + ?Sized, +{ + type Inner = Src; + type Mapped = crate::ValidityError; + fn map NewSrc>(self, f: F) -> Self::Mapped { + self.map_src(f) + } +} + impl PartialEq for ValidityError { #[inline] fn eq(&self, other: &Self) -> bool { @@ -775,6 +789,22 @@ impl CastError { } } +// SAFETY: `CastError` is either a single `AlignmentError` or a single +// `SizeError`. In either case, it contains a single `Self::Inner = Src`, and no +// other non-ZST fields. `map` passes ownership of `self`'s sole `Self::Inner` +// to `f`. +unsafe impl crate::pointer::TryWithError for crate::CastError +where + Dst: ?Sized, +{ + type Inner = Src; + type Mapped = crate::CastError; + + fn map NewSrc>(self, f: F) -> Self::Mapped { + self.map_src(f) + } +} + impl From> for SizeError { /// Infallibly extracts the [`SizeError`] from this `CastError` since `Dst` /// is unaligned. diff --git a/src/pointer/mod.rs b/src/pointer/mod.rs index 0f255802fd..3b3a7b1a5d 100644 --- a/src/pointer/mod.rs +++ b/src/pointer/mod.rs @@ -19,7 +19,7 @@ pub use {inner::PtrInner, transmute::*}; #[doc(hidden)] pub use { invariant::{BecauseExclusive, BecauseImmutable, Read}, - ptr::Ptr, + ptr::*, }; use crate::wrappers::ReadOnly; diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 5fe8fad1e8..3c447e0e8e 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -795,6 +795,7 @@ mod _transitions { } /// Casts of the referent type. +pub(crate) use _casts::TryWithError; mod _casts { use core::cell::UnsafeCell; @@ -912,6 +913,89 @@ mod _casts { let tag = unsafe { tag.assume_alignment() }; tag.unify_invariants() } + + /// Attempts to transform the pointer, restoring the original on + /// failure. + /// + /// # Safety + /// + /// If `I::Aliasing != Shared`, then if `f` returns `Err(err)`, no copy + /// of `f`'s argument must exist outside of `err`. + #[inline(always)] + pub(crate) unsafe fn try_with_unchecked( + self, + f: F, + ) -> Result, E::Mapped> + where + U: 'a + ?Sized, + J: Invariants, + E: TryWithError, + F: FnOnce(Ptr<'a, T, I>) -> Result, E>, + { + let old_inner = self.as_inner(); + #[rustfmt::skip] + let res = f(self).map_err(#[inline(always)] move |err: E| { + err.map(#[inline(always)] |src| { + drop(src); + + // SAFETY: + // 0. Aliasing is either `Shared` or `Exclusive`: + // - If aliasing is `Shared`, then it cannot violate + // aliasing make another copy of this pointer (in fact, + // using `I::Aliasing = Shared`, we could have just + // cloned `self`). + // - If aliasing is `Exclusive`, then `f` is not allowed + // to make another copy of `self`. In `map_err`, we are + // consuming the only value in the returned `Result`. + // By invariant on `E: TryWithError`, that `err: + // E` only contains a single `Self` and no other + // non-ZST fields which could be `Ptr`s or references + // to `self`'s referent. By the same invariant, `map` + // consumes this single `Self` and passes it to this + // closure. Since `self` was, by invariant on + // `Exclusive`, the only `Ptr` or reference live for + // `'a` with this referent, and since we `drop(src)` + // above, there are no copies left, and so we are + // creating the only copy. + // 1. `self` conforms to `I::Aliasing` by invariant on + // `Ptr`, and `old_inner` has the same address, so it + // does too. + // 2. `f` could not have violated `self`'s validity without + // itself being unsound. Assuming that `f` is sound, the + // referent of `self` is still valid for `T`. + unsafe { Ptr::from_inner(old_inner) } + }) + }); + res + } + + /// Attempts to transform the pointer, restoring the original on + /// failure. + pub(crate) fn try_with(self, f: F) -> Result, E::Mapped> + where + U: 'a + ?Sized, + J: Invariants, + E: TryWithError, + F: FnOnce(Ptr<'a, T, I>) -> Result, E>, + I: Invariants, + { + // SAFETY: `I::Aliasing = Shared`, so the safety condition does not + // apply. + unsafe { self.try_with_unchecked(f) } + } + } + + /// # Safety + /// + /// `Self` only contains a single `Self::Inner`, and `Self::Mapped` only + /// contains a single `MappedInner`. Other than that, `Self` and + /// `Self::Mapped` contain no non-ZST fields. + /// + /// `map` must pass ownership of `self`'s sole `Self::Inner` to `f`. + pub(crate) unsafe trait TryWithError { + type Inner; + type Mapped; + fn map MappedInner>(self, f: F) -> Self::Mapped; } impl<'a, T, I> Ptr<'a, T, I> diff --git a/src/util/macro_util.rs b/src/util/macro_util.rs index 48cd06b3c1..499fd035bb 100644 --- a/src/util/macro_util.rs +++ b/src/util/macro_util.rs @@ -31,7 +31,6 @@ use crate::{ pointer::{ cast::CastSized, invariant::{Aligned, Initialized, Valid}, - BecauseInvariantsEq, }, FromBytes, Immutable, IntoBytes, KnownLayout, Ptr, ReadOnly, TryFromBytes, ValidityError, }; @@ -665,9 +664,13 @@ where Dst: TryFromBytes + Immutable, { let ptr = Ptr::from_ref(src); - let ptr = ptr.recall_validity::(); - let ptr = ptr.cast::<_, CastSized, _>(); - match ptr.try_into_valid() { + #[rustfmt::skip] + let res = ptr.try_with(#[inline(always)] |ptr| { + let ptr = ptr.recall_validity::(); + let ptr = ptr.cast::<_, CastSized, _>(); + ptr.try_into_valid() + }); + match res { Ok(ptr) => { static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); // SAFETY: We have checked that `Dst` does not have a stricter @@ -675,23 +678,7 @@ where let ptr = unsafe { ptr.assume_alignment::() }; Ok(ptr.as_ref()) } - Err(err) => Err(err.map_src(|ptr| { - let ptr = ptr.cast::<_, CastSized, _>(); - // SAFETY: `ptr` has the same address as `src: &Src`, which is - // aligned by invariant on `&Src`. - let ptr = unsafe { ptr.assume_alignment::() }; - // SAFETY: Because `Src: Immutable` and we create a `Ptr` via - // `Ptr::from_ref`, the resulting `Ptr` is a shared-and-`Immutable` - // `Ptr`, which does not permit mutation of its referent. Therefore, - // no mutation could have happened during the call to - // `try_into_valid` (any such mutation would be unsound). - // - // `try_into_valid` promises to return its original argument, and - // so we know that we are getting back the same `ptr` that we - // originally passed, and that `ptr` was a bit-valid `Src`. - let ptr = unsafe { ptr.assume_valid() }; - ptr.as_ref() - })), + Err(err) => Err(err.map_src(Ptr::as_ref)), } } @@ -714,9 +701,16 @@ where Dst: TryFromBytes + IntoBytes, { let ptr = Ptr::from_mut(src); - let ptr = ptr.recall_validity::(); - let ptr = ptr.cast::<_, CastSized, _>(); - match ptr.try_into_valid() { + // SAFETY: The provided closure returns the only copy of `ptr`. + #[rustfmt::skip] + let res = unsafe { + ptr.try_with_unchecked(#[inline(always)] |ptr| { + let ptr = ptr.recall_validity::(); + let ptr = ptr.cast::<_, CastSized, _>(); + ptr.try_into_valid() + }) + }; + match res { Ok(ptr) => { static_assert!(Src, Dst => mem::align_of::() <= mem::align_of::()); // SAFETY: We have checked that `Dst` does not have a stricter @@ -724,15 +718,7 @@ where let ptr = unsafe { ptr.assume_alignment::() }; Ok(ptr.as_mut()) } - Err(err) => { - Err(err.map_src(|ptr| { - let ptr = ptr.cast::<_, CastSized, _>(); - // SAFETY: `ptr` has the same address as `src: &mut Src`, which - // is aligned by invariant on `&mut Src`. - let ptr = unsafe { ptr.assume_alignment::() }; - ptr.recall_validity::<_, (_, BecauseInvariantsEq)>().as_mut() - })) - } + Err(err) => Err(err.map_src(Ptr::as_mut)), } } diff --git a/zerocopy-derive/tests/ui-nightly/enum.stderr b/zerocopy-derive/tests/ui-nightly/enum.stderr index 5d6f4a92cb..2ece01b011 100644 --- a/zerocopy-derive/tests/ui-nightly/enum.stderr +++ b/zerocopy-derive/tests/ui-nightly/enum.stderr @@ -482,7 +482,7 @@ help: the trait `PaddingFree` is not implemented for `()` but trait `PaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 65 | impl PaddingFree for () {} + 64 | impl PaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -504,7 +504,7 @@ help: the trait `PaddingFree` is not implemented for `()` but trait `PaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 65 | impl PaddingFree for () {} + 64 | impl PaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -526,7 +526,7 @@ help: the trait `PaddingFree` is not implemented for `()` but trait `PaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 65 | impl PaddingFree for () {} + 64 | impl PaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/zerocopy-derive/tests/ui-nightly/struct.stderr b/zerocopy-derive/tests/ui-nightly/struct.stderr index 30fcb20fb1..3fef7ed1f6 100644 --- a/zerocopy-derive/tests/ui-nightly/struct.stderr +++ b/zerocopy-derive/tests/ui-nightly/struct.stderr @@ -325,7 +325,7 @@ help: the trait `PaddingFree` is not implemented for `()` but trait `PaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 65 | impl PaddingFree for () {} + 64 | impl PaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -347,7 +347,7 @@ help: the trait `PaddingFree` is not implemented for `()` but trait `PaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 65 | impl PaddingFree for () {} + 64 | impl PaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -405,7 +405,7 @@ help: the trait `DynamicPaddingFree` is not implemented for `( but trait `DynamicPaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 83 | impl DynamicPaddingFree for () {} + 82 | impl DynamicPaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -427,7 +427,7 @@ help: the trait `DynamicPaddingFree` is not implemented for `( but trait `DynamicPaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 83 | impl DynamicPaddingFree for () {} + 82 | impl DynamicPaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -449,7 +449,7 @@ help: the trait `DynamicPaddingFree` is not implemented for `( but trait `DynamicPaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 83 | impl DynamicPaddingFree for () {} + 82 | impl DynamicPaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/zerocopy-derive/tests/ui-stable/enum.stderr b/zerocopy-derive/tests/ui-stable/enum.stderr index 24d0c42799..c91e177995 100644 --- a/zerocopy-derive/tests/ui-stable/enum.stderr +++ b/zerocopy-derive/tests/ui-stable/enum.stderr @@ -439,7 +439,7 @@ help: the trait `PaddingFree` is not implemented for `()` but trait `PaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 65 | impl PaddingFree for () {} + 64 | impl PaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -457,7 +457,7 @@ help: the trait `PaddingFree` is not implemented for `()` but trait `PaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 65 | impl PaddingFree for () {} + 64 | impl PaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -475,7 +475,7 @@ help: the trait `PaddingFree` is not implemented for `()` but trait `PaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 65 | impl PaddingFree for () {} + 64 | impl PaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/zerocopy-derive/tests/ui-stable/struct.stderr b/zerocopy-derive/tests/ui-stable/struct.stderr index 4adff4acef..66ebb049df 100644 --- a/zerocopy-derive/tests/ui-stable/struct.stderr +++ b/zerocopy-derive/tests/ui-stable/struct.stderr @@ -293,7 +293,7 @@ help: the trait `PaddingFree` is not implemented for `()` but trait `PaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 65 | impl PaddingFree for () {} + 64 | impl PaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -311,7 +311,7 @@ help: the trait `PaddingFree` is not implemented for `()` but trait `PaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 65 | impl PaddingFree for () {} + 64 | impl PaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -365,7 +365,7 @@ help: the trait `DynamicPaddingFree` is not implemented for `( but trait `DynamicPaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 83 | impl DynamicPaddingFree for () {} + 82 | impl DynamicPaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -383,7 +383,7 @@ help: the trait `DynamicPaddingFree` is not implemented for `( but trait `DynamicPaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 83 | impl DynamicPaddingFree for () {} + 82 | impl DynamicPaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -401,7 +401,7 @@ help: the trait `DynamicPaddingFree` is not implemented for `( but trait `DynamicPaddingFree` is implemented for it --> $WORKSPACE/src/util/macro_util.rs | - 83 | impl DynamicPaddingFree for () {} + 82 | impl DynamicPaddingFree for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: see issue #48214 = note: this error originates in the derive macro `IntoBytes` (in Nightly builds, run with -Z macro-backtrace for more info)