Skip to content

Commit c37397b

Browse files
committed
[pointer] Add generic Cast framework
Add `unsafe trait Cast`, which is implemented by any raw pointer cast. Use this to unify `PtrInner` and `Ptr` casts and field projections and `SizeEq` casts. gherrit-pr-id: G57b42ad4ff767a765f9b4ac149aa1c19d3796711
1 parent e28215f commit c37397b

File tree

11 files changed

+337
-274
lines changed

11 files changed

+337
-274
lines changed

src/impls.rs

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,7 @@ mod atomics {
426426
crate::util::macros::__unsafe();
427427

428428
use core::cell::UnsafeCell;
429-
use crate::pointer::{PtrInner, SizeEq, TransmuteFrom, invariant::Valid};
429+
use crate::pointer::{SizeEq, TransmuteFrom, invariant::Valid};
430430

431431
$(
432432
// SAFETY: The caller promised that `$atomic` and `$prim` have
@@ -439,21 +439,11 @@ mod atomics {
439439
// SAFETY: The caller promised that `$atomic` and `$prim` have
440440
// the same size.
441441
unsafe impl<$($tyvar)?> SizeEq<$atomic> for $prim {
442-
#[inline]
443-
fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, $prim> {
444-
// SAFETY: The caller promised that `$atomic` and
445-
// `$prim` have the same size. Thus, this cast preserves
446-
// address, referent size, and provenance.
447-
unsafe { cast!(a) }
448-
}
442+
type CastFrom = $crate::pointer::cast::CastSized;
449443
}
450444
// SAFETY: See previous safety comment.
451445
unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic {
452-
#[inline]
453-
fn cast_from_raw(p: PtrInner<'_, $prim>) -> PtrInner<'_, $atomic> {
454-
// SAFETY: See previous safety comment.
455-
unsafe { cast!(p) }
456-
}
446+
type CastFrom = $crate::pointer::cast::CastSized;
457447
}
458448
// SAFETY: The caller promised that `$atomic` and `$prim` have
459449
// the same size. `UnsafeCell<T>` has the same size as `T` [1].
@@ -464,19 +454,11 @@ mod atomics {
464454
// its inner type `T`. A consequence of this guarantee is that
465455
// it is possible to convert between `T` and `UnsafeCell<T>`.
466456
unsafe impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> {
467-
#[inline]
468-
fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, UnsafeCell<$prim>> {
469-
// SAFETY: See previous safety comment.
470-
unsafe { cast!(a) }
471-
}
457+
type CastFrom = $crate::pointer::cast::CastSized;
472458
}
473459
// SAFETY: See previous safety comment.
474460
unsafe impl<$($tyvar)?> SizeEq<UnsafeCell<$prim>> for $atomic {
475-
#[inline]
476-
fn cast_from_raw(p: PtrInner<'_, UnsafeCell<$prim>>) -> PtrInner<'_, $atomic> {
477-
// SAFETY: See previous safety comment.
478-
unsafe { cast!(p) }
479-
}
461+
type CastFrom = $crate::pointer::cast::CastSized;
480462
}
481463

482464
// SAFETY: The caller promised that `$atomic` and `$prim` have
@@ -818,7 +800,7 @@ const _: () = {
818800
// private field, and because it is the name it is referred to in the public
819801
// documentation of `ManuallyDrop::new`, `ManuallyDrop::into_inner`,
820802
// `ManuallyDrop::take` and `ManuallyDrop::drop`.
821-
unsafe impl<T> HasField<value, 0, { crate::ident_id!(value) }> for ManuallyDrop<T> {
803+
unsafe impl<T: ?Sized> HasField<value, 0, { crate::ident_id!(value) }> for ManuallyDrop<T> {
822804
type Type = T;
823805

824806
#[inline]
@@ -829,15 +811,15 @@ const _: () = {
829811
}
830812

831813
#[inline(always)]
832-
fn project(slf: PtrInner<'_, Self>) -> PtrInner<'_, T> {
814+
fn project(slf: *mut Self) -> *mut T {
833815
// SAFETY: `ManuallyDrop<T>` has the same layout and bit validity as
834816
// `T` [1].
835817
//
836818
// [1] Per https://doc.rust-lang.org/1.85.0/std/mem/struct.ManuallyDrop.html:
837819
//
838820
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit
839821
// validity as `T`
840-
unsafe { slf.cast() }
822+
slf as *mut T
841823
}
842824
}
843825
};

src/layout.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -738,10 +738,29 @@ impl DstLayout {
738738
}
739739
}
740740

741-
pub(crate) use cast_from_raw::cast_from_raw;
741+
pub(crate) use cast_from_raw::CastFromRaw;
742742
mod cast_from_raw {
743743
use crate::{pointer::PtrInner, *};
744744

745+
pub(crate) struct CastFromRaw<Dst: ?Sized>(PhantomData<Dst>);
746+
747+
impl<Dst: ?Sized> Default for CastFromRaw<Dst> {
748+
fn default() -> Self {
749+
Self(PhantomData)
750+
}
751+
}
752+
753+
unsafe impl<Src, Dst> crate::pointer::cast::Cast<Src, Dst> for CastFromRaw<Dst>
754+
where
755+
Src: KnownLayout<PointerMetadata = usize> + ?Sized,
756+
Dst: KnownLayout<PointerMetadata = usize> + ?Sized,
757+
{
758+
unsafe fn cast(self, src: *mut Src) -> *mut Dst {
759+
let src = unsafe { PtrInner::new(NonNull::new_unchecked(src)) };
760+
cast_from_raw(src).as_non_null().as_ptr()
761+
}
762+
}
763+
745764
/// Implements [`<Dst as SizeEq<Src>>::cast_from_raw`][cast_from_raw].
746765
///
747766
/// # PME

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,7 @@ pub unsafe trait HasField<Field, const VARIANT_ID: u128, const FIELD_ID: u128> {
11131113
type Type: ?Sized;
11141114

11151115
/// Projects from `slf` to the field.
1116-
fn project(slf: PtrInner<'_, Self>) -> PtrInner<'_, Self::Type>;
1116+
fn project(slf: *mut Self) -> *mut Self::Type;
11171117
}
11181118

11191119
/// Analyzes whether a type is [`FromZeros`].

src/pointer/inner.rs

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

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

1111
pub use _def::PtrInner;
1212

1313
#[allow(unused_imports)]
1414
use crate::util::polyfills::NumExt as _;
1515
use crate::{
1616
layout::{CastType, MetadataCastError},
17+
pointer::cast,
1718
util::AsAddress,
1819
AlignmentError, CastError, HasField, KnownLayout, MetadataOf, SizeError, SplitAt,
1920
};
@@ -170,31 +171,7 @@ impl<'a, T: ?Sized> PtrInner<'a, T> {
170171
where
171172
T: Sized,
172173
{
173-
static_assert!(T, U => mem::size_of::<T>() >= mem::size_of::<U>());
174-
// SAFETY: By the preceding assert, `U` is no larger than `T`, which is
175-
// the size of `self`'s referent.
176-
unsafe { self.cast() }
177-
}
178-
179-
/// # Safety
180-
///
181-
/// `U` must not be larger than the size of `self`'s referent.
182-
#[must_use]
183-
#[inline(always)]
184-
pub unsafe fn cast<U>(self) -> PtrInner<'a, U> {
185-
let ptr = self.as_non_null().cast::<U>();
186-
187-
// SAFETY: The caller promises that `U` is no larger than `self`'s
188-
// referent. Thus, `ptr` addresses a subset of the bytes addressed by
189-
// `self`.
190-
//
191-
// 0. By invariant on `self`, if `self`'s referent is not zero sized,
192-
// then `self` has valid provenance for its referent, which is
193-
// entirely contained in some Rust allocation, `A`. Thus, the same
194-
// holds of `ptr`.
195-
// 1. By invariant on `self`, if `self`'s referent is not zero sized,
196-
// then `A` is guaranteed to live for at least `'a`.
197-
unsafe { PtrInner::new(ptr) }
174+
self.cast(cast::CastSized)
198175
}
199176

200177
/// Projects a field.
@@ -204,7 +181,46 @@ impl<'a, T: ?Sized> PtrInner<'a, T> {
204181
where
205182
T: HasField<F, VARIANT_ID, FIELD_ID>,
206183
{
207-
<T as HasField<F, VARIANT_ID, FIELD_ID>>::project(self)
184+
let cast = unsafe {
185+
crate::pointer::cast::FnCast::new(<T as HasField<F, VARIANT_ID, FIELD_ID>>::project)
186+
};
187+
self.cast(cast)
188+
}
189+
190+
#[must_use]
191+
#[inline(always)]
192+
pub fn cast<U: ?Sized, C: cast::Cast<T, U>>(self, cast: C) -> PtrInner<'a, U> {
193+
let non_null = self.as_non_null();
194+
let raw = non_null.as_ptr();
195+
196+
// SAFETY: By invariant on `self`, `raw`'s referent is zero-sized or
197+
// lives in a single allocation.
198+
let projected_raw = unsafe { cast.cast(raw) };
199+
200+
// SAFETY: `self`'s referent lives at a `NonNull` address, and is either
201+
// zero-sized or lives in an allocation. In either case, it does not
202+
// wrap around the address space [1], and so none of the addresses
203+
// contained in it or one-past-the-end of it are null.
204+
//
205+
// By invariant on `C: Cast`, `C::cast` is a provenance-preserving cast
206+
// which preserves or shrinks the set of referent bytes, so
207+
// `projected_raw` references a subset of `self`'s referent, and so it
208+
// cannot be null.
209+
//
210+
// [1] https://doc.rust-lang.org/1.92.0/std/ptr/index.html#allocation
211+
let projected_non_null = unsafe { NonNull::new_unchecked(projected_raw) };
212+
213+
// SAFETY: As described in the preceding safety comment, `projected_raw`,
214+
// and thus `projected_non_null`, addresses a subset of `self`'s
215+
// referent. Thus, `projected_non_null` either:
216+
// - Addresses zero bytes or,
217+
// - Addresses a subset of the referent of `self`. In this case, `self`
218+
// has provenance for its referent, which lives in an allocation.
219+
// Since `projected_non_null` was constructed using a sequence of
220+
// provenance-preserving operations, it also has provenance for its
221+
// referent and that referent lives in an allocation. By invariant on
222+
// `self`, that allocation lives for `'a`.
223+
unsafe { PtrInner::new(projected_non_null) }
208224
}
209225
}
210226

@@ -262,37 +278,6 @@ where
262278
// zero sized, then `A` is guaranteed to live for at least `'a`.
263279
unsafe { PtrInner::new(raw) }
264280
}
265-
266-
pub(crate) fn as_bytes(self) -> PtrInner<'a, [u8]> {
267-
let ptr = self.as_non_null();
268-
let bytes = match T::size_of_val_raw(ptr) {
269-
Some(bytes) => bytes,
270-
// SAFETY: `KnownLayout::size_of_val_raw` promises to always
271-
// return `Some` so long as the resulting size fits in a
272-
// `usize`. By invariant on `PtrInner`, `self` refers to a range
273-
// of bytes whose size fits in an `isize`, which implies that it
274-
// also fits in a `usize`.
275-
None => unsafe { core::hint::unreachable_unchecked() },
276-
};
277-
278-
let ptr = core::ptr::slice_from_raw_parts_mut(ptr.cast::<u8>().as_ptr(), bytes);
279-
280-
// SAFETY: `ptr` has the same address as `ptr = self.as_non_null()`,
281-
// which is non-null by construction.
282-
let ptr = unsafe { NonNull::new_unchecked(ptr) };
283-
284-
// SAFETY: `ptr` points to `bytes` `u8`s starting at the same address as
285-
// `self`'s referent. Since `bytes` is the length of `self`'s referent,
286-
// `ptr` addresses the same byte range as `self`. Thus, by invariant on
287-
// `self` (as a `PtrInner`):
288-
//
289-
// 0. If `ptr`'s referent is not zero sized, then `ptr` has valid
290-
// provenance for its referent, which is entirely contained in some
291-
// Rust allocation, `A`.
292-
// 1. If `ptr`'s referent is not zero sized, `A` is guaranteed to live
293-
// for at least `'a`.
294-
unsafe { PtrInner::new(ptr) }
295-
}
296281
}
297282

298283
#[allow(clippy::needless_lifetimes)]

src/pointer/mod.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,141 @@ where
3838
{
3939
ptr.as_bytes::<BecauseImmutable>().as_ref().iter().all(|&byte| byte == 0)
4040
}
41+
42+
#[doc(hidden)]
43+
pub mod cast {
44+
use core::{marker::PhantomData, mem, ptr::NonNull};
45+
46+
use crate::{
47+
layout::{SizeInfo, TrailingSliceLayout},
48+
KnownLayout,
49+
};
50+
51+
/// # Safety
52+
///
53+
/// `cast` must be a provenance-preserving cast which preserves or shrinks the
54+
/// set of referent bytes.
55+
pub unsafe trait Cast<Src: ?Sized, Dst: ?Sized> {
56+
/// # Safety
57+
///
58+
/// `src` must have provenance for its entire referent, which must be
59+
/// zero-sized or live in a single allocation.
60+
unsafe fn cast(self, src: *mut Src) -> *mut Dst;
61+
}
62+
63+
#[derive(Default, Copy, Clone)]
64+
#[allow(missing_debug_implementations)]
65+
pub struct IdCast;
66+
67+
unsafe impl<T: ?Sized> Cast<T, T> for IdCast {
68+
unsafe fn cast(self, src: *mut T) -> *mut T {
69+
src
70+
}
71+
}
72+
73+
#[allow(missing_debug_implementations)]
74+
pub struct FnCast<F>(F);
75+
76+
impl<F> FnCast<F> {
77+
pub const unsafe fn new(f: F) -> Self {
78+
Self(f)
79+
}
80+
}
81+
82+
unsafe impl<F, Src: ?Sized, Dst: ?Sized> Cast<Src, Dst> for FnCast<F>
83+
where
84+
F: Fn(*mut Src) -> *mut Dst,
85+
{
86+
unsafe fn cast(self, src: *mut Src) -> *mut Dst {
87+
(self.0)(src)
88+
}
89+
}
90+
91+
#[derive(Default)]
92+
#[allow(missing_debug_implementations)]
93+
pub struct CastSized;
94+
95+
#[derive(Default)]
96+
#[allow(missing_debug_implementations)]
97+
pub struct CastUnsized;
98+
99+
unsafe impl<Src, Dst> Cast<Src, Dst> for CastUnsized
100+
where
101+
Src: ?Sized + KnownLayout,
102+
Dst: ?Sized + KnownLayout<PointerMetadata = Src::PointerMetadata>,
103+
{
104+
unsafe fn cast(self, src: *mut Src) -> *mut Dst {
105+
static_assert!(Src: ?Sized + KnownLayout, Dst: ?Sized + KnownLayout => {
106+
let t = <Src as KnownLayout>::LAYOUT;
107+
let u = <Dst as KnownLayout>::LAYOUT;
108+
t.align.get() >= u.align.get() && match (t.size_info, u.size_info) {
109+
(SizeInfo::Sized { size: t }, SizeInfo::Sized { size: u }) => t == u,
110+
(
111+
SizeInfo::SliceDst(TrailingSliceLayout { offset: t_offset, elem_size: t_elem_size }),
112+
SizeInfo::SliceDst(TrailingSliceLayout { offset: u_offset, elem_size: u_elem_size })
113+
) => t_offset == u_offset && t_elem_size == u_elem_size,
114+
_ => false,
115+
}
116+
});
117+
118+
let metadata = Src::pointer_to_metadata(src);
119+
// TODO: Need a non-null guarantee in order for this to be sound.
120+
let src = unsafe { NonNull::new_unchecked(src) };
121+
Dst::raw_from_ptr_len(src.cast::<u8>(), metadata).as_ptr()
122+
}
123+
}
124+
125+
// SAFETY: By the `static_assert!`, `Dst` is no larger than `Src`,
126+
// and `<*mut Src>::cast` is a provenance-preserving cast.
127+
unsafe impl<Src, Dst> Cast<Src, Dst> for CastSized {
128+
unsafe fn cast(self, src: *mut Src) -> *mut Dst {
129+
static_assert!(Src, Dst => mem::size_of::<Src>() >= mem::size_of::<Dst>());
130+
src.cast::<Dst>()
131+
}
132+
}
133+
134+
#[allow(missing_debug_implementations)]
135+
pub struct TransitiveCast<U: ?Sized, TU, UV> {
136+
tu: TU,
137+
uv: UV,
138+
_u: PhantomData<U>,
139+
}
140+
141+
impl<U: ?Sized, TU: Default, UV: Default> Default for TransitiveCast<U, TU, UV> {
142+
fn default() -> Self {
143+
Self { tu: Default::default(), uv: Default::default(), _u: PhantomData }
144+
}
145+
}
146+
147+
unsafe impl<T, U, V, TU, UV> Cast<T, V> for TransitiveCast<U, TU, UV>
148+
where
149+
T: ?Sized,
150+
U: ?Sized,
151+
V: ?Sized,
152+
TU: Cast<T, U>,
153+
UV: Cast<U, V>,
154+
{
155+
unsafe fn cast(self, t: *mut T) -> *mut V {
156+
let u = unsafe { self.tu.cast(t) };
157+
unsafe { self.uv.cast(u) }
158+
}
159+
}
160+
161+
pub(crate) struct AsBytesCast;
162+
163+
unsafe impl<T: ?Sized + KnownLayout> Cast<T, [u8]> for AsBytesCast {
164+
unsafe fn cast(self, src: *mut T) -> *mut [u8] {
165+
let bytes = match T::size_of_val_raw(src) {
166+
Some(bytes) => bytes,
167+
// SAFETY: `KnownLayout::size_of_val_raw` promises to always
168+
// return `Some` so long as the resulting size fits in a
169+
// `usize`. By invariant on `PtrInner`, `self` refers to a range
170+
// of bytes whose size fits in an `isize`, which implies that it
171+
// also fits in a `usize`.
172+
None => unsafe { core::hint::unreachable_unchecked() },
173+
};
174+
175+
core::ptr::slice_from_raw_parts_mut(src.cast::<u8>(), bytes)
176+
}
177+
}
178+
}

0 commit comments

Comments
 (0)