Skip to content

Commit

Permalink
[pointer][WIP] Validity in referent
Browse files Browse the repository at this point in the history
gherrit-pr-id: Icdd795ee43df33bd553deb69675c1cdca686a1d8
  • Loading branch information
joshlf committed Mar 3, 2025
1 parent ce4af82 commit 96ecd59
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 195 deletions.
80 changes: 56 additions & 24 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
pub trait Invariants: Sealed {
type Aliasing: Aliasing;
type Alignment: Alignment;
type Validity: Validity;
// type Validity: Validity;
}

impl<A: Aliasing, AA: Alignment, V: Validity> Invariants for (A, AA, V) {
impl<A: Aliasing, AA: Alignment> Invariants for (A, AA) {
type Aliasing = A;
type Alignment = AA;
type Validity = V;
// type Validity = V;
}

/// The aliasing invariant of a [`Ptr`][super::Ptr].
Expand Down Expand Up @@ -83,7 +83,17 @@ pub trait Alignment: Sealed {}
/// mechanism (e.g. a `&` reference used to derive `src`) to write `x` where
/// `x ∈ S(T, V)` but `x ∉ S(U, W)`, which would violate the guarantee that
/// `dst`'s referent may only contain values in `S(U, W)`.
pub unsafe trait Validity: Sealed {}
pub unsafe trait Validity: Sealed {
type Inner: ?Sized;
}

/// Does `V` have the same validity invariant as `Self`?
///
/// # Safety
///
/// Unsafe code may assume that `W: SameValidity<V>` guarantees that `V` and `W`
/// have the same validity invariant.
pub unsafe trait SameValidity<V>: Sealed {}

/// An [`Aliasing`] invariant which is either [`Shared`] or [`Exclusive`].
///
Expand Down Expand Up @@ -128,14 +138,24 @@ impl Alignment for Unaligned {}
pub enum Aligned {}
impl Alignment for Aligned {}

struct NeverPhantomData<T: ?Sized> {
_marker: core::marker::PhantomData<T>,
_never: core::convert::Infallible,
}

/// Any bit pattern is allowed in the `Ptr`'s referent, including uninitialized
/// bytes.
pub enum Uninit {}
pub struct Uninit<T: ?Sized>(NeverPhantomData<T>);
// SAFETY: `Uninit`'s validity is well-defined for all `T: ?Sized`, and is not a
// function of any property of `T` other than its bit validity (in fact, it's
// not even a property of `T`'s bit validity, but this is more than we are
// required to uphold).
unsafe impl Validity for Uninit {}
unsafe impl<T: ?Sized> Validity for Uninit<T> {
type Inner = T;
}

// SAFETY: The same validity (`Uninit`) is used for both types.
unsafe impl<T: ?Sized, U: ?Sized> SameValidity<Uninit<T>> for Uninit<U> {}

/// The byte ranges initialized in `T` are also initialized in the referent of a
/// `Ptr<T>`.
Expand Down Expand Up @@ -164,36 +184,48 @@ unsafe impl Validity for Uninit {}
/// variant's bit validity (although note that the variant may contain another
/// enum type, in which case the same rules apply depending on the state of
/// its discriminant, and so on recursively).
pub enum AsInitialized {}
pub struct AsInitialized<T: ?Sized>(NeverPhantomData<T>);
// SAFETY: `AsInitialized`'s validity is well-defined for all `T: ?Sized`, and
// is not a function of any property of `T` other than its bit validity.
unsafe impl Validity for AsInitialized {}
unsafe impl<T: ?Sized> Validity for AsInitialized<T> {
type Inner = T;
}
// SAFETY: The same validity (`AsInitialized`) is used for both types.
unsafe impl<T: ?Sized, U: ?Sized> SameValidity<AsInitialized<T>> for AsInitialized<U> {}

/// The byte ranges in the referent are fully initialized. In other words, if
/// the referent is `N` bytes long, then it contains a bit-valid `[u8; N]`.
pub enum Initialized {}
pub struct Initialized<T: ?Sized>(NeverPhantomData<T>);
// SAFETY: `Initialized`'s validity is well-defined for all `T: ?Sized`, and is
// not a function of any property of `T` other than its bit validity (in fact,
// it's not even a property of `T`'s bit validity, but this is more than we are
// required to uphold).
unsafe impl Validity for Initialized {}
unsafe impl<T: ?Sized> Validity for Initialized<T> {
type Inner = T;
}
// SAFETY: The same validity (`Initialized`) is used for both types.
unsafe impl<T: ?Sized, U: ?Sized> SameValidity<Initialized<T>> for Initialized<U> {}

/// The referent of a `Ptr<T>` is bit-valid for `T`.
pub enum Valid {}
pub struct Valid<T: ?Sized>(NeverPhantomData<T>);
// SAFETY: `Valid`'s validity is well-defined for all `T: ?Sized`, and is not a
// function of any property of `T` other than its bit validity.
unsafe impl Validity for Valid {}
unsafe impl<T: ?Sized> Validity for Valid<T> {
type Inner = T;
}
// SAFETY: The same validity (`Valid`) is used for both types.
unsafe impl<T: ?Sized, U: ?Sized> SameValidity<Valid<T>> for Valid<U> {}

/// # Safety
///
/// `DT: CastableFrom<ST, SV, DV>` is sound if `SV = DV = Uninit` or `SV = DV =
/// Initialized`.
pub unsafe trait CastableFrom<ST: ?Sized, SV, DV> {}
/// `U: CastableFrom<T>` is sound if `T` and `U` have the same validity, and
/// that validity is either [`Uninit`] or [`Initialized`].
pub unsafe trait CastableFrom<T: ?Sized> {}

// SAFETY: `SV = DV = Uninit`.
unsafe impl<ST: ?Sized, DT: ?Sized> CastableFrom<ST, Uninit, Uninit> for DT {}
// SAFETY: `SV = DV = Initialized`.
unsafe impl<ST: ?Sized, DT: ?Sized> CastableFrom<ST, Initialized, Initialized> for DT {}
// SAFETY: Both types have validity `Uninit`.
unsafe impl<T: ?Sized, U: ?Sized> CastableFrom<Uninit<T>> for Uninit<U> {}
// SAFETY: Both types have validity `Initialized`.
unsafe impl<T: ?Sized, U: ?Sized> CastableFrom<Initialized<T>> for Initialized<U> {}

/// [`Ptr`](crate::Ptr) referents that permit unsynchronized read operations.
///
Expand Down Expand Up @@ -238,12 +270,12 @@ mod sealed {
impl Sealed for Unaligned {}
impl Sealed for Aligned {}

impl Sealed for Uninit {}
impl Sealed for AsInitialized {}
impl Sealed for Initialized {}
impl Sealed for Valid {}
impl<T: ?Sized> Sealed for Uninit<T> {}
impl<T: ?Sized> Sealed for AsInitialized<T> {}
impl<T: ?Sized> Sealed for Initialized<T> {}
impl<T: ?Sized> Sealed for Valid<T> {}

impl<A: Sealed, AA: Sealed, V: Sealed> Sealed for (A, AA, V) {}
impl<A: Sealed, AA: Sealed> Sealed for (A, AA) {}

impl Sealed for BecauseImmutable {}
impl Sealed for BecauseExclusive {}
Expand Down
42 changes: 19 additions & 23 deletions src/pointer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,55 +17,51 @@ mod transmute;
#[doc(hidden)]
pub(crate) use transmute::*;
#[doc(hidden)]
pub use {
invariant::{BecauseExclusive, BecauseImmutable, Read},
ptr::Ptr,
};
pub use {invariant::*, ptr::Ptr};

use crate::Unaligned;

/// A shorthand for a maybe-valid, maybe-aligned reference. Used as the argument
/// to [`TryFromBytes::is_bit_valid`].
///
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unaligned> =
Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>;
pub type Maybe<'a, T, Aliasing = Shared, Alignment = Unaligned> =
Ptr<'a, Initialized<T>, (Aliasing, Alignment)>;

/// 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::Unaligned> =
Ptr<'a, T, (Aliasing, Alignment, invariant::Valid)>;
pub type MaybeAligned<'a, T, Aliasing = Shared, Alignment = Unaligned> =
Ptr<'a, Valid<T>, (Aliasing, Alignment)>;

// These methods are defined on the type alias, `MaybeAligned`, so as to bring
// them to the forefront of the rendered rustdoc for that type alias.
impl<'a, T, Aliasing, Alignment> MaybeAligned<'a, T, Aliasing, Alignment>
impl<'a, T, A, AA> MaybeAligned<'a, T, A, AA>
where
T: 'a + ?Sized,
Aliasing: invariant::Aliasing,
Alignment: invariant::Alignment,
A: Aliasing,
AA: Alignment,
{
/// Reads the value from `MaybeAligned`.
#[must_use]
#[inline]
pub fn read_unaligned<R>(self) -> T
where
T: Copy,
T: invariant::Read<Aliasing, R>,
T: Read<A, R> + Copy,
{
// SAFETY: By invariant on `MaybeAligned`, `self` contains
// validly-initialized data for `T`. By `T: Read<Aliasing>`, we are
// permitted to perform a read of `self`'s referent.
// validly-initialized data for `T`. By `T: Read<A>`, we are permitted
// to perform a read of `self`'s referent.
unsafe { self.as_inner().read_unaligned() }
}
}

impl<'a, T, Aliasing, Alignment> MaybeAligned<'a, T, Aliasing, Alignment>
impl<'a, T, A, AA> MaybeAligned<'a, T, A, AA>
where
T: 'a + ?Sized,
Aliasing: invariant::Reference,
Alignment: invariant::Alignment,
A: Reference,
AA: Alignment,
{
/// Views the value as an aligned reference.
///
Expand All @@ -74,18 +70,18 @@ where
#[inline]
pub fn unaligned_as_ref(self) -> &'a T
where
T: Unaligned,
T: crate::Unaligned,
{
self.bikeshed_recall_aligned().as_ref()
}
}

/// Checks if the referent is zeroed.
pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, T, I>) -> bool
pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, Initialized<T>, I>) -> bool
where
T: crate::Immutable + crate::KnownLayout,
I: invariant::Invariants<Validity = invariant::Initialized>,
I::Aliasing: invariant::Reference,
T: crate::Immutable + crate::KnownLayout + ?Sized,
I: Invariants,
I::Aliasing: Reference,
{
ptr.as_bytes::<BecauseImmutable>().as_ref().iter().all(|&byte| byte == 0)
}
Loading

0 comments on commit 96ecd59

Please sign in to comment.