From fa30a6bfe73f60cbf0977a9e6ec67c6b2fcbffd0 Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 6 May 2024 15:35:43 +0000 Subject: [PATCH] Use `AlignmentError` in `Unalign`'s failure conditions This make's `Unalign`'s methods consistent with zerocopy's other methods, and, in the case of `Unalign::try_deref_mut`, allows the original `&mut Unalign` to be reused in the event of failure. Makes progress towards #1139 --- src/pointer/ptr.rs | 37 +++++++++++++++++++++++++++++++++---- src/wrappers.rs | 18 ++++++++++++------ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 54e993f80e..0ee76a4f61 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -613,12 +613,41 @@ mod _conversions { c } } + + impl<'a, T, I> Ptr<'a, T, I> + where + I: Invariants, + { + /// Converts a `Ptr` to a `Unalign` into an `Ptr` to an unaligned `T`. + pub(crate) fn into_unaligned( + self, + ) -> Ptr<'a, crate::Unalign, (I::Aliasing, Aligned, I::Validity)> { + // SAFETY: We define `Unalign` to be a `#[repr(C, packed)]` type + // wrapping a single `T` field. Thus, `Unalign` has the same size + // as `T` and contains `UnsafeCell`s at the same locations as `T`. + // The cast is implemented in the form `|p: *mut T| p as *mut U`, + // where `U` is `Unalign`. + let ptr = unsafe { + #[allow(clippy::as_conversions)] + self.cast_unsized(|p: *mut T| p as *mut crate::Unalign) + }; + // SAFETY: We define `Unalign` to be a `#[repr(C, packed)]` type + // wrapping a single `T` field, thus `Unalign` has exactly the + // same validity as `T`. + let ptr = unsafe { ptr.assume_validity::() }; + // SAFETY: We define `Unalign` to be a `#[repr(C, packed)]` type + // wrapping a single `T` field, thus `Unalign` is always + // trivially aligned. + let ptr = unsafe { ptr.assume_alignment::() }; + ptr + } + } } /// State transitions between invariants. mod _transitions { use super::*; - use crate::{TryFromBytes, ValidityError}; + use crate::{AlignmentError, TryFromBytes, ValidityError}; impl<'a, T, I> Ptr<'a, T, I> where @@ -745,16 +774,16 @@ mod _transitions { /// on success. pub(crate) fn bikeshed_try_into_aligned( self, - ) -> Option> + ) -> Result, AlignmentError> where T: Sized, { if !crate::util::aligned_to::<_, T>(self.as_non_null()) { - return None; + return Err(AlignmentError::new(self)); } // SAFETY: We just checked the alignment. - Some(unsafe { self.assume_alignment::() }) + Ok(unsafe { self.assume_alignment::() }) } /// Recalls that `self`'s referent is validly-aligned for `T`. diff --git a/src/wrappers.rs b/src/wrappers.rs index f9b28819e8..5020e2a274 100644 --- a/src/wrappers.rs +++ b/src/wrappers.rs @@ -136,14 +136,17 @@ impl Unalign { /// not properly aligned. /// /// If `self` does not satisfy `mem::align_of::()`, then it is unsound to - /// return a reference to the wrapped `T`, and `try_deref` returns `None`. + /// return a reference to the wrapped `T`, and `try_deref` returns `Err`. /// /// If `T: Unaligned`, then `Unalign` implements [`Deref`], and callers /// may prefer [`Deref::deref`], which is infallible. #[inline(always)] - pub fn try_deref(&self) -> Option<&T> { + pub fn try_deref(&self) -> Result<&T, AlignmentError<&Self, T>> { let inner = Ptr::from_ref(self).transparent_wrapper_into_inner(); - inner.bikeshed_try_into_aligned().map(Ptr::as_ref) + match inner.bikeshed_try_into_aligned() { + Ok(aligned) => Ok(aligned.as_ref()), + Err(err) => Err(err.map_src(|src| src.into_unaligned().as_ref())), + } } /// Attempts to return a mutable reference to the wrapped `T`, failing if @@ -151,14 +154,17 @@ impl Unalign { /// /// If `self` does not satisfy `mem::align_of::()`, then it is unsound to /// return a reference to the wrapped `T`, and `try_deref_mut` returns - /// `None`. + /// `Err`. /// /// If `T: Unaligned`, then `Unalign` implements [`DerefMut`], and /// callers may prefer [`DerefMut::deref_mut`], which is infallible. #[inline(always)] - pub fn try_deref_mut(&mut self) -> Option<&mut T> { + pub fn try_deref_mut(&mut self) -> Result<&mut T, AlignmentError<&mut Self, T>> { let inner = Ptr::from_mut(self).transparent_wrapper_into_inner(); - inner.bikeshed_try_into_aligned().map(Ptr::as_mut) + match inner.bikeshed_try_into_aligned() { + Ok(aligned) => Ok(aligned.as_mut()), + Err(err) => Err(err.map_src(|src| src.into_unaligned().as_mut())), + } } /// Returns a reference to the wrapped `T` without checking alignment.