Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ptr] Add TransparentWrapper #958

Merged
merged 1 commit into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 5 additions & 41 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1337,7 +1337,8 @@ pub unsafe trait TryFromBytes {
Self: Sized + NoCell, // TODO(#251): Remove the `NoCell` bound.
{
let candidate = MaybeUninit::<Self>::read_from(bytes)?;
let c_ptr = Ptr::from_maybe_uninit_ref(&candidate);
let c_ptr = Ptr::from_ref(&candidate);
let c_ptr = c_ptr.transparent_wrapper_into_inner();
// SAFETY: `c_ptr` has no uninitialized sub-ranges because it derived
// from `candidate`, which in turn derives from `bytes: &[u8]`.
let c_ptr = unsafe { c_ptr.assume_validity::<invariant::Initialized>() };
Expand Down Expand Up @@ -3649,33 +3650,7 @@ safety_comment! {
unsafe_impl!(
T: ?Sized + TryFromBytes => TryFromBytes for ManuallyDrop<T>;
|candidate: Maybe<ManuallyDrop<T>>| {
// SAFETY:
// - Since `ManuallyDrop<T>` has the same layout as `T` [1], this
// cast preserves size.
// - This is implemented as a raw pointer cast as required by
// `cast_unsized`.
// - `T` and `ManuallyDrop<T>` have `UnsafeCell`s at the same byte
// ranges [2].
//
//
// [1] Per https://doc.rust-lang.org/nightly/core/mem/struct.ManuallyDrop.html:
//
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit
// validity as `T`.
//
// [2] TODO(#896): Write safety proof before next stable release.
#[allow(clippy::as_conversions)]
let c = unsafe { candidate.cast_unsized(|p: *mut ManuallyDrop<T>| p as *mut T) };

// SAFETY: Since the original `candidate` had the validity
// `Initialized`, and since `c` refers to the same bytes, `c` does
// too.
let c = unsafe { c.assume_validity::<invariant::Initialized>() };
// Confirm that `Maybe` is a type alias for `Ptr` with the validity
// invariant `Initialized`. Our safety proof depends upon this
// invariant, and it might change at some point. If that happens, we
// want this function to stop compiling.
let _: Ptr<'_, ManuallyDrop<T>, (_, _, invariant::Initialized)> = candidate;
let c = candidate.transparent_wrapper_into_inner();

// SAFETY: Since `ManuallyDrop<T>` has the same bit validity as `T`
// [1], it is bit-valid exactly if its bytes are a bit-valid `T`.
Expand Down Expand Up @@ -3771,19 +3746,8 @@ unsafe impl<T: TryFromBytes> TryFromBytes for UnsafeCell<T> {
let c: &mut Unalign<MaybeUninit<T>> = c.as_mut().get_mut();
// This converts from an aligned `Unalign<MaybeUninit<T>>` pointer to an
// unaligned `MaybeUninit<T>` pointer.
let c: Ptr<'_, MaybeUninit<T>, _> = Ptr::from_mut(c).into_inner();

// SAFETY: `MaybeUninit<T>` has the same size as `T` [1]. Thus, this
// cast will preserve the size of the pointer. `MaybeUninit<T>` has
// `UnsafeCell`s at the same byte ranges as `T` [2].
//
// [1] Per https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#layout-1:
//
// MaybeUninit<T> is guaranteed to have the same size, alignment, and
// ABI as T.
//
// [2] TODO(#896): Write safety proof before next stable release.
let c: Ptr<'_, T, _> = unsafe { c.cast_unsized(|c: *mut MaybeUninit<T>| c.cast::<T>()) };
let c: Ptr<'_, MaybeUninit<T>, _> = Ptr::from_mut(c).transparent_wrapper_into_inner();
let c: Ptr<'_, T, _> = c.transparent_wrapper_into_inner();

// SAFETY: The original `candidate` argument has `Initialized` validity.
// None of the subsequent operations modify the memory itself, and so
Expand Down
4 changes: 2 additions & 2 deletions src/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ use crate::Unaligned;
/// to [`TryFromBytes::is_bit_valid`].
///
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::AnyAlignment> =
pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Any> =
Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>;

/// A semi-user-facing wrapper type representing a maybe-aligned reference, for
/// use in [`TryFromBytes::is_bit_valid`].
///
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
pub type MaybeAligned<'a, T, Aliasing = invariant::Shared, Alignment = invariant::AnyAlignment> =
pub type MaybeAligned<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Any> =
Ptr<'a, T, (Aliasing, Alignment, invariant::Valid)>;

// These methods are defined on the type alias, `MaybeAligned`, so as to bring
Expand Down
153 changes: 62 additions & 91 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ macro_rules! define_system {
$($set: super::$set,)*
{}

impl Sealed for super::Any {}

$($(
impl Sealed for super::$elem {}
)*)*
Expand Down Expand Up @@ -201,6 +203,10 @@ macro_rules! define_system {
const NAME: &'static str;
}

impl $set for Any {
const NAME: &'static str = stringify!(Any);
}

$(
$(#[$elem_attr])*
#[allow(missing_copy_implementations, missing_debug_implementations)]
Expand All @@ -221,7 +227,7 @@ macro_rules! define_system {
// (instead of just the trailing, repeating impl block), we
// ensure that we don't define `at_least` traits that only have
// a trivial implementation (e.g., `at_least::Exclusive for
// Exclusive`, `at_least::AnyValidity for AnyValidity`, etc).
// Exclusive`, etc).
$(
#[doc = concat!(
"[",
Expand Down Expand Up @@ -249,15 +255,17 @@ macro_rules! define_system {
/// Invariants are encoded as ([`Aliasing`][invariant::Aliasing],
/// [`Alignment`][invariant::Alignment], [`Validity`][invariant::Validity])
/// triples implementing the [`Invariants`] trait.
#[doc(hidden)]
pub mod invariant {
/// No requirement - any invariant is allowed.
#[allow(missing_copy_implementations, missing_debug_implementations)]
pub enum Any {}

define_system! {
/// The invariants of a [`Ptr`][super::Ptr].
Invariants {
/// The aliasing invariant of a [`Ptr`][super::Ptr].
Aliasing {
/// No aliasing invariant.
AnyAliasing,

/// The `Ptr<'a, T>` adheres to the aliasing rules of a `&'a T`.
///
/// The referent of a shared-aliased `Ptr` may be concurrently
Expand All @@ -281,18 +289,13 @@ pub mod invariant {

/// The alignment invariant of a [`Ptr`][super::Ptr].
Alignment {
/// The referent is not necessarily aligned.
AnyAlignment,
/// The referent is aligned: for `Ptr<T>`, the referent's
/// address is a multiple of the `T`'s alignment.
Aligned,
}

/// The validity invariant of a [`Ptr`][super::Ptr].
Validity {
/// The referent is not necessarily initialized.
AnyValidity,

/// The byte ranges initialized in `T` are also initialized in
/// the referent.
///
Expand Down Expand Up @@ -385,9 +388,8 @@ mod _external {

/// Methods for converting to and from `Ptr` and Rust's safe reference types.
mod _conversions {
use core::mem::MaybeUninit;

use super::*;
use crate::util::{AlignmentVariance, TransparentWrapper, ValidityVariance};

/// `&'a T` → `Ptr<'a, T>`
impl<'a, T> Ptr<'a, T, (invariant::Shared, invariant::Aligned, invariant::Valid)>
Expand Down Expand Up @@ -604,45 +606,45 @@ mod _conversions {
}
}

/// `&'a MaybeUninit<T>` → `Ptr<'a, T>`
impl<'a, T> Ptr<'a, T, (invariant::Shared, invariant::Aligned, invariant::AnyValidity)>
/// `Ptr<'a, T = Wrapper<U>>` → `Ptr<'a, U>`
impl<'a, T, I> Ptr<'a, T, I>
where
T: 'a,
T: 'a + TransparentWrapper<I> + ?Sized,
I: Invariants,
{
/// Constructs a `Ptr` from a shared reference to a [`MaybeUninit<T>`].
///
/// [`MaybeUninit<T>`]: MaybeUninit
#[doc(hidden)]
#[inline]
pub fn from_maybe_uninit_ref(mu: &'a MaybeUninit<T>) -> Self {
let ptr = Ptr::from_ref(mu);

/// Converts the `Ptr` to a transparent wrapper type into a `Ptr` to the
/// wrapped inner type.
pub(crate) fn transparent_wrapper_into_inner(
self,
) -> Ptr<
'a,
T::Inner,
(
I::Aliasing,
<T::AlignmentVariance as AlignmentVariance<I::Alignment>>::Applied,
<T::ValidityVariance as ValidityVariance<I::Validity>>::Applied,
),
> {
// SAFETY:
// - Since `MaybeUninit<T>` has the same size as `T` [1],
// this cast does not increase the referent's size.
// - Since `MaybeUninit<T>` has the same layout as `T` [2], `T` has
// `UnsafeCell`s at exactly the same ranges as `MaybeUninit<T>`.
//
// [1] Per https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#layout-1:
//
// `MaybeUninit<T>` is guaranteed to have the same size,
// alignment, and ABI as `T`
//
// [2] TODO(https://github.com/rust-lang/rust/pull/121215)
//
// TODO(#896): Finish this safety proof (namely, [2]) before the
// next stable release.
#[allow(clippy::as_conversions)]
let ptr = unsafe { ptr.cast_unsized(|p| p.cast::<T>()) };
// SAFETY: Since `MaybeUninit<T>` has the same alignment as `T` [1],
// the fact that `mu` is aligned to `MaybeUninit<T>` means that it
// is aligned to `T`.
//
// [1] Per https://doc.rust-lang.org/std/mem/union.MaybeUninit.html#layout-1:
//
// `MaybeUninit<T>` is guaranteed to have the same size,
// alignment, and ABI as `T`
unsafe { ptr.assume_alignment::<invariant::Aligned>() }
// - 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.
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
// `T::Inner`) satisfies the given "applied" alignment invariant.
let c = unsafe {
c.assume_alignment::<<T::AlignmentVariance as AlignmentVariance<I::Alignment>>::Applied>()
};
// SAFETY: By invariant on `TransparentWrapper`, since `self`
// satisfies the validity invariant `I::Validity`, `c` (of type
// `T::Inner`) satisfies the given "applied" validity invariant.
let c = unsafe {
c.assume_validity::<<T::ValidityVariance as ValidityVariance<I::Validity>>::Applied>()
};
c
}
}
}
Expand Down Expand Up @@ -870,10 +872,8 @@ mod _transitions {
/// Forgets that `Ptr`'s referent is validly-aligned for `T`.
#[doc(hidden)]
#[inline]
pub fn forget_aligned(
self,
) -> Ptr<'a, T, (I::Aliasing, invariant::AnyAlignment, I::Validity)> {
// SAFETY: `AnyAlignment` is less restrictive than `Aligned`.
pub fn forget_aligned(self) -> Ptr<'a, T, (I::Aliasing, invariant::Any, I::Validity)> {
// SAFETY: `Any` is less restrictive than `Aligned`.
unsafe { Ptr::from_ptr(self) }
}
}
Expand All @@ -882,7 +882,6 @@ mod _transitions {
/// Casts of the referent type.
mod _casts {
use super::*;
use crate::Unalign;

impl<'a, T, I> Ptr<'a, T, I>
where
Expand Down Expand Up @@ -929,7 +928,7 @@ mod _casts {
pub unsafe fn cast_unsized<U: 'a + ?Sized, F: FnOnce(*mut T) -> *mut U>(
self,
cast: F,
) -> Ptr<'a, U, (I::Aliasing, invariant::AnyAlignment, invariant::AnyValidity)>
) -> Ptr<'a, U, (I::Aliasing, invariant::Any, invariant::Any)>
where
U: 'a,
{
Expand Down Expand Up @@ -962,9 +961,9 @@ mod _casts {
// 6. `ptr` conforms to the aliasing invariant of `I::Aliasing`
// because casting does not impact the aliasing invariant.
// 7. `ptr`, trivially, conforms to the alignment invariant of
// `AnyAlignment`.
// `Any`.
// 8. `ptr`, trivially, conforms to the validity invariant of
// `AnyValidity`.
// `Any`.
// 9. During the lifetime 'a, no code will reference or load or
// store this memory region treating `UnsafeCell`s as existing at
// different ranges than they exist in `U`. This is true by
Expand Down Expand Up @@ -1100,7 +1099,7 @@ mod _casts {
pub(crate) fn try_cast_into<U: 'a + ?Sized + KnownLayout>(
&self,
cast_type: _CastType,
) -> Option<(Ptr<'a, U, (I::Aliasing, invariant::Aligned, invariant::AnyValidity)>, usize)>
) -> Option<(Ptr<'a, U, (I::Aliasing, invariant::Aligned, invariant::Any)>, usize)>
where
U: NoCell,
{
Expand Down Expand Up @@ -1175,7 +1174,7 @@ mod _casts {
// promises that the object described by `split_at` is validly
// aligned for `U`.
// 8. `ptr`, trivially, conforms to the validity invariant of
// `AnyValidity`.
// `Any`.
// 9. During the lifetime 'a, no code will reference or load or
// store this memory region treating `UnsafeCell`s as existing at
// different ranges than they exist in `U`. This is true by
Expand All @@ -1199,7 +1198,7 @@ mod _casts {
#[inline(always)]
pub(crate) fn try_cast_into_no_leftover<U: 'a + ?Sized + KnownLayout>(
&self,
) -> Option<Ptr<'a, U, (I::Aliasing, invariant::Aligned, invariant::AnyValidity)>>
) -> Option<Ptr<'a, U, (I::Aliasing, invariant::Aligned, invariant::Any)>>
where
U: NoCell,
{
Expand All @@ -1212,30 +1211,6 @@ mod _casts {
}
}
}

impl<'a, T, I> Ptr<'a, Unalign<T>, I>
where
T: 'a,
I: Invariants,
{
/// Converts a possibly-aligned pointer to [`Unalign<T>`] to an
/// unaligned pointer to `T`.
///
/// [`Unalign<T>`]: crate::Unalign
#[inline]
pub(crate) fn into_inner(
self,
) -> Ptr<'a, T, (I::Aliasing, invariant::AnyAlignment, I::Validity)> {
// SAFETY: `Unalign<T>` has the same size as `T`, and so this cast
// preserves size. It also has the same layout as `T` (not including
// alignment), and so `UnsafeCell`s exist at the same byte ranges in
// `Unalign<T>` and `T`.
let ptr = unsafe { self.cast_unsized(|p: *mut Unalign<T>| p.cast::<T>()) };
// SAFETY: `Unalign<T>` has the same validity as `T`, and so the
// preceding cast preserved validity.
unsafe { ptr.assume_validity::<I::Validity>() }
}
}
}

/// Projections through the referent.
Expand Down Expand Up @@ -1272,7 +1247,7 @@ mod _project {
pub unsafe fn project<U: 'a + ?Sized>(
self,
projector: impl FnOnce(*mut T) -> *mut U,
) -> Ptr<'a, U, (I::Aliasing, invariant::AnyAlignment, invariant::Initialized)> {
) -> Ptr<'a, U, (I::Aliasing, invariant::Any, invariant::Initialized)> {
// SAFETY: `projector` is provided with `self` casted to a raw
// pointer.
let field = projector(self.as_non_null().as_ptr());
Expand Down Expand Up @@ -1315,7 +1290,7 @@ mod _project {
// 6. `field` conforms to the aliasing invariant of `I::Aliasing`
// because projection does not impact the aliasing invariant.
// 7. `field`, trivially, conforms to the alignment invariant of
// `AnyAlignment`.
// `Any`.
// 8. By type bound on `I::Validity`, `self` satisfies the
// "as-initialized" property relative to `T`. The returned `Ptr`
// has the validity `AsInitialized`. The caller promises that `T`
Expand Down Expand Up @@ -1516,11 +1491,7 @@ mod tests {

// SAFETY: The bytes in `slf` must be initialized.
unsafe fn validate_and_get_len<T: ?Sized + KnownLayout + FromBytes>(
slf: Ptr<
'_,
T,
(invariant::Shared, invariant::Aligned, invariant::AnyValidity),
>,
slf: Ptr<'_, T, (invariant::Shared, invariant::Aligned, invariant::Any)>,
) -> usize {
// SAFETY:
// - Since all bytes in `slf` are initialized and
Expand Down Expand Up @@ -1606,11 +1577,11 @@ mod tests {
// Test that the correct invariant relationships hold.
use super::invariant::*;

assert_not_impl_any!(AnyAliasing: at_least::Shared);
assert_not_impl_any!(Any: at_least::Shared);
assert_impl_all!(Shared: at_least::Shared);
assert_impl_all!(Exclusive: at_least::Shared);

assert_not_impl_any!(AnyValidity: at_least::AsInitialized);
assert_not_impl_any!(Any: at_least::AsInitialized);
assert_impl_all!(AsInitialized: at_least::AsInitialized);
assert_impl_all!(Initialized: at_least::AsInitialized);
assert_impl_all!(Valid: at_least::AsInitialized);
Expand Down
Loading
Loading