Skip to content

Commit a637a1e

Browse files
committed
Implement TryFromBytes for unsized UnsafeCell
Makes progress on #251
1 parent 623a5ee commit a637a1e

File tree

3 files changed

+83
-68
lines changed

3 files changed

+83
-68
lines changed

src/impls.rs

+4-52
Original file line numberDiff line numberDiff line change
@@ -643,17 +643,14 @@ impl_for_transparent_wrapper!(T: ?Sized + IntoBytes => IntoBytes for ManuallyDro
643643
impl_for_transparent_wrapper!(T: ?Sized + Unaligned => Unaligned for ManuallyDrop<T>);
644644
assert_unaligned!(ManuallyDrop<()>, ManuallyDrop<u8>);
645645

646-
// TODO(#5): Implement `FromZeros` and `FromBytes` when `T: ?Sized`.
647-
impl_for_transparent_wrapper!(T: FromZeros => FromZeros for UnsafeCell<T>);
648-
impl_for_transparent_wrapper!(T: FromBytes => FromBytes for UnsafeCell<T>);
646+
impl_for_transparent_wrapper!(T: ?Sized + FromZeros => FromZeros for UnsafeCell<T>);
647+
impl_for_transparent_wrapper!(T: ?Sized + FromBytes => FromBytes for UnsafeCell<T>);
649648
impl_for_transparent_wrapper!(T: ?Sized + IntoBytes => IntoBytes for UnsafeCell<T>);
650649
impl_for_transparent_wrapper!(T: ?Sized + Unaligned => Unaligned for UnsafeCell<T>);
651650
assert_unaligned!(UnsafeCell<()>, UnsafeCell<u8>);
652651

653652
// SAFETY: See safety comment in `is_bit_valid` impl.
654-
//
655-
// TODO(#5): Try to add `T: ?Sized` bound.
656-
unsafe impl<T: TryFromBytes> TryFromBytes for UnsafeCell<T> {
653+
unsafe impl<T: TryFromBytes + ?Sized> TryFromBytes for UnsafeCell<T> {
657654
#[allow(clippy::missing_inline_in_public_items)]
658655
fn only_derive_is_allowed_to_implement_this_trait()
659656
where
@@ -682,56 +679,11 @@ unsafe impl<T: TryFromBytes> TryFromBytes for UnsafeCell<T> {
682679
// chance to fix it quickly.
683680
let c = candidate.into_exclusive_or_post_monomorphization_error();
684681

685-
// We wrap in `Unalign` here so that we can get a vanilla Rust reference
686-
// below, which in turn allows us to call `UnsafeCell::get_mut`.
687-
//
688-
// SAFETY:
689-
// - `.cast` preserves address. `Unalign` and `MaybeUninit` both have
690-
// the same size as the types they wrap [1]. Thus, this cast will
691-
// preserve the size of the pointer. As a result, the cast will
692-
// address the same bytes as `c`.
693-
// - `.cast` preserves provenance.
694-
// - Since both the source and destination types are wrapped in
695-
// `UnsafeCell`, all bytes of both types are inside of `UnsafeCell`s,
696-
// and so the byte ranges covered by `UnsafeCell`s are identical in
697-
// both types. Since the pointers refer to the same byte ranges,
698-
// the same is true of the pointers' referents as well.
699-
//
700-
// [1] Per https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#layout-1:
701-
//
702-
// MaybeUninit<T> is guaranteed to have the same size, alignment, and
703-
// ABI as T.
704-
let c = unsafe {
705-
c.cast_unsized(|c: *mut UnsafeCell<T>| c.cast::<UnsafeCell<Unalign<MaybeUninit<T>>>>())
706-
};
707-
// SAFETY: `MaybeUninit` has no validity requirements.
708-
let c = unsafe { c.assume_valid() };
709-
let c = c.bikeshed_recall_aligned();
710-
// This is the crucial step at which we use `UnsafeCell::get_mut` to go
711-
// from `UnsafeCell<U>` to `U` (where `U = Unalign<MaybeUninit<T>>`).
712-
// Now that we've gotten rid of the `UnsafeCell`, we can delegate to
713-
// `T::is_bit_valid`.
714-
let c: &mut Unalign<MaybeUninit<T>> = c.as_mut().get_mut();
715-
// This converts from an aligned `Unalign<MaybeUninit<T>>` pointer to an
716-
// unaligned `MaybeUninit<T>` pointer.
717-
let c: Ptr<'_, MaybeUninit<T>, _> = Ptr::from_mut(c).transparent_wrapper_into_inner();
718-
let c: Ptr<'_, T, _> = c.transparent_wrapper_into_inner();
719-
720-
// SAFETY: The original `candidate` argument has `Initialized` validity.
721-
// None of the subsequent operations modify the memory itself, and so
722-
// that guarantee is still upheld.
723-
let c = unsafe { c.assume_initialized() };
724-
// Confirm that `Maybe` is a type alias for `Ptr` with the validity
725-
// invariant `Initialized`. Our safety proof depends upon this
726-
// invariant, and it might change at some point. If that happens, we
727-
// want this function to stop compiling.
728-
let _: Ptr<'_, UnsafeCell<T>, (_, _, invariant::Initialized)> = candidate;
729-
730682
// SAFETY: Since `UnsafeCell<T>` and `T` have the same layout and bit
731683
// validity, `UnsafeCell<T>` is bit-valid exactly when its wrapped `T`
732684
// is. Thus, this is a sound implementation of
733685
// `UnsafeCell::is_bit_valid`.
734-
T::is_bit_valid(c.forget_exclusive())
686+
T::is_bit_valid(c.get_mut())
735687
}
736688
}
737689

src/pointer/ptr.rs

+78-15
Original file line numberDiff line numberDiff line change
@@ -717,13 +717,25 @@ mod _transitions {
717717
/// # Safety
718718
///
719719
/// The caller promises that `self` satisfies the invariants `H`.
720-
pub(super) unsafe fn assume_invariants<H: Invariants>(self) -> Ptr<'a, T, H> {
720+
unsafe fn assume_invariants<H: Invariants>(self) -> Ptr<'a, T, H> {
721721
// SAFETY: The caller has promised to satisfy all parameterized
722722
// invariants of `Ptr`. `Ptr`'s other invariants are satisfied
723723
// by-contract by the source `Ptr`.
724724
unsafe { Ptr::new(self.as_non_null()) }
725725
}
726726

727+
/// Helps the type system unify two distinct invariant types which are
728+
/// actually the same.
729+
pub(super) fn unify_invariants<
730+
H: Invariants<Aliasing = I::Aliasing, Alignment = I::Alignment, Validity = I::Validity>,
731+
>(
732+
self,
733+
) -> Ptr<'a, T, H> {
734+
// SAFETY: The associated type bounds on `H` ensure that the
735+
// invariants are unchanged.
736+
unsafe { self.assume_invariants::<H>() }
737+
}
738+
727739
/// Assumes that `self` satisfies the aliasing requirement of `A`.
728740
///
729741
/// # Safety
@@ -1288,22 +1300,72 @@ mod _casts {
12881300
} else {
12891301
// Undo the cast so we can return the original bytes.
12901302
let slf = slf.as_bytes();
1291-
// Restore the initial invariants of `self`.
1303+
// Restore the initial alignment invariant of `self`.
12921304
//
12931305
// SAFETY: The referent type of `slf` is now equal to
1294-
// that of `self`, but the invariants nominally differ.
1295-
// Since `slf` and `self` refer to the same memory and
1296-
// no actions have been taken that would violate the
1297-
// original invariants on `self`, it is sound to apply
1298-
// the invariants of `self` onto `slf`.
1299-
let slf = unsafe { slf.assume_invariants() };
1306+
// that of `self`, but the alignment invariants
1307+
// nominally differ. Since `slf` and `self` refer to the
1308+
// same memory and no actions have been taken that would
1309+
// violate the original invariants on `self`, it is
1310+
// sound to apply the alignment invariant of `self` onto
1311+
// `slf`.
1312+
let slf = unsafe { slf.assume_alignment::<I::Alignment>() };
1313+
let slf = slf.unify_invariants();
13001314
Err(CastError::Size(SizeError::<_, U>::new(slf)))
13011315
}
13021316
}
13031317
Err(err) => Err(err),
13041318
}
13051319
}
13061320
}
1321+
1322+
impl<'a, T, I> Ptr<'a, core::cell::UnsafeCell<T>, I>
1323+
where
1324+
T: 'a + ?Sized,
1325+
I: Invariants<Aliasing = Exclusive>,
1326+
{
1327+
/// Converts this `Ptr` into a pointer to the underlying data.
1328+
///
1329+
/// This call borrows the `UnsafeCell` mutably (at compile-time) which
1330+
/// guarantees that we possess the only reference.
1331+
///
1332+
/// This is like [`UnsafeCell::get_mut`], but for `Ptr`.
1333+
#[inline(always)]
1334+
pub fn get_mut(self) -> Ptr<'a, T, I> {
1335+
// SAFETY:
1336+
// - The closure uses an `as` cast, which preserves address range
1337+
// and provenance.
1338+
// - We require `I: Invariants<Aliasing = Exclusive>`, so we are not
1339+
// required to uphold `UnsafeCell` equality.
1340+
let ptr = unsafe { self.cast_unsized(|p| p as *mut T) };
1341+
1342+
// SAFETY: `UnsafeCell<T>` has the same alignment as `T` [1],
1343+
// and so if `self` is guaranteed to be aligned, then so is the
1344+
// returned `Ptr`.
1345+
//
1346+
// [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout:
1347+
//
1348+
// `UnsafeCell<T>` has the same in-memory representation as
1349+
// its inner type `T`. A consequence of this guarantee is that
1350+
// it is possible to convert between `T` and `UnsafeCell<T>`.
1351+
let ptr = unsafe { ptr.assume_alignment::<I::Alignment>() };
1352+
1353+
// SAFETY: `UnsafeCell<T>` has the same bit validity as `T` [1], and
1354+
// so if `self` has a particular validity invariant, then the same
1355+
// holds of the returned `Ptr`. Technically the term
1356+
// "representation" doesn't guarantee this, but the subsequent
1357+
// sentence in the documentation makes it clear that this is the
1358+
// intention.
1359+
//
1360+
// [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout:
1361+
//
1362+
// `UnsafeCell<T>` has the same in-memory representation as its
1363+
// inner type `T`. A consequence of this guarantee is that it is
1364+
// possible to convert between `T` and `UnsafeCell<T>`.
1365+
let ptr = unsafe { ptr.assume_validity::<I::Validity>() };
1366+
ptr.unify_invariants()
1367+
}
1368+
}
13071369
}
13081370

13091371
/// Projections through the referent.
@@ -1467,16 +1529,17 @@ mod _project {
14671529
// its (inclusive) lower bound. Thus, no index is a member of both
14681530
// ranges.
14691531

1470-
// SAFETY: We never change invariants other than aliasing.
1471-
//
1472-
// By the preceding lemma, `left` and `right` do not alias. We do
1473-
// not construct any other `Ptr`s or references which alias `left`
1474-
// or `right`. Thus, the only `Ptr`s or references which alias
1475-
// `left` or `right` are outside of this method. By invariant,
1532+
// SAFETY: By the preceding lemma, `left` and `right` do not alias.
1533+
// We do not construct any other `Ptr`s or references which alias
1534+
// `left` or `right`. Thus, the only `Ptr`s or references which
1535+
// alias `left` or `right` are outside of this method. By invariant,
14761536
// `self` obeys the aliasing invariant `I::Aliasing` with respect to
14771537
// those other `Ptr`s or references, and so `left` and `right` do as
14781538
// well.
1479-
unsafe { (left.assume_invariants::<I>(), right.assume_invariants::<I>()) }
1539+
let (left, right) = unsafe {
1540+
(left.assume_aliasing::<I::Aliasing>(), right.assume_aliasing::<I::Aliasing>())
1541+
};
1542+
(left.unify_invariants(), right.unify_invariants())
14801543
}
14811544

14821545
/// Iteratively projects the elements `Ptr<T>` from `Ptr<[T]>`.

src/util.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ unsafe impl<T, I: Invariants> TransparentWrapper<I> for Wrapping<T> {
261261
// - Per [1], `UnsafeCell<T>` has the same size as `T`.
262262
// - See inline comments for other safety justifications.
263263
//
264-
// [1] Per https://doc.rust-lang.org/core/cell/struct.UnsafeCell.html#memory-layout:
264+
// [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout:
265265
//
266266
// `UnsafeCell<T>` has the same in-memory representation as its inner type
267267
// `T`.

0 commit comments

Comments
 (0)