From 1d90765468de9c77f1ac193bd55bbae89f780cfc Mon Sep 17 00:00:00 2001 From: Jack Wrenn Date: Mon, 6 May 2024 13:42:48 -0400 Subject: [PATCH] Use `AlignmentError` in `Unalign`'s failure conditions (#1198) 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 | 39 +++++++++++++++++++++++++++++++++++---- src/wrappers.rs | 30 ++++++++++++++++++------------ 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/pointer/ptr.rs b/src/pointer/ptr.rs index 54e993f80e..530194a565 100644 --- a/src/pointer/ptr.rs +++ b/src/pointer/ptr.rs @@ -613,12 +613,43 @@ mod _conversions { c } } + + /// `Ptr<'a, T, (_, _, _)>` → `Ptr<'a, Unalign, (_, Aligned, _)>` + impl<'a, T, I> Ptr<'a, T, I> + where + I: Invariants, + { + /// Converts a `Ptr` an unaligned `T` into a `Ptr` to an aligned + /// `Unalign`. + pub(crate) fn into_unalign( + 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 +776,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..763a3987c6 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_unalign().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_unalign().as_mut())), + } } /// Returns a reference to the wrapped `T` without checking alignment. @@ -423,8 +429,8 @@ mod tests { // Test methods that depend on alignment (when alignment is satisfied). let mut u: Align<_, AU64> = Align::new(Unalign::new(AU64(123))); - assert_eq!(u.t.try_deref(), Some(&AU64(123))); - assert_eq!(u.t.try_deref_mut(), Some(&mut AU64(123))); + assert_eq!(u.t.try_deref().unwrap(), &AU64(123)); + assert_eq!(u.t.try_deref_mut().unwrap(), &mut AU64(123)); // SAFETY: The `Align<_, AU64>` guarantees proper alignment. assert_eq!(unsafe { u.t.deref_unchecked() }, &AU64(123)); // SAFETY: The `Align<_, AU64>` guarantees proper alignment. @@ -435,13 +441,13 @@ mod tests { // Test methods that depend on alignment (when alignment is not // satisfied). let mut u: ForceUnalign<_, AU64> = ForceUnalign::new(Unalign::new(AU64(123))); - assert_eq!(u.t.try_deref(), None); - assert_eq!(u.t.try_deref_mut(), None); + assert!(matches!(u.t.try_deref(), Err(AlignmentError { .. }))); + assert!(matches!(u.t.try_deref_mut(), Err(AlignmentError { .. }))); // Test methods that depend on `T: Unaligned`. let mut u = Unalign::new(123u8); - assert_eq!(u.try_deref(), Some(&123)); - assert_eq!(u.try_deref_mut(), Some(&mut 123)); + assert_eq!(u.try_deref(), Ok(&123)); + assert_eq!(u.try_deref_mut(), Ok(&mut 123)); assert_eq!(u.deref(), &123); assert_eq!(u.deref_mut(), &mut 123); *u = 21;