Skip to content

Commit e78e8f6

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

File tree

3 files changed

+61
-50
lines changed

3 files changed

+61
-50
lines changed

src/impls.rs

+2-49
Original file line numberDiff line numberDiff line change
@@ -651,9 +651,7 @@ impl_for_transparent_wrapper!(T: ?Sized + Unaligned => Unaligned for UnsafeCell<
651651
assert_unaligned!(UnsafeCell<()>, UnsafeCell<u8>);
652652

653653
// 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> {
654+
unsafe impl<T: TryFromBytes + ?Sized> TryFromBytes for UnsafeCell<T> {
657655
#[allow(clippy::missing_inline_in_public_items)]
658656
fn only_derive_is_allowed_to_implement_this_trait()
659657
where
@@ -682,56 +680,11 @@ unsafe impl<T: TryFromBytes> TryFromBytes for UnsafeCell<T> {
682680
// chance to fix it quickly.
683681
let c = candidate.into_exclusive_or_post_monomorphization_error();
684682

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-
730683
// SAFETY: Since `UnsafeCell<T>` and `T` have the same layout and bit
731684
// validity, `UnsafeCell<T>` is bit-valid exactly when its wrapped `T`
732685
// is. Thus, this is a sound implementation of
733686
// `UnsafeCell::is_bit_valid`.
734-
T::is_bit_valid(c.forget_exclusive())
687+
T::is_bit_valid(c.get_mut())
735688
}
736689
}
737690

src/pointer/ptr.rs

+58
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,16 @@ mod _transitions {
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+
unsafe { self.assume_invariants::<H>() }
735+
}
736+
727737
/// Assumes that `self` satisfies the aliasing requirement of `A`.
728738
///
729739
/// # Safety
@@ -1304,6 +1314,54 @@ mod _casts {
13041314
}
13051315
}
13061316
}
1317+
1318+
impl<'a, T, I> Ptr<'a, core::cell::UnsafeCell<T>, I>
1319+
where
1320+
T: 'a + ?Sized,
1321+
I: Invariants<Aliasing = Exclusive>,
1322+
{
1323+
/// Converts this `Ptr` into a pointer to the underlying data.
1324+
///
1325+
/// This call borrows the `UnsafeCell` mutably (at compile-time) which
1326+
/// guarantees that we possess the only reference.
1327+
///
1328+
/// This is like [`UnsafeCell::get_mut`], but for `Ptr`.
1329+
#[inline(always)]
1330+
pub fn get_mut(self) -> Ptr<'a, T, I> {
1331+
// SAFETY:
1332+
// - The closure uses an `as` cast, which preserves address range
1333+
// and provenance.
1334+
// - We require `I: Invariants<Aliasing = Exclusive>`, so we are not
1335+
// required to uphold `UnsafeCell` equality.
1336+
let ptr = unsafe { self.cast_unsized(|p| p as *mut T) };
1337+
1338+
// SAFETY: `UnsafeCell<T>` has the same alignment as `T` [1],
1339+
// and so if `self` is guaranteed to be aligned, then so is the
1340+
// returned `Ptr`.
1341+
//
1342+
// [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout:
1343+
//
1344+
// `UnsafeCell<T>` has the same in-memory representation as
1345+
// its inner type `T`. A consequence of this guarantee is that
1346+
// it is possible to convert between `T` and `UnsafeCell<T>`.
1347+
let ptr = unsafe { ptr.assume_alignment::<I::Alignment>() };
1348+
1349+
// SAFETY: `UnsafeCell<T>` has the same bit validity as `T` [1], and
1350+
// so if `self` has a particular validity invariant, then the same
1351+
// holds of the returned `Ptr`. Technically the term
1352+
// "representation" doesn't guarantee this, but the subsequent
1353+
// sentence in the documentation makes it clear that this is the
1354+
// intention.
1355+
//
1356+
// [1] Per https://doc.rust-lang.org/1.81.0/core/cell/struct.UnsafeCell.html#memory-layout:
1357+
//
1358+
// `UnsafeCell<T>` has the same in-memory representation as its
1359+
// inner type `T`. A consequence of this guarantee is that it is
1360+
// possible to convert between `T` and `UnsafeCell<T>`.
1361+
let ptr = unsafe { ptr.assume_validity::<I::Validity>() };
1362+
ptr.unify_invariants()
1363+
}
1364+
}
13071365
}
13081366

13091367
/// Projections through the referent.

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)