Skip to content
Closed
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
70 changes: 68 additions & 2 deletions src/pointer/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// This file may not be copied, modified, or distributed except according to
// those terms.

use core::{marker::PhantomData, ops::Range, ptr::NonNull};
use core::{marker::PhantomData, mem, ops::Range, ptr::NonNull};

#[allow(unused_imports)]
use crate::util::polyfills::NumExt as _;
Expand Down Expand Up @@ -89,7 +89,9 @@ mod _def {
/// Rust allocation, `A`.
/// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
/// for at least `'a`.
pub(crate) const unsafe fn new(ptr: NonNull<T>) -> PtrInner<'a, T> {
#[inline(always)]
#[must_use]
pub const unsafe fn new(ptr: NonNull<T>) -> PtrInner<'a, T> {
// SAFETY: The caller has promised to satisfy all safety invariants
// of `PtrInner`.
Self { ptr, _marker: PhantomData }
Expand Down Expand Up @@ -160,6 +162,39 @@ impl<'a, T: ?Sized> PtrInner<'a, T> {
// single allocated object.
unsafe { Self::new(ptr) }
}

#[must_use]
#[inline(always)]
pub fn cast_sized<U>(self) -> PtrInner<'a, U>
where
T: Sized,
{
static_assert!(T, U => mem::size_of::<T>() >= mem::size_of::<U>());
// SAFETY: By the preceding assert, `U` is no larger than `T`, which is
// the size of `self`'s referent.
unsafe { self.cast() }
}

/// # Safety
///
/// `U` must not be larger than the size of `self`'s referent.
#[must_use]
#[inline(always)]
pub unsafe fn cast<U>(self) -> PtrInner<'a, U> {
let ptr = self.as_non_null().cast::<U>();

// SAFETY: The caller promises that `U` is no larger than `self`'s
// referent. Thus, `ptr` addresses a subset of the bytes addressed by
// `self`.
//
// 0. By invariant on `self`, if `self`'s referent is not zero sized,
// then `self` has valid provenance for its referent, which is
// entirely contained in some Rust allocation, `A`. Thus, the same
// holds of `ptr`.
// 1. By invariant on `self`, if `self`'s referent is not zero sized,
// then `A` is guaranteed to live for at least `'a`.
unsafe { PtrInner::new(ptr) }
}
}

#[allow(clippy::needless_lifetimes)]
Expand Down Expand Up @@ -216,6 +251,37 @@ where
// zero sized, then `A` is guaranteed to live for at least `'a`.
unsafe { PtrInner::new(raw) }
}

pub(crate) fn as_bytes(self) -> PtrInner<'a, [u8]> {
let ptr = self.as_non_null();
let bytes = match T::size_of_val_raw(ptr) {
Some(bytes) => bytes,
// SAFETY: `KnownLayout::size_of_val_raw` promises to always
// return `Some` so long as the resulting size fits in a
// `usize`. By invariant on `PtrInner`, `self` refers to a range
// of bytes whose size fits in an `isize`, which implies that it
// also fits in a `usize`.
None => unsafe { core::hint::unreachable_unchecked() },
};

let ptr = core::ptr::slice_from_raw_parts_mut(ptr.cast::<u8>().as_ptr(), bytes);

// SAFETY: `ptr` has the same address as `ptr = self.as_non_null()`,
// which is non-null by construction.
let ptr = unsafe { NonNull::new_unchecked(ptr) };

// SAFETY: `ptr` points to `bytes` `u8`s starting at the same address as
// `self`'s referent. Since `bytes` is the length of `self`'s referent,
// `ptr` addresses the same byte range as `self`. Thus, by invariant on
// `self` (as a `PtrInner`):
//
// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
// provenance for its referent, which is entirely contained in some
// Rust allocation, `A`.
// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
// for at least `'a`.
unsafe { PtrInner::new(ptr) }
}
}

#[allow(clippy::needless_lifetimes)]
Expand Down
112 changes: 24 additions & 88 deletions src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
use core::{
fmt::{Debug, Formatter},
marker::PhantomData,
ptr::NonNull,
};

use crate::{
Expand Down Expand Up @@ -50,6 +49,7 @@ mod def {
///
/// `Ptr<'a, T>` is [covariant] in `'a` and invariant in `T`.
///
/// [`NonNull<T>`]: core::ptr::NonNull
/// [covariant]: https://doc.rust-lang.org/reference/subtyping.html
pub struct Ptr<'a, T, I>
where
Expand All @@ -74,32 +74,6 @@ mod def {
T: 'a + ?Sized,
I: Invariants,
{
/// Constructs a `Ptr` from a [`NonNull`].
///
/// # Safety
///
/// The caller promises that:
///
/// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
/// provenance for its referent, which is entirely contained in some
/// Rust allocation, `A`.
/// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
/// for at least `'a`.
/// 2. `ptr` conforms to the aliasing invariant of
/// [`I::Aliasing`](invariant::Aliasing).
/// 3. `ptr` conforms to the alignment invariant of
/// [`I::Alignment`](invariant::Alignment).
/// 4. `ptr` conforms to the validity invariant of
/// [`I::Validity`](invariant::Validity).
pub(super) unsafe fn new(ptr: NonNull<T>) -> Ptr<'a, T, I> {
// SAFETY: The caller has promised (in 0 - 1) to satisfy all safety
// invariants of `PtrInner::new`.
let ptr = unsafe { PtrInner::new(ptr) };
// SAFETY: The caller has promised (in 2 - 4) to satisfy all safety
// invariants of `Ptr`.
Self { ptr, _invariants: PhantomData }
}

/// Constructs a new `Ptr` from a [`PtrInner`].
///
/// # Safety
Expand Down Expand Up @@ -402,7 +376,7 @@ mod _conversions {
// operate on these references simultaneously
// - By `U: TransmuteFromPtr<T, I::Aliasing, I::Validity, V>`, it is
// sound to perform this transmute.
unsafe { self.transmute_unchecked(|ptr| SizeEq::cast_from_raw(ptr).as_non_null()) }
unsafe { self.transmute_unchecked(SizeEq::cast_from_raw) }
}

#[doc(hidden)]
Expand All @@ -420,7 +394,7 @@ mod _conversions {
// referent simultaneously
// - By `T: TransmuteFromPtr<T, I::Aliasing, I::Validity, V>`, it is
// sound to perform this transmute.
let ptr = unsafe { self.transmute_unchecked(|t| t.as_non_null()) };
let ptr = unsafe { self.transmute_unchecked(SizeEq::cast_from_raw) };
// SAFETY: `self` and `ptr` have the same address and referent type.
// Therefore, if `self` satisfies `I::Alignment`, then so does
// `ptr`.
Expand Down Expand Up @@ -450,12 +424,6 @@ mod _conversions {
/// `I::Aliasing`, `I::Validity`, and `V`, and may depend upon the
/// presence, absence, or specific location of `UnsafeCell`s in `T`
/// and/or `U`. See [`Validity`] for more details.
///
/// `transmute_unchecked` guarantees that the pointer passed to `cast`
/// will reference a byte sequence which is either contained inside a
/// single allocated object or is zero sized. In either case, this means
/// that its size will fit in an `isize` and it will not wrap around the
/// address space.
#[doc(hidden)]
#[inline]
pub unsafe fn transmute_unchecked<U: ?Sized, V, F>(
Expand All @@ -464,25 +432,18 @@ mod _conversions {
) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
where
V: Validity,
F: FnOnce(PtrInner<'_, T>) -> NonNull<U>,
F: FnOnce(PtrInner<'a, T>) -> PtrInner<'a, U>,
{
// SAFETY: By invariant on `self`, `self.as_inner().as_non_null()`
// either references a zero-sized byte range, or else it references
// a byte range contained inside of a single allocated objection.
let ptr = cast(self.as_inner());

// SAFETY:
//
// Lemma 1: `ptr` has the same provenance as `self`. The caller
// promises that `cast` preserves provenance, and we call it with
// `self.as_inner().as_non_null()`.
// The following safety arguments rely on the fact that the caller
// promises that `cast` returns a `PtrInner` which addresses a
// prefix of the bytes of `*self`, and so properties that hold of
// `*self` also hold of `*ptr`.
//
// 0. By invariant, if `self`'s referent is not zero sized, then
// `self` has valid provenance for its entire referent, which is
// entirely contained in `A`. By Lemma 1, so does `ptr`.
// 1. By invariant on `self`, if `self`'s referent is not zero
// sized, then `A` is guaranteed to live for at least `'a`.
// 2. `ptr` conforms to the aliasing invariant of `I::Aliasing`:
// 0. `ptr` conforms to the aliasing invariant of `I::Aliasing`:
// - `Exclusive`: `self` is the only `Ptr` or reference which is
// permitted to read or modify the referent for the lifetime
// `'a`. Since we consume `self` by value, the returned pointer
Expand All @@ -499,10 +460,10 @@ mod _conversions {
// of `UnsafeCell`s is unsound, this must be impossible using
// `&T` and `&U`.
// - `Inaccessible`: There are no restrictions we need to uphold.
// 3. `ptr` trivially satisfies the alignment invariant `Unaligned`.
// 4. The caller promises that `ptr` conforms to the validity
// 1. `ptr` trivially satisfies the alignment invariant `Unaligned`.
// 2. The caller promises that `ptr` conforms to the validity
// invariant `V` with respect to its referent type, `U`.
unsafe { Ptr::new(ptr) }
unsafe { Ptr::from_inner(ptr) }
}
}

Expand Down Expand Up @@ -533,10 +494,7 @@ mod _conversions {
// and the returned `Ptr` permit the same set of bit patterns in
// their referents, and so neither can be used to violate the
// validity of the other.
let ptr = unsafe {
#[allow(clippy::as_conversions)]
self.transmute_unchecked(|ptr| ptr.as_non_null().cast::<crate::Unalign<T>>())
};
let ptr = unsafe { self.transmute_unchecked(PtrInner::cast_sized) };
ptr.bikeshed_recall_aligned()
}
}
Expand Down Expand Up @@ -911,7 +869,7 @@ mod _casts {
/// around the address space.
#[doc(hidden)]
#[inline]
pub unsafe fn cast_unsized_unchecked<U, F: FnOnce(PtrInner<'_, T>) -> NonNull<U>>(
pub unsafe fn cast_unsized_unchecked<U, F: FnOnce(PtrInner<'a, T>) -> PtrInner<'a, U>>(
self,
cast: F,
) -> Ptr<'a, U, (I::Aliasing, Unaligned, I::Validity)>
Expand Down Expand Up @@ -959,7 +917,7 @@ mod _casts {
where
T: MutationCompatible<U, I::Aliasing, I::Validity, I::Validity, R>,
U: 'a + ?Sized + CastableFrom<T, I::Validity, I::Validity>,
F: FnOnce(PtrInner<'_, T>) -> NonNull<U>,
F: FnOnce(PtrInner<'a, T>) -> PtrInner<'a, U>,
{
// SAFETY: Because `T: MutationCompatible<U, I::Aliasing, R>`, one
// of the following holds:
Expand All @@ -982,40 +940,18 @@ mod _casts {
{
/// Casts this pointer-to-initialized into a pointer-to-bytes.
#[allow(clippy::wrong_self_convention)]
pub(crate) fn as_bytes<R>(self) -> Ptr<'a, [u8], (I::Aliasing, Aligned, Valid)>
#[must_use]
#[inline]
pub fn as_bytes<R>(self) -> Ptr<'a, [u8], (I::Aliasing, Aligned, Valid)>
where
T: Read<I::Aliasing, R>,
I::Aliasing: Reference,
{
let bytes = match T::size_of_val_raw(self.as_inner().as_non_null()) {
Some(bytes) => bytes,
// SAFETY: `KnownLayout::size_of_val_raw` promises to always
// return `Some` so long as the resulting size fits in a
// `usize`. By invariant on `Ptr`, `self` refers to a range of
// bytes whose size fits in an `isize`, which implies that it
// also fits in a `usize`.
None => unsafe { core::hint::unreachable_unchecked() },
};

// SAFETY:
// - `slice_from_raw_parts_mut` and `.cast` both preserve the
// pointer's address, and `bytes` is the length of `p`, so the
// returned pointer addresses the same bytes as `p`
// - `slice_from_raw_parts_mut` and `.cast` both preserve provenance
let ptr: Ptr<'a, [u8], _> = unsafe {
self.cast_unsized(|p: PtrInner<'_, T>| {
let ptr = core::ptr::slice_from_raw_parts_mut(
p.as_non_null().cast::<u8>().as_ptr(),
bytes,
);
// SAFETY: `ptr` has the same address as `p`, which is
// non-null.
core::ptr::NonNull::new_unchecked(ptr)
})
};

let ptr = ptr.bikeshed_recall_aligned();
ptr.recall_validity::<_, (_, (_, _))>()
// SAFETY: `PtrInner::as_bytes` returns a pointer which addresses
// the same byte range as its argument, and which has the same
// provenance.
let ptr = unsafe { self.cast_unsized(PtrInner::as_bytes) };
ptr.bikeshed_recall_aligned().recall_validity::<Valid, (_, (_, _))>()
}
}

Expand Down Expand Up @@ -1234,7 +1170,7 @@ mod _casts {
// inner type `T`. A consequence of this guarantee is that it is
// possible to convert between `T` and `UnsafeCell<T>`.
#[allow(clippy::as_conversions)]
let ptr = unsafe { self.transmute_unchecked(|ptr| cast!(ptr).as_non_null()) };
let ptr = unsafe { self.transmute_unchecked(|ptr| cast!(ptr)) };

// SAFETY: `UnsafeCell<T>` has the same alignment as `T` [1],
// and so if `self` is guaranteed to be aligned, then so is the
Expand Down
8 changes: 3 additions & 5 deletions src/util/macro_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -516,8 +516,7 @@ where
// because we assert above that the size of `Dst` equal to the size of
// `Src`.
// - `p as *mut Dst` is a provenance-preserving cast
#[allow(clippy::as_conversions)]
let c_ptr = unsafe { src.cast_unsized(|ptr| ptr.as_non_null().cast::<Dst>()) };
let c_ptr = unsafe { src.cast_unsized(|p| cast!(p)) };

match c_ptr.try_into_valid() {
Ok(ptr) => Ok(ptr),
Expand All @@ -530,8 +529,7 @@ where
// `ptr`, because we assert above that the size of `Dst` is equal
// to the size of `Src`.
// - `p as *mut Src` is a provenance-preserving cast
#[allow(clippy::as_conversions)]
let ptr = unsafe { ptr.cast_unsized(|ptr| ptr.as_non_null().cast::<Src>()) };
let ptr = unsafe { ptr.cast_unsized(|p| cast!(p)) };
// SAFETY: `ptr` is `src`, and has the same alignment invariant.
let ptr = unsafe { ptr.assume_alignment::<I::Alignment>() };
// SAFETY: `ptr` is `src` and has the same validity invariant.
Expand Down Expand Up @@ -586,7 +584,7 @@ where
// ABI as `T`
let ptr: Ptr<'_, Dst, _> = unsafe {
ptr.cast_unsized(|ptr: crate::pointer::PtrInner<'_, mem::MaybeUninit<Dst>>| {
ptr.as_non_null().cast()
ptr.cast_sized()
})
};

Expand Down
23 changes: 11 additions & 12 deletions src/util/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,22 +677,21 @@ macro_rules! static_assert_dst_is_not_zst {
}}
}

/// # Safety
///
/// The caller must ensure that the cast does not grow the size of the referent.
/// Preserving or shrinking the size of the referent are both acceptable.
macro_rules! cast {
() => {
|p| {
// SAFETY: `NonNull::as_ptr` returns a non-null pointer, so the
// argument to `NonNull::new_unchecked` is also non-null.
#[allow(clippy::as_conversions, unused_unsafe)]
#[allow(clippy::undocumented_unsafe_blocks)] // Clippy false positive
return unsafe {
core::ptr::NonNull::new_unchecked(core::ptr::NonNull::as_ptr(p) as *mut _)
};
}
};
($p:expr) => {{
let ptr: crate::pointer::PtrInner<'_, _> = $p;
let ptr = ptr.as_non_null();
let ptr = cast!()(ptr);
let ptr = ptr.as_ptr();
#[allow(clippy::as_conversions)]
let ptr = ptr as *mut _;
#[allow(unused_unsafe)]
// SAFETY: `NonNull::as_ptr` returns a non-null pointer, so the argument
// to `NonNull::new_unchecked` is also non-null.
let ptr = unsafe { core::ptr::NonNull::new_unchecked(ptr) };
// SAFETY: The caller promises that the cast preserves or shrinks
// referent size. By invariant on `$p: PtrInner` (guaranteed by type
// annotation above), `$p` refers to a byte range entirely contained
Expand Down
Loading
Loading