Skip to content

Commit 71bc399

Browse files
joshlfjswrenn
andauthored
Simplify some Ptr operations (#2577)
Some raw pointer casts now have the signature `fn(PtrInner) -> PtrInner` instead of `fn(PtrInner) -> NonNull`. Simplify the implementations of some `Ptr` methods. gherrit-pr-id: I1945c8e554dd80a0475462d0dc5e0ae1eb2e9ac4 Co-authored-by: Jack Wrenn <[email protected]>
1 parent 94f4692 commit 71bc399

File tree

10 files changed

+295
-218
lines changed

10 files changed

+295
-218
lines changed

src/pointer/inner.rs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
// This file may not be copied, modified, or distributed except according to
77
// those terms.
88

9-
use core::{marker::PhantomData, ops::Range, ptr::NonNull};
9+
use core::{marker::PhantomData, mem, ops::Range, ptr::NonNull};
1010

1111
#[allow(unused_imports)]
1212
use crate::util::polyfills::NumExt as _;
@@ -89,7 +89,9 @@ mod _def {
8989
/// Rust allocation, `A`.
9090
/// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
9191
/// for at least `'a`.
92-
pub(crate) const unsafe fn new(ptr: NonNull<T>) -> PtrInner<'a, T> {
92+
#[inline(always)]
93+
#[must_use]
94+
pub const unsafe fn new(ptr: NonNull<T>) -> PtrInner<'a, T> {
9395
// SAFETY: The caller has promised to satisfy all safety invariants
9496
// of `PtrInner`.
9597
Self { ptr, _marker: PhantomData }
@@ -160,6 +162,39 @@ impl<'a, T: ?Sized> PtrInner<'a, T> {
160162
// single allocated object.
161163
unsafe { Self::new(ptr) }
162164
}
165+
166+
#[must_use]
167+
#[inline(always)]
168+
pub fn cast_sized<U>(self) -> PtrInner<'a, U>
169+
where
170+
T: Sized,
171+
{
172+
static_assert!(T, U => mem::size_of::<T>() >= mem::size_of::<U>());
173+
// SAFETY: By the preceding assert, `U` is no larger than `T`, which is
174+
// the size of `self`'s referent.
175+
unsafe { self.cast() }
176+
}
177+
178+
/// # Safety
179+
///
180+
/// `U` must not be larger than the size of `self`'s referent.
181+
#[must_use]
182+
#[inline(always)]
183+
pub unsafe fn cast<U>(self) -> PtrInner<'a, U> {
184+
let ptr = self.as_non_null().cast::<U>();
185+
186+
// SAFETY: The caller promises that `U` is no larger than `self`'s
187+
// referent. Thus, `ptr` addresses a subset of the bytes addressed by
188+
// `self`.
189+
//
190+
// 0. By invariant on `self`, if `self`'s referent is not zero sized,
191+
// then `self` has valid provenance for its referent, which is
192+
// entirely contained in some Rust allocation, `A`. Thus, the same
193+
// holds of `ptr`.
194+
// 1. By invariant on `self`, if `self`'s referent is not zero sized,
195+
// then `A` is guaranteed to live for at least `'a`.
196+
unsafe { PtrInner::new(ptr) }
197+
}
163198
}
164199

165200
#[allow(clippy::needless_lifetimes)]
@@ -216,6 +251,37 @@ where
216251
// zero sized, then `A` is guaranteed to live for at least `'a`.
217252
unsafe { PtrInner::new(raw) }
218253
}
254+
255+
pub(crate) fn as_bytes(self) -> PtrInner<'a, [u8]> {
256+
let ptr = self.as_non_null();
257+
let bytes = match T::size_of_val_raw(ptr) {
258+
Some(bytes) => bytes,
259+
// SAFETY: `KnownLayout::size_of_val_raw` promises to always
260+
// return `Some` so long as the resulting size fits in a
261+
// `usize`. By invariant on `PtrInner`, `self` refers to a range
262+
// of bytes whose size fits in an `isize`, which implies that it
263+
// also fits in a `usize`.
264+
None => unsafe { core::hint::unreachable_unchecked() },
265+
};
266+
267+
let ptr = core::ptr::slice_from_raw_parts_mut(ptr.cast::<u8>().as_ptr(), bytes);
268+
269+
// SAFETY: `ptr` has the same address as `ptr = self.as_non_null()`,
270+
// which is non-null by construction.
271+
let ptr = unsafe { NonNull::new_unchecked(ptr) };
272+
273+
// SAFETY: `ptr` points to `bytes` `u8`s starting at the same address as
274+
// `self`'s referent. Since `bytes` is the length of `self`'s referent,
275+
// `ptr` addresses the same byte range as `self`. Thus, by invariant on
276+
// `self` (as a `PtrInner`):
277+
//
278+
// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
279+
// provenance for its referent, which is entirely contained in some
280+
// Rust allocation, `A`.
281+
// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
282+
// for at least `'a`.
283+
unsafe { PtrInner::new(ptr) }
284+
}
219285
}
220286

221287
#[allow(clippy::needless_lifetimes)]

src/pointer/ptr.rs

Lines changed: 24 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
use core::{
1010
fmt::{Debug, Formatter},
1111
marker::PhantomData,
12-
ptr::NonNull,
1312
};
1413

1514
use crate::{
@@ -50,6 +49,7 @@ mod def {
5049
///
5150
/// `Ptr<'a, T>` is [covariant] in `'a` and invariant in `T`.
5251
///
52+
/// [`NonNull<T>`]: core::ptr::NonNull
5353
/// [covariant]: https://doc.rust-lang.org/reference/subtyping.html
5454
pub struct Ptr<'a, T, I>
5555
where
@@ -74,32 +74,6 @@ mod def {
7474
T: 'a + ?Sized,
7575
I: Invariants,
7676
{
77-
/// Constructs a `Ptr` from a [`NonNull`].
78-
///
79-
/// # Safety
80-
///
81-
/// The caller promises that:
82-
///
83-
/// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
84-
/// provenance for its referent, which is entirely contained in some
85-
/// Rust allocation, `A`.
86-
/// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
87-
/// for at least `'a`.
88-
/// 2. `ptr` conforms to the aliasing invariant of
89-
/// [`I::Aliasing`](invariant::Aliasing).
90-
/// 3. `ptr` conforms to the alignment invariant of
91-
/// [`I::Alignment`](invariant::Alignment).
92-
/// 4. `ptr` conforms to the validity invariant of
93-
/// [`I::Validity`](invariant::Validity).
94-
pub(super) unsafe fn new(ptr: NonNull<T>) -> Ptr<'a, T, I> {
95-
// SAFETY: The caller has promised (in 0 - 1) to satisfy all safety
96-
// invariants of `PtrInner::new`.
97-
let ptr = unsafe { PtrInner::new(ptr) };
98-
// SAFETY: The caller has promised (in 2 - 4) to satisfy all safety
99-
// invariants of `Ptr`.
100-
Self { ptr, _invariants: PhantomData }
101-
}
102-
10377
/// Constructs a new `Ptr` from a [`PtrInner`].
10478
///
10579
/// # Safety
@@ -402,7 +376,7 @@ mod _conversions {
402376
// operate on these references simultaneously
403377
// - By `U: TransmuteFromPtr<T, I::Aliasing, I::Validity, V>`, it is
404378
// sound to perform this transmute.
405-
unsafe { self.transmute_unchecked(|ptr| SizeEq::cast_from_raw(ptr).as_non_null()) }
379+
unsafe { self.transmute_unchecked(SizeEq::cast_from_raw) }
406380
}
407381

408382
#[doc(hidden)]
@@ -420,7 +394,7 @@ mod _conversions {
420394
// referent simultaneously
421395
// - By `T: TransmuteFromPtr<T, I::Aliasing, I::Validity, V>`, it is
422396
// sound to perform this transmute.
423-
let ptr = unsafe { self.transmute_unchecked(|t| t.as_non_null()) };
397+
let ptr = unsafe { self.transmute_unchecked(SizeEq::cast_from_raw) };
424398
// SAFETY: `self` and `ptr` have the same address and referent type.
425399
// Therefore, if `self` satisfies `I::Alignment`, then so does
426400
// `ptr`.
@@ -450,12 +424,6 @@ mod _conversions {
450424
/// `I::Aliasing`, `I::Validity`, and `V`, and may depend upon the
451425
/// presence, absence, or specific location of `UnsafeCell`s in `T`
452426
/// and/or `U`. See [`Validity`] for more details.
453-
///
454-
/// `transmute_unchecked` guarantees that the pointer passed to `cast`
455-
/// will reference a byte sequence which is either contained inside a
456-
/// single allocated object or is zero sized. In either case, this means
457-
/// that its size will fit in an `isize` and it will not wrap around the
458-
/// address space.
459427
#[doc(hidden)]
460428
#[inline]
461429
pub unsafe fn transmute_unchecked<U: ?Sized, V, F>(
@@ -464,25 +432,18 @@ mod _conversions {
464432
) -> Ptr<'a, U, (I::Aliasing, Unaligned, V)>
465433
where
466434
V: Validity,
467-
F: FnOnce(PtrInner<'_, T>) -> NonNull<U>,
435+
F: FnOnce(PtrInner<'a, T>) -> PtrInner<'a, U>,
468436
{
469-
// SAFETY: By invariant on `self`, `self.as_inner().as_non_null()`
470-
// either references a zero-sized byte range, or else it references
471-
// a byte range contained inside of a single allocated objection.
472437
let ptr = cast(self.as_inner());
473438

474439
// SAFETY:
475440
//
476-
// Lemma 1: `ptr` has the same provenance as `self`. The caller
477-
// promises that `cast` preserves provenance, and we call it with
478-
// `self.as_inner().as_non_null()`.
441+
// The following safety arguments rely on the fact that the caller
442+
// promises that `cast` returns a `PtrInner` which addresses a
443+
// prefix of the bytes of `*self`, and so properties that hold of
444+
// `*self` also hold of `*ptr`.
479445
//
480-
// 0. By invariant, if `self`'s referent is not zero sized, then
481-
// `self` has valid provenance for its entire referent, which is
482-
// entirely contained in `A`. By Lemma 1, so does `ptr`.
483-
// 1. By invariant on `self`, if `self`'s referent is not zero
484-
// sized, then `A` is guaranteed to live for at least `'a`.
485-
// 2. `ptr` conforms to the aliasing invariant of `I::Aliasing`:
446+
// 0. `ptr` conforms to the aliasing invariant of `I::Aliasing`:
486447
// - `Exclusive`: `self` is the only `Ptr` or reference which is
487448
// permitted to read or modify the referent for the lifetime
488449
// `'a`. Since we consume `self` by value, the returned pointer
@@ -499,10 +460,10 @@ mod _conversions {
499460
// of `UnsafeCell`s is unsound, this must be impossible using
500461
// `&T` and `&U`.
501462
// - `Inaccessible`: There are no restrictions we need to uphold.
502-
// 3. `ptr` trivially satisfies the alignment invariant `Unaligned`.
503-
// 4. The caller promises that `ptr` conforms to the validity
463+
// 1. `ptr` trivially satisfies the alignment invariant `Unaligned`.
464+
// 2. The caller promises that `ptr` conforms to the validity
504465
// invariant `V` with respect to its referent type, `U`.
505-
unsafe { Ptr::new(ptr) }
466+
unsafe { Ptr::from_inner(ptr) }
506467
}
507468
}
508469

@@ -533,10 +494,7 @@ mod _conversions {
533494
// and the returned `Ptr` permit the same set of bit patterns in
534495
// their referents, and so neither can be used to violate the
535496
// validity of the other.
536-
let ptr = unsafe {
537-
#[allow(clippy::as_conversions)]
538-
self.transmute_unchecked(|ptr| ptr.as_non_null().cast::<crate::Unalign<T>>())
539-
};
497+
let ptr = unsafe { self.transmute_unchecked(PtrInner::cast_sized) };
540498
ptr.bikeshed_recall_aligned()
541499
}
542500
}
@@ -911,7 +869,7 @@ mod _casts {
911869
/// around the address space.
912870
#[doc(hidden)]
913871
#[inline]
914-
pub unsafe fn cast_unsized_unchecked<U, F: FnOnce(PtrInner<'_, T>) -> NonNull<U>>(
872+
pub unsafe fn cast_unsized_unchecked<U, F: FnOnce(PtrInner<'a, T>) -> PtrInner<'a, U>>(
915873
self,
916874
cast: F,
917875
) -> Ptr<'a, U, (I::Aliasing, Unaligned, I::Validity)>
@@ -959,7 +917,7 @@ mod _casts {
959917
where
960918
T: MutationCompatible<U, I::Aliasing, I::Validity, I::Validity, R>,
961919
U: 'a + ?Sized + CastableFrom<T, I::Validity, I::Validity>,
962-
F: FnOnce(PtrInner<'_, T>) -> NonNull<U>,
920+
F: FnOnce(PtrInner<'a, T>) -> PtrInner<'a, U>,
963921
{
964922
// SAFETY: Because `T: MutationCompatible<U, I::Aliasing, R>`, one
965923
// of the following holds:
@@ -982,40 +940,18 @@ mod _casts {
982940
{
983941
/// Casts this pointer-to-initialized into a pointer-to-bytes.
984942
#[allow(clippy::wrong_self_convention)]
985-
pub(crate) fn as_bytes<R>(self) -> Ptr<'a, [u8], (I::Aliasing, Aligned, Valid)>
943+
#[must_use]
944+
#[inline]
945+
pub fn as_bytes<R>(self) -> Ptr<'a, [u8], (I::Aliasing, Aligned, Valid)>
986946
where
987947
T: Read<I::Aliasing, R>,
988948
I::Aliasing: Reference,
989949
{
990-
let bytes = match T::size_of_val_raw(self.as_inner().as_non_null()) {
991-
Some(bytes) => bytes,
992-
// SAFETY: `KnownLayout::size_of_val_raw` promises to always
993-
// return `Some` so long as the resulting size fits in a
994-
// `usize`. By invariant on `Ptr`, `self` refers to a range of
995-
// bytes whose size fits in an `isize`, which implies that it
996-
// also fits in a `usize`.
997-
None => unsafe { core::hint::unreachable_unchecked() },
998-
};
999-
1000-
// SAFETY:
1001-
// - `slice_from_raw_parts_mut` and `.cast` both preserve the
1002-
// pointer's address, and `bytes` is the length of `p`, so the
1003-
// returned pointer addresses the same bytes as `p`
1004-
// - `slice_from_raw_parts_mut` and `.cast` both preserve provenance
1005-
let ptr: Ptr<'a, [u8], _> = unsafe {
1006-
self.cast_unsized(|p: PtrInner<'_, T>| {
1007-
let ptr = core::ptr::slice_from_raw_parts_mut(
1008-
p.as_non_null().cast::<u8>().as_ptr(),
1009-
bytes,
1010-
);
1011-
// SAFETY: `ptr` has the same address as `p`, which is
1012-
// non-null.
1013-
core::ptr::NonNull::new_unchecked(ptr)
1014-
})
1015-
};
1016-
1017-
let ptr = ptr.bikeshed_recall_aligned();
1018-
ptr.recall_validity::<_, (_, (_, _))>()
950+
// SAFETY: `PtrInner::as_bytes` returns a pointer which addresses
951+
// the same byte range as its argument, and which has the same
952+
// provenance.
953+
let ptr = unsafe { self.cast_unsized(PtrInner::as_bytes) };
954+
ptr.bikeshed_recall_aligned().recall_validity::<Valid, (_, (_, _))>()
1019955
}
1020956
}
1021957

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

12391175
// SAFETY: `UnsafeCell<T>` has the same alignment as `T` [1],
12401176
// and so if `self` is guaranteed to be aligned, then so is the

src/util/macro_util.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,7 @@ where
516516
// because we assert above that the size of `Dst` equal to the size of
517517
// `Src`.
518518
// - `p as *mut Dst` is a provenance-preserving cast
519-
#[allow(clippy::as_conversions)]
520-
let c_ptr = unsafe { src.cast_unsized(|ptr| ptr.as_non_null().cast::<Dst>()) };
519+
let c_ptr = unsafe { src.cast_unsized(|p| cast!(p)) };
521520

522521
match c_ptr.try_into_valid() {
523522
Ok(ptr) => Ok(ptr),
@@ -530,8 +529,7 @@ where
530529
// `ptr`, because we assert above that the size of `Dst` is equal
531530
// to the size of `Src`.
532531
// - `p as *mut Src` is a provenance-preserving cast
533-
#[allow(clippy::as_conversions)]
534-
let ptr = unsafe { ptr.cast_unsized(|ptr| ptr.as_non_null().cast::<Src>()) };
532+
let ptr = unsafe { ptr.cast_unsized(|p| cast!(p)) };
535533
// SAFETY: `ptr` is `src`, and has the same alignment invariant.
536534
let ptr = unsafe { ptr.assume_alignment::<I::Alignment>() };
537535
// SAFETY: `ptr` is `src` and has the same validity invariant.
@@ -586,7 +584,7 @@ where
586584
// ABI as `T`
587585
let ptr: Ptr<'_, Dst, _> = unsafe {
588586
ptr.cast_unsized(|ptr: crate::pointer::PtrInner<'_, mem::MaybeUninit<Dst>>| {
589-
ptr.as_non_null().cast()
587+
ptr.cast_sized()
590588
})
591589
};
592590

src/util/macros.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -677,22 +677,21 @@ macro_rules! static_assert_dst_is_not_zst {
677677
}}
678678
}
679679

680+
/// # Safety
681+
///
682+
/// The caller must ensure that the cast does not grow the size of the referent.
683+
/// Preserving or shrinking the size of the referent are both acceptable.
680684
macro_rules! cast {
681-
() => {
682-
|p| {
683-
// SAFETY: `NonNull::as_ptr` returns a non-null pointer, so the
684-
// argument to `NonNull::new_unchecked` is also non-null.
685-
#[allow(clippy::as_conversions, unused_unsafe)]
686-
#[allow(clippy::undocumented_unsafe_blocks)] // Clippy false positive
687-
return unsafe {
688-
core::ptr::NonNull::new_unchecked(core::ptr::NonNull::as_ptr(p) as *mut _)
689-
};
690-
}
691-
};
692685
($p:expr) => {{
693686
let ptr: crate::pointer::PtrInner<'_, _> = $p;
694687
let ptr = ptr.as_non_null();
695-
let ptr = cast!()(ptr);
688+
let ptr = ptr.as_ptr();
689+
#[allow(clippy::as_conversions)]
690+
let ptr = ptr as *mut _;
691+
#[allow(unused_unsafe)]
692+
// SAFETY: `NonNull::as_ptr` returns a non-null pointer, so the argument
693+
// to `NonNull::new_unchecked` is also non-null.
694+
let ptr = unsafe { core::ptr::NonNull::new_unchecked(ptr) };
696695
// SAFETY: The caller promises that the cast preserves or shrinks
697696
// referent size. By invariant on `$p: PtrInner` (guaranteed by type
698697
// annotation above), `$p` refers to a byte range entirely contained

0 commit comments

Comments
 (0)