Skip to content

Commit

Permalink
TransparentWrapper supports UnsafeCell variance (#1024)
Browse files Browse the repository at this point in the history
This will allow us to implement `TransparentWrapper` for `T` where `T`
and `T::Inner` do not have `UnsafeCell`s at the same byte ranges. This,
in turn, will let us automatically implement traits for these types so
long as those traits don't require reasoning about `UnsafeCell` ranges
(in other words, all traits other than `NoCell`).
  • Loading branch information
joshlf committed Mar 6, 2024
1 parent 88c0edd commit f45a227
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 36 deletions.
11 changes: 6 additions & 5 deletions src/macros.rs
Expand Up @@ -247,11 +247,12 @@ macro_rules! impl_for_transparent_wrapper {
}
};
(@is_transparent_wrapper NoCell) => {
// SAFETY: `W: TransparentWrapper` requires that `W` has `UnsafeCell`s
// at the same byte offsets as `W::Inner = T`. `T: NoCell` implies that
// `T` does not contain any `UnsafeCell`s, and so `W` does not contain
// any `UnsafeCell`s. Thus, `W` can soundly implement `NoCell`.
fn is_transparent_wrapper<I: Invariants, T: ?Sized, W: TransparentWrapper<I, Inner=T> + ?Sized>() {}
// SAFETY: `W: TransparentWrapper<UnsafeCellVariance=Covariant>`
// requires that `W` has `UnsafeCell`s at the same byte offsets as
// `W::Inner = T`. `T: NoCell` implies that `T` does not contain any
// `UnsafeCell`s, and so `W` does not contain any `UnsafeCell`s. Thus,
// `W` can soundly implement `NoCell`.
fn is_transparent_wrapper<I: Invariants, T: ?Sized, W: TransparentWrapper<I, Inner=T, UnsafeCellVariance=Covariant> + ?Sized>() {}
};
(@is_transparent_wrapper FromZeros) => {
// SAFETY: `W: TransparentWrapper<ValidityVariance=Covariant>` requires
Expand Down
9 changes: 5 additions & 4 deletions src/pointer/ptr.rs
Expand Up @@ -389,7 +389,7 @@ mod _external {
/// Methods for converting to and from `Ptr` and Rust's safe reference types.
mod _conversions {
use super::*;
use crate::util::{AlignmentVariance, TransparentWrapper, ValidityVariance};
use crate::util::{AlignmentVariance, Covariant, TransparentWrapper, ValidityVariance};

/// `&'a T` → `Ptr<'a, T>`
impl<'a, T> Ptr<'a, T, (invariant::Shared, invariant::Aligned, invariant::Valid)>
Expand Down Expand Up @@ -609,7 +609,7 @@ mod _conversions {
/// `Ptr<'a, T = Wrapper<U>>` → `Ptr<'a, U>`
impl<'a, T, I> Ptr<'a, T, I>
where
T: 'a + TransparentWrapper<I> + ?Sized,
T: 'a + TransparentWrapper<I, UnsafeCellVariance = Covariant> + ?Sized,
I: Invariants,
{
/// Converts the `Ptr` to a transparent wrapper type into a `Ptr` to the
Expand All @@ -629,8 +629,9 @@ mod _conversions {
// - By invariant on `TransparentWrapper::cast_into_inner`:
// - This cast preserves the referent's size.
// - This cast preserves provenance.
// - By invariant on `TransparentWrapper`, `T` and `T::Inner` have
// `UnsafeCell`s at the same byte ranges.
// - By invariant on `TransparentWrapper<UnsafeCellVariance =
// Covariant>`, `T` and `T::Inner` have `UnsafeCell`s at the same
// byte ranges.
let c = unsafe { self.cast_unsized(|p| T::cast_into_inner(p)) };
// SAFETY: By invariant on `TransparentWrapper`, since `self`
// satisfies the alignment invariant `I::Alignment`, `c` (of type
Expand Down
68 changes: 41 additions & 27 deletions src/util.rs
Expand Up @@ -26,13 +26,12 @@ use crate::{
///
/// # Safety
///
/// `T: TransparentWrapper` implies that:
/// - `T` has the same size as [`T::Inner`]
/// - `T` has `UnsafeCell`s covering the same byte ranges as `T::Inner`
/// - `T` has zero-sized `UnsafeCell`s (e.g., `UnsafeCell<()>`,
/// `[UnsafeCell<u8>; 0]`, etc) at the same byte offsets as `T::Inner`
///
/// `T: TransparentWrapper` implies that `T` has the same size as [`T::Inner`].
/// Further, `T: TransparentWrapper<I>` implies that:
/// - If `T::UnsafeCellVariance = Covariant`, then:
/// - `T` has `UnsafeCell`s covering the same byte ranges as `T::Inner`
/// - `T` has zero-sized `UnsafeCell`s (e.g., `UnsafeCell<()>`,
/// `[UnsafeCell<u8>; 0]`, etc) at the same byte offsets as `T::Inner`
/// - If a `T` pointer satisfies the alignment invariant `I::Alignment`, then
/// that same pointer, cast to `T::Inner`, satisfies the alignment invariant
/// `<T::AlignmentVariance as AlignmentVariance<I::Alignment>>::Applied`.
Expand All @@ -48,6 +47,7 @@ use crate::{
pub unsafe trait TransparentWrapper<I: Invariants> {
type Inner: ?Sized;

type UnsafeCellVariance;
type AlignmentVariance: AlignmentVariance<I::Alignment>;
type ValidityVariance: ValidityVariance<I::Validity>;

Expand Down Expand Up @@ -106,14 +106,17 @@ impl<I: invariant::Validity> ValidityVariance<I> for Invariant {

// SAFETY:
// - Per [1], `MaybeUninit<T>` has the same size as `T`.
// - Per [1], `MaybeUninit<T>` has `UnsafeCell`s at the same byte ranges as
// `Inner = T`, and `UnsafeCell`s at the same byte offsets as `T`.
// - See inline comments for other safety justifications.
//
// [1] TODO(#896): Write a safety proof before the next stable release.
unsafe impl<T, I: Invariants> TransparentWrapper<I> for MaybeUninit<T> {
type Inner = T;

// SAFETY: Per [1], `MaybeUninit<T>` has `UnsafeCell`s at the same byte
// ranges as `Inner = T`, and `UnsafeCell`s at the same byte offsets as `T`.
//
// [1] TODO(#896): Write a safety proof before the next stable release.
type UnsafeCellVariance = Covariant;
// SAFETY: Per [1], `MaybeUninit<T>` has the same layout as `T`, and thus
// has the same alignment as `T`.
//
Expand Down Expand Up @@ -145,14 +148,17 @@ unsafe impl<T, I: Invariants> TransparentWrapper<I> for MaybeUninit<T> {

// SAFETY:
// - Per [1], `ManuallyDrop<T>` has the same size as `T`.
// - Per [1], `ManuallyDrop<T>` has `UnsafeCell`s at the same byte ranges as
// `Inner = T`, and `UnsafeCell`s at the same byte offsets as `T`.
// - See inline comments for other safety justifications.
//
// [1] TODO(#896): Write a safety proof before the next stable release.
unsafe impl<T: ?Sized, I: Invariants> TransparentWrapper<I> for ManuallyDrop<T> {
type Inner = T;

// SAFETY: Per [1], `ManuallyDrop<T>` has `UnsafeCell`s at the same byte
// ranges as `Inner = T`, and `UnsafeCell`s at the same byte offsets as `T`.
//
// [1] TODO(#896): Write a safety proof before the next stable release.
type UnsafeCellVariance = Covariant;
// SAFETY: Per [1], `ManuallyDrop<T>` has the same layout as `T`, and thus
// has the same alignment as `T`.
//
Expand Down Expand Up @@ -187,14 +193,17 @@ unsafe impl<T: ?Sized, I: Invariants> TransparentWrapper<I> for ManuallyDrop<T>

// SAFETY:
// - Per [1], `Wrapping<T>` has the same size as `T`.
// - Per [1], `Wrapping<T>` has `UnsafeCell`s at the same byte ranges as `Inner
// = T`, and `UnsafeCell`s at the same byte offsets as `T`.
// - See inline comments for other safety justifications.
//
// [1] TODO(#896): Write a safety proof before the next stable release.
unsafe impl<T, I: Invariants> TransparentWrapper<I> for Wrapping<T> {
type Inner = T;

// SAFETY: Per [1], `Wrapping<T>` has `UnsafeCell`s at the same byte ranges
// as `Inner = T`, and `UnsafeCell`s at the same byte offsets as `T`.
//
// [1] TODO(#896): Write a safety proof before the next stable release.
type UnsafeCellVariance = Covariant;
// SAFETY: Per [1], `Wrapping<T>` has the same layout as `T`, and thus has
// the same alignment as `T`.
//
Expand Down Expand Up @@ -235,14 +244,17 @@ unsafe impl<T, I: Invariants> TransparentWrapper<I> for Wrapping<T> {
}

// SAFETY: We define `Unalign<T>` to be a `#[repr(C, packed)]` type wrapping a
// single `T` field. Thus, `Unalign<T>` has the same size as `T`, has
// `UnsafeCell`s at the same byte ranges as `T`, and has `UnsafeCell`s at the
// same byte offsets as `T`.
// single `T` field. Thus, `Unalign<T>` has the same size as `T`.
//
// See inline comments for other safety justifications.
unsafe impl<T, I: Invariants> TransparentWrapper<I> for Unalign<T> {
type Inner = T;

// SAFETY: `Unalign<T>` is a `#[repr(C, packed)]` type wrapping a single `T`
// field, and so has `UnsafeCell`s at the same byte ranges as `Inner = T`,
// and `UnsafeCell`s at the same byte offsets as `T`.
type UnsafeCellVariance = Covariant;

// SAFETY: Since `Unalign<T>` is `repr(packed)`, it has the alignment 1
// regardless of `T`'s alignment. Thus, an aligned pointer to `Unalign<T>`
// is not necessarily an aligned pointer to `T`.
Expand Down Expand Up @@ -291,8 +303,19 @@ macro_rules! unsafe_impl_transparent_wrapper_for_atomic {
// native counterpart, respectively. Per [1], `$atomic` and `$native`
// have the same size.
//
// It is "obvious" that each atomic type contains a single `UnsafeCell`
// that covers all bytes of the type, but we can also prove it:
// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
// Cite docs once they've landed.
$(#[$attr])*
unsafe impl<$tyvar, I: Invariants> TransparentWrapper<I> for $atomic {
unsafe_impl_transparent_wrapper_for_atomic!(@inner $atomic [$native]);
}
};
(@inner $atomic:ty [$native:ty]) => {
type Inner = UnsafeCell<$native>;

// SAFETY: It is "obvious" that each atomic type contains a single
// `UnsafeCell` that covers all bytes of the type, but we can also prove
// it:
// - Since `$atomic` provides an API which permits loading and storing
// values of type `$native` via a `&self` (shared) reference, *some*
// interior mutation must be happening, and interior mutation can only
Expand All @@ -311,18 +334,9 @@ macro_rules! unsafe_impl_transparent_wrapper_for_atomic {
// set `type Inner = UnsafeCell<$native>`. Thus, `Self` and
// `Self::Inner` have `UnsafeCell`s covering the same byte ranges.
//
// See inline comments for safety requirements regarding invariant
// variance.
//
// [1] TODO(#896), TODO(https://github.com/rust-lang/rust/pull/121943):
// Cite docs once they've landed.
$(#[$attr])*
unsafe impl<$tyvar, I: Invariants> TransparentWrapper<I> for $atomic {
unsafe_impl_transparent_wrapper_for_atomic!(@inner $atomic [$native]);
}
};
(@inner $atomic:ty [$native:ty]) => {
type Inner = UnsafeCell<$native>;
type UnsafeCellVariance = Covariant;

// SAFETY: No safety justification is required for an invariant
// variance.
Expand Down

0 comments on commit f45a227

Please sign in to comment.