Skip to content

Commit 61e64f7

Browse files
committed
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<T>` to be reused in the event of failure. Makes progress towards #1139
1 parent f4a6c55 commit 61e64f7

File tree

2 files changed

+51
-16
lines changed

2 files changed

+51
-16
lines changed

src/pointer/ptr.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -613,12 +613,41 @@ mod _conversions {
613613
c
614614
}
615615
}
616+
617+
impl<'a, T, I> Ptr<'a, T, I>
618+
where
619+
I: Invariants,
620+
{
621+
/// Converts a `Ptr` to a `Unalign<T>` into an `Ptr` to an unaligned `T`.
622+
pub(crate) fn into_unaligned(
623+
self,
624+
) -> Ptr<'a, crate::Unalign<T>, (I::Aliasing, Aligned, I::Validity)> {
625+
// SAFETY: We define `Unalign<T>` to be a `#[repr(C, packed)]` type
626+
// wrapping a single `T` field. Thus, `Unalign<T>` has the same size
627+
// as `T` and contains `UnsafeCell`s at the same locations as `T`.
628+
// The cast is implemented in the form `|p: *mut T| p as *mut U`,
629+
// where `U` is `Unalign<T>`.
630+
let ptr = unsafe {
631+
#[allow(clippy::as_conversions)]
632+
self.cast_unsized(|p: *mut T| p as *mut crate::Unalign<T>)
633+
};
634+
// SAFETY: We define `Unalign<T>` to be a `#[repr(C, packed)]` type
635+
// wrapping a single `T` field, thus `Unalign<T>` has exactly the
636+
// same validity as `T`.
637+
let ptr = unsafe { ptr.assume_validity::<I::Validity>() };
638+
// SAFETY: We define `Unalign<T>` to be a `#[repr(C, packed)]` type
639+
// wrapping a single `T` field, thus `Unalign<T>` is always
640+
// trivially aligned.
641+
let ptr = unsafe { ptr.assume_alignment::<Aligned>() };
642+
ptr
643+
}
644+
}
616645
}
617646

618647
/// State transitions between invariants.
619648
mod _transitions {
620649
use super::*;
621-
use crate::{TryFromBytes, ValidityError};
650+
use crate::{AlignmentError, TryFromBytes, ValidityError};
622651

623652
impl<'a, T, I> Ptr<'a, T, I>
624653
where
@@ -745,16 +774,16 @@ mod _transitions {
745774
/// on success.
746775
pub(crate) fn bikeshed_try_into_aligned(
747776
self,
748-
) -> Option<Ptr<'a, T, (I::Aliasing, Aligned, I::Validity)>>
777+
) -> Result<Ptr<'a, T, (I::Aliasing, Aligned, I::Validity)>, AlignmentError<Self, T>>
749778
where
750779
T: Sized,
751780
{
752781
if !crate::util::aligned_to::<_, T>(self.as_non_null()) {
753-
return None;
782+
return Err(AlignmentError::new(self));
754783
}
755784

756785
// SAFETY: We just checked the alignment.
757-
Some(unsafe { self.assume_alignment::<Aligned>() })
786+
Ok(unsafe { self.assume_alignment::<Aligned>() })
758787
}
759788

760789
/// Recalls that `self`'s referent is validly-aligned for `T`.

src/wrappers.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,29 +136,35 @@ impl<T> Unalign<T> {
136136
/// not properly aligned.
137137
///
138138
/// If `self` does not satisfy `mem::align_of::<T>()`, then it is unsound to
139-
/// return a reference to the wrapped `T`, and `try_deref` returns `None`.
139+
/// return a reference to the wrapped `T`, and `try_deref` returns `Err`.
140140
///
141141
/// If `T: Unaligned`, then `Unalign<T>` implements [`Deref`], and callers
142142
/// may prefer [`Deref::deref`], which is infallible.
143143
#[inline(always)]
144-
pub fn try_deref(&self) -> Option<&T> {
144+
pub fn try_deref(&self) -> Result<&T, AlignmentError<&Self, T>> {
145145
let inner = Ptr::from_ref(self).transparent_wrapper_into_inner();
146-
inner.bikeshed_try_into_aligned().map(Ptr::as_ref)
146+
match inner.bikeshed_try_into_aligned() {
147+
Ok(aligned) => Ok(aligned.as_ref()),
148+
Err(err) => Err(err.map_src(|src| src.into_unaligned().as_ref())),
149+
}
147150
}
148151

149152
/// Attempts to return a mutable reference to the wrapped `T`, failing if
150153
/// `self` is not properly aligned.
151154
///
152155
/// If `self` does not satisfy `mem::align_of::<T>()`, then it is unsound to
153156
/// return a reference to the wrapped `T`, and `try_deref_mut` returns
154-
/// `None`.
157+
/// `Err`.
155158
///
156159
/// If `T: Unaligned`, then `Unalign<T>` implements [`DerefMut`], and
157160
/// callers may prefer [`DerefMut::deref_mut`], which is infallible.
158161
#[inline(always)]
159-
pub fn try_deref_mut(&mut self) -> Option<&mut T> {
162+
pub fn try_deref_mut(&mut self) -> Result<&mut T, AlignmentError<&mut Self, T>> {
160163
let inner = Ptr::from_mut(self).transparent_wrapper_into_inner();
161-
inner.bikeshed_try_into_aligned().map(Ptr::as_mut)
164+
match inner.bikeshed_try_into_aligned() {
165+
Ok(aligned) => Ok(aligned.as_mut()),
166+
Err(err) => Err(err.map_src(|src| src.into_unaligned().as_mut())),
167+
}
162168
}
163169

164170
/// Returns a reference to the wrapped `T` without checking alignment.
@@ -423,8 +429,8 @@ mod tests {
423429

424430
// Test methods that depend on alignment (when alignment is satisfied).
425431
let mut u: Align<_, AU64> = Align::new(Unalign::new(AU64(123)));
426-
assert_eq!(u.t.try_deref(), Some(&AU64(123)));
427-
assert_eq!(u.t.try_deref_mut(), Some(&mut AU64(123)));
432+
assert_eq!(u.t.try_deref().unwrap(), &AU64(123));
433+
assert_eq!(u.t.try_deref_mut().unwrap(), &mut AU64(123));
428434
// SAFETY: The `Align<_, AU64>` guarantees proper alignment.
429435
assert_eq!(unsafe { u.t.deref_unchecked() }, &AU64(123));
430436
// SAFETY: The `Align<_, AU64>` guarantees proper alignment.
@@ -435,13 +441,13 @@ mod tests {
435441
// Test methods that depend on alignment (when alignment is not
436442
// satisfied).
437443
let mut u: ForceUnalign<_, AU64> = ForceUnalign::new(Unalign::new(AU64(123)));
438-
assert_eq!(u.t.try_deref(), None);
439-
assert_eq!(u.t.try_deref_mut(), None);
444+
assert!(matches!(u.t.try_deref(), Err(AlignmentError { .. })));
445+
assert!(matches!(u.t.try_deref_mut(), Err(AlignmentError { .. })));
440446

441447
// Test methods that depend on `T: Unaligned`.
442448
let mut u = Unalign::new(123u8);
443-
assert_eq!(u.try_deref(), Some(&123));
444-
assert_eq!(u.try_deref_mut(), Some(&mut 123));
449+
assert_eq!(u.try_deref(), Ok(&123));
450+
assert_eq!(u.try_deref_mut(), Ok(&mut 123));
445451
assert_eq!(u.deref(), &123);
446452
assert_eq!(u.deref_mut(), &mut 123);
447453
*u = 21;

0 commit comments

Comments
 (0)