Skip to content

[pointer] Make invariants opaque, more ergonomic #1895

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

Merged
merged 1 commit into from
Oct 14, 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
68 changes: 48 additions & 20 deletions src/pointer/invariant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,40 @@ pub trait Invariants: Sealed {
type Aliasing: Aliasing;
type Alignment: Alignment;
type Validity: Validity;

/// Invariants identical to `Self` except with a different aliasing
/// invariant.
type WithAliasing<A: Aliasing>: Invariants<
Aliasing = A,
Alignment = Self::Alignment,
Validity = Self::Validity,
>;

/// Invariants identical to `Self` except with a different alignment
/// invariant.
type WithAlignment<A: Alignment>: Invariants<
Aliasing = Self::Aliasing,
Alignment = A,
Validity = Self::Validity,
>;

/// Invariants identical to `Self` except with a different validity
/// invariant.
type WithValidity<V: Validity>: Invariants<
Aliasing = Self::Aliasing,
Alignment = Self::Alignment,
Validity = V,
>;
}

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

type WithAliasing<AB: Aliasing> = (AB, AA, V);
type WithAlignment<AB: Alignment> = (A, AB, V);
type WithValidity<VB: Validity> = (A, AA, VB);
}

/// The aliasing invariant of a [`Ptr`][super::Ptr].
Expand Down Expand Up @@ -107,29 +135,29 @@ impl Alignment for Aligned {}
/// The byte ranges initialized in `T` are also initialized in the referent.
///
/// Formally: uninitialized bytes may only be present in `Ptr<T>`'s referent
/// where they are guaranteed to be present in `T`. This is a dynamic property:
/// if, at a particular byte offset, a valid enum discriminant is set, the
/// subsequent bytes may only have uninitialized bytes as specificed by the
/// corresponding enum.
/// where they are guaranteed to be present in `T`. This is a dynamic
/// property: if, at a particular byte offset, a valid enum discriminant is
/// set, the subsequent bytes may only have uninitialized bytes as
/// specificed by the corresponding enum.
///
/// Formally, given `len = size_of_val_raw(ptr)`, at every byte offset, `b`, in
/// the range `[0, len)`:
/// - If, in any instance `t: T` of length `len`, the byte at offset `b` in `t`
/// is initialized, then the byte at offset `b` within `*ptr` must be
/// Formally, given `len = size_of_val_raw(ptr)`, at every byte offset, `b`,
/// in the range `[0, len)`:
/// - If, in any instance `t: T` of length `len`, the byte at offset `b` in
/// `t` is initialized, then the byte at offset `b` within `*ptr` must be
/// initialized.
/// - Let `c` be the contents of the byte range `[0, b)` in `*ptr`. Let `S` be
/// the subset of valid instances of `T` of length `len` which contain `c` in
/// the offset range `[0, b)`. If, in any instance of `t: T` in `S`, the byte
/// at offset `b` in `t` is initialized, then the byte at offset `b` in `*ptr`
/// must be initialized.
/// - Let `c` be the contents of the byte range `[0, b)` in `*ptr`. Let `S`
/// be the subset of valid instances of `T` of length `len` which contain
/// `c` in the offset range `[0, b)`. If, in any instance of `t: T` in
/// `S`, the byte at offset `b` in `t` is initialized, then the byte at
/// offset `b` in `*ptr` must be initialized.
///
/// Pragmatically, this means that if `*ptr` is guaranteed to contain an enum
/// type at a particular offset, and the enum discriminant stored in `*ptr`
/// corresponds to a valid variant of that enum type, then it is guaranteed
/// that the appropriate bytes of `*ptr` are initialized as defined by that
/// 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).
/// Pragmatically, this means that if `*ptr` is guaranteed to contain an
/// enum type at a particular offset, and the enum discriminant stored in
/// `*ptr` corresponds to a valid variant of that enum type, then it is
/// guaranteed that the appropriate bytes of `*ptr` are initialized as
/// defined by that 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 {}
impl Validity for AsInitialized {}

Expand Down
38 changes: 15 additions & 23 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -401,9 +401,7 @@ mod _conversions {
{
/// Converts a `Ptr` an unaligned `T` into a `Ptr` to an aligned
/// `Unalign<T>`.
pub(crate) fn into_unalign(
self,
) -> Ptr<'a, crate::Unalign<T>, (I::Aliasing, Aligned, I::Validity)> {
pub(crate) fn into_unalign(self) -> Ptr<'a, crate::Unalign<T>, I::WithAlignment<Aligned>> {
// SAFETY:
// - This cast preserves provenance.
// - This cast preserves address. `Unalign<T>` promises to have the
Expand All @@ -421,7 +419,7 @@ mod _conversions {
// SAFETY: `Unalign<T>` promises to have alignment 1, and so it is
// trivially aligned.
let ptr = unsafe { ptr.assume_alignment::<Aligned>() };
ptr
ptr.unify_invariants()
}
}
}
Expand All @@ -446,7 +444,7 @@ mod _transitions {
#[inline]
pub(crate) fn into_exclusive_or_post_monomorphization_error(
self,
) -> Ptr<'a, T, (Exclusive, I::Alignment, I::Validity)> {
) -> Ptr<'a, T, I::WithAliasing<Exclusive>> {
// NOTE(https://github.com/rust-lang/rust/issues/131625): We do this
// rather than just having `Aliasing::IS_EXCLUSIVE` have the panic
// behavior because doing it that way causes rustdoc to fail while
Expand Down Expand Up @@ -506,7 +504,7 @@ mod _transitions {
#[inline]
pub(crate) const unsafe fn assume_aliasing<A: Aliasing>(
self,
) -> Ptr<'a, T, (A, I::Alignment, I::Validity)> {
) -> Ptr<'a, T, I::WithAliasing<A>> {
// SAFETY: The caller promises that `self` satisfies the aliasing
// requirements of `A`.
unsafe { self.assume_invariants() }
Expand All @@ -523,7 +521,7 @@ mod _transitions {
#[inline]
pub(crate) const unsafe fn assume_exclusive(
self,
) -> Ptr<'a, T, (Exclusive, I::Alignment, I::Validity)> {
) -> Ptr<'a, T, I::WithAliasing<Exclusive>> {
// SAFETY: The caller promises that `self` satisfies the aliasing
// requirements of `Exclusive`.
unsafe { self.assume_aliasing::<Exclusive>() }
Expand All @@ -539,7 +537,7 @@ mod _transitions {
#[inline]
pub(crate) const unsafe fn assume_alignment<A: Alignment>(
self,
) -> Ptr<'a, T, (I::Aliasing, A, I::Validity)> {
) -> Ptr<'a, T, I::WithAlignment<A>> {
// SAFETY: The caller promises that `self`'s referent is
// well-aligned for `T` if required by `A` .
unsafe { self.assume_invariants() }
Expand All @@ -549,7 +547,7 @@ mod _transitions {
/// on success.
pub(crate) fn bikeshed_try_into_aligned(
self,
) -> Result<Ptr<'a, T, (I::Aliasing, Aligned, I::Validity)>, AlignmentError<Self, T>>
) -> Result<Ptr<'a, T, I::WithAlignment<Aligned>>, AlignmentError<Self, T>>
where
T: Sized,
{
Expand All @@ -567,9 +565,7 @@ mod _transitions {
#[inline]
// TODO(#859): Reconsider the name of this method before making it
// public.
pub(crate) const fn bikeshed_recall_aligned(
self,
) -> Ptr<'a, T, (I::Aliasing, Aligned, I::Validity)>
pub(crate) const fn bikeshed_recall_aligned(self) -> Ptr<'a, T, I::WithAlignment<Aligned>>
where
T: crate::Unaligned,
{
Expand All @@ -588,9 +584,7 @@ mod _transitions {
#[doc(hidden)]
#[must_use]
#[inline]
pub const unsafe fn assume_validity<V: Validity>(
self,
) -> Ptr<'a, T, (I::Aliasing, I::Alignment, V)> {
pub const unsafe fn assume_validity<V: Validity>(self) -> Ptr<'a, T, I::WithValidity<V>> {
// SAFETY: The caller promises that `self`'s referent conforms to
// the validity requirement of `V`.
unsafe { self.assume_invariants() }
Expand All @@ -605,9 +599,7 @@ mod _transitions {
#[doc(hidden)]
#[must_use]
#[inline]
pub const unsafe fn assume_initialized(
self,
) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Initialized)> {
pub const unsafe fn assume_initialized(self) -> Ptr<'a, T, I::WithValidity<Initialized>> {
// SAFETY: The caller has promised to uphold the safety
// preconditions.
unsafe { self.assume_validity::<Initialized>() }
Expand All @@ -622,7 +614,7 @@ mod _transitions {
#[doc(hidden)]
#[must_use]
#[inline]
pub const unsafe fn assume_valid(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)> {
pub const unsafe fn assume_valid(self) -> Ptr<'a, T, I::WithValidity<Valid>> {
// SAFETY: The caller has promised to uphold the safety
// preconditions.
unsafe { self.assume_validity::<Valid>() }
Expand All @@ -634,7 +626,7 @@ mod _transitions {
#[inline]
// TODO(#859): Reconsider the name of this method before making it
// public.
pub const fn bikeshed_recall_valid(self) -> Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>
pub const fn bikeshed_recall_valid(self) -> Ptr<'a, T, I::WithValidity<Valid>>
where
T: crate::FromBytes,
I: Invariants<Validity = Initialized>,
Expand All @@ -661,7 +653,7 @@ mod _transitions {
#[inline]
pub(crate) fn try_into_valid(
mut self,
) -> Result<Ptr<'a, T, (I::Aliasing, I::Alignment, Valid)>, ValidityError<Self, T>>
) -> Result<Ptr<'a, T, I::WithValidity<Valid>>, ValidityError<Self, T>>
where
T: TryFromBytes,
I::Aliasing: Reference,
Expand All @@ -670,7 +662,7 @@ mod _transitions {
// This call may panic. If that happens, it doesn't cause any soundness
// issues, as we have not generated any invalid state which we need to
// fix before returning.
if T::is_bit_valid(self.reborrow().forget_aligned()) {
if T::is_bit_valid(self.reborrow().forget_aligned().unify_invariants()) {
// SAFETY: If `T::is_bit_valid`, code may assume that `self`
// contains a bit-valid instance of `Self`.
Ok(unsafe { self.assume_valid() })
Expand All @@ -683,7 +675,7 @@ mod _transitions {
#[doc(hidden)]
#[must_use]
#[inline]
pub const fn forget_aligned(self) -> Ptr<'a, T, (I::Aliasing, Any, I::Validity)> {
pub const fn forget_aligned(self) -> Ptr<'a, T, I::WithAlignment<Any>> {
// SAFETY: `Any` is less restrictive than `Aligned`.
unsafe { self.assume_invariants() }
}
Expand Down
Loading