Skip to content

Commit 1d90765

Browse files
authored
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<T>` to be reused in the event of failure. Makes progress towards #1139
1 parent 8d321c7 commit 1d90765

File tree

2 files changed

+53
-16
lines changed

2 files changed

+53
-16
lines changed

src/pointer/ptr.rs

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

618649
/// State transitions between invariants.
619650
mod _transitions {
620651
use super::*;
621-
use crate::{TryFromBytes, ValidityError};
652+
use crate::{AlignmentError, TryFromBytes, ValidityError};
622653

623654
impl<'a, T, I> Ptr<'a, T, I>
624655
where
@@ -745,16 +776,16 @@ mod _transitions {
745776
/// on success.
746777
pub(crate) fn bikeshed_try_into_aligned(
747778
self,
748-
) -> Option<Ptr<'a, T, (I::Aliasing, Aligned, I::Validity)>>
779+
) -> Result<Ptr<'a, T, (I::Aliasing, Aligned, I::Validity)>, AlignmentError<Self, T>>
749780
where
750781
T: Sized,
751782
{
752783
if !crate::util::aligned_to::<_, T>(self.as_non_null()) {
753-
return None;
784+
return Err(AlignmentError::new(self));
754785
}
755786

756787
// SAFETY: We just checked the alignment.
757-
Some(unsafe { self.assume_alignment::<Aligned>() })
788+
Ok(unsafe { self.assume_alignment::<Aligned>() })
758789
}
759790

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

src/wrappers.rs

+18-12
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_unalign().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_unalign().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)