Skip to content

Commit db34e90

Browse files
committed
[pointer] Add project_ptr_inner! helper
gherrit-pr-id: G57b42ad4ff767a765f9b4ac149aa1c19d3796711
1 parent 4e3e874 commit db34e90

9 files changed

Lines changed: 157 additions & 99 deletions

File tree

src/impls.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -444,15 +444,15 @@ mod atomics {
444444
// SAFETY: The caller promised that `$atomic` and
445445
// `$prim` have the same size. Thus, this cast preserves
446446
// address, referent size, and provenance.
447-
unsafe { cast!(a) }
447+
unsafe { cast!(a => _) }
448448
}
449449
}
450450
// SAFETY: See previous safety comment.
451451
unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic {
452452
#[inline]
453453
fn cast_from_raw(p: PtrInner<'_, $prim>) -> PtrInner<'_, $atomic> {
454454
// SAFETY: See previous safety comment.
455-
unsafe { cast!(p) }
455+
unsafe { cast!(p => _) }
456456
}
457457
}
458458
// SAFETY: The caller promised that `$atomic` and `$prim` have
@@ -467,15 +467,15 @@ mod atomics {
467467
#[inline]
468468
fn cast_from_raw(a: PtrInner<'_, $atomic>) -> PtrInner<'_, UnsafeCell<$prim>> {
469469
// SAFETY: See previous safety comment.
470-
unsafe { cast!(a) }
470+
unsafe { cast!(a => _) }
471471
}
472472
}
473473
// SAFETY: See previous safety comment.
474474
unsafe impl<$($tyvar)?> SizeEq<UnsafeCell<$prim>> for $atomic {
475475
#[inline]
476476
fn cast_from_raw(p: PtrInner<'_, UnsafeCell<$prim>>) -> PtrInner<'_, $atomic> {
477477
// SAFETY: See previous safety comment.
478-
unsafe { cast!(p) }
478+
unsafe { cast!(p => _) }
479479
}
480480
}
481481

@@ -813,7 +813,7 @@ const _: () = {
813813
//
814814
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit validity as
815815
// `T`
816-
unsafe impl<T> HasField<value, 0, { crate::ident_id!(value) }> for ManuallyDrop<T> {
816+
unsafe impl<T: ?Sized> HasField<value, 0, { crate::ident_id!(value) }> for ManuallyDrop<T> {
817817
type Type = T;
818818

819819
#[inline]
@@ -831,7 +831,7 @@ const _: () = {
831831
//
832832
// `ManuallyDrop<T>` is guaranteed to have the same layout and bit validity as
833833
// `T`
834-
unsafe { slf.cast() }
834+
unsafe { cast!(slf => _) }
835835
}
836836
}
837837
};

src/pointer/inner.rs

Lines changed: 118 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -170,31 +170,7 @@ impl<'a, T: ?Sized> PtrInner<'a, T> {
170170
where
171171
T: Sized,
172172
{
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) }
173+
self.cast(CastSized::new())
198174
}
199175

200176
/// Projects a field.
@@ -206,6 +182,123 @@ impl<'a, T: ?Sized> PtrInner<'a, T> {
206182
{
207183
<T as HasField<F, VARIANT_ID, FIELD_ID>>::project(self)
208184
}
185+
186+
pub fn cast<C: Cast<T>>(self, cast: C) -> PtrInner<'a, C::Dst> {
187+
let non_null = self.as_non_null();
188+
let raw = non_null.as_ptr();
189+
190+
// SAFETY: By invariant on `self`, `raw`'s referent is zero-sized or
191+
// lives in a single allocation.
192+
let projected_raw = unsafe { cast.cast(raw) };
193+
194+
// SAFETY: `self`'s referent lives at a `NonNull` address, and is either
195+
// zero-sized or lives in an allocation. In either case, it does not
196+
// wrap around the address space [1], and so none of the addresses
197+
// contained in it or one-past-the-end of it are null.
198+
//
199+
// By invariant on `C: Cast`, `C::cast` is a provenance-preserving cast
200+
// which preserves or shrinks the set of referent bytes, so
201+
// `projected_raw` references a subset of `self`'s referent, and so it
202+
// cannot be null.
203+
//
204+
// [1] https://doc.rust-lang.org/1.92.0/std/ptr/index.html#allocation
205+
let projected_non_null = unsafe { NonNull::new_unchecked(projected_raw) };
206+
207+
// SAFETY: As described in the preceding safety comment, `projected_raw`,
208+
// and thus `projected_non_null`, addresses a subset of `self`'s
209+
// referent. Thus, `projected_non_null` either:
210+
// - Addresses zero bytes or,
211+
// - Addresses a subset of the referent of `self`. In this case, `self`
212+
// has provenance for its referent, which lives in an allocation.
213+
// Since `projected_non_null` was constructed using a sequence of
214+
// provenance-preserving operations, it also has provenance for its
215+
// referent and that referent lives in an allocation. By invariant on
216+
// `self`, that allocation lives for `'a`.
217+
unsafe { PtrInner::new(projected_non_null) }
218+
}
219+
}
220+
221+
// TODO: Migrate other casts to this:
222+
// - `Ptr`'s various cast methods
223+
// - `SizeEq`
224+
225+
/// # Safety
226+
///
227+
/// `cast` must be a provenance-preserving cast which preserves or shrinks the
228+
/// set of referent bytes.
229+
pub unsafe trait Cast<Src: ?Sized> {
230+
type Dst: ?Sized;
231+
232+
/// # Safety
233+
///
234+
/// `src` must have provenance for its entire referent, which must be
235+
/// zero-sized or live in a single allocation.
236+
unsafe fn cast(self, src: *mut Src) -> *mut Self::Dst;
237+
}
238+
239+
#[allow(missing_debug_implementations)]
240+
pub struct FnCast<F>(F);
241+
242+
impl<F> FnCast<F> {
243+
pub const unsafe fn new(f: F) -> Self {
244+
Self(f)
245+
}
246+
}
247+
248+
unsafe impl<F, Src: ?Sized, Dst: ?Sized> Cast<Src> for FnCast<F>
249+
where
250+
F: Fn(*mut Src) -> *mut Dst,
251+
{
252+
type Dst = Dst;
253+
254+
unsafe fn cast(self, src: *mut Src) -> *mut Self::Dst {
255+
(self.0)(src)
256+
}
257+
}
258+
struct CastSized<Dst>(PhantomData<Dst>);
259+
260+
impl<Dst> Default for CastSized<Dst> {
261+
fn default() -> Self {
262+
Self(PhantomData)
263+
}
264+
}
265+
266+
impl<Dst> CastSized<Dst> {
267+
const fn new() -> Self {
268+
Self(PhantomData)
269+
}
270+
}
271+
272+
// SAFETY: By the `static_assert!`, `Dst` is no larger than `Src`,
273+
// and `<*mut Src>::cast` is a provenance-preserving cast.
274+
unsafe impl<Src, Dst> Cast<Src> for CastSized<Dst> {
275+
type Dst = Dst;
276+
277+
unsafe fn cast(self, src: *mut Src) -> *mut Self::Dst {
278+
static_assert!(Src, Dst => mem::size_of::<Src>() >= mem::size_of::<Dst>());
279+
src.cast::<Dst>()
280+
}
281+
}
282+
283+
/// Constructs a [`Cast`] which projects from a type to one of its fields.
284+
#[macro_export]
285+
#[doc(hidden)]
286+
macro_rules! project_cast {
287+
($src:ty => $field_name:tt) => {
288+
$crate::pointer::__project_cast::<$src, _, _>(|src| {
289+
$crate::util::macro_util::core_reexport::ptr::addr_of_mut!((*src).$field_name)
290+
})
291+
};
292+
}
293+
294+
#[doc(hidden)]
295+
pub const unsafe fn __project_cast<Src: ?Sized, Dst: ?Sized, F>(
296+
cast: F,
297+
) -> impl Cast<Src, Dst = Dst>
298+
where
299+
F: Fn(*mut Src) -> *mut Dst,
300+
{
301+
unsafe { FnCast::new(cast) }
209302
}
210303

211304
#[allow(clippy::needless_lifetimes)]

src/pointer/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ mod ptr;
1515
mod transmute;
1616

1717
#[doc(hidden)]
18-
pub use {inner::PtrInner, transmute::*};
18+
pub use {
19+
inner::{FnCast, PtrInner, __project_cast},
20+
transmute::*,
21+
};
1922
#[doc(hidden)]
2023
pub use {
2124
invariant::{BecauseExclusive, BecauseImmutable, Read},

src/pointer/ptr.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,9 @@ mod _conversions {
451451
V: Validity,
452452
F: FnOnce(PtrInner<'a, T>) -> PtrInner<'a, U>,
453453
{
454+
// TODO: Should this accept a `*mut -> *mut` rather than `PtrInner ->
455+
// PtrInner` and then use `PtrInner::cast_unchecked`? Would that allow
456+
// us to simplify downstream callers of `transmute_unchecked`?
454457
let ptr = cast(self.as_inner());
455458

456459
// SAFETY:
@@ -1188,9 +1191,7 @@ mod _casts {
11881191
// `UnsafeCell<T>` has the same in-memory representation as its
11891192
// inner type `T`. A consequence of this guarantee is that it is
11901193
// possible to convert between `T` and `UnsafeCell<T>`.
1191-
#[allow(clippy::as_conversions)]
1192-
#[allow(clippy::multiple_unsafe_ops_per_block)]
1193-
let ptr = unsafe { self.transmute_unchecked(|ptr| cast!(ptr)) };
1194+
let ptr = unsafe { self.transmute_unchecked(|ptr| cast!(ptr => _)) };
11941195

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

src/pointer/transmute.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -471,10 +471,7 @@ unsafe impl<T> SizeEq<T> for MaybeUninit<T> {
471471
fn cast_from_raw(t: PtrInner<'_, T>) -> PtrInner<'_, MaybeUninit<T>> {
472472
// SAFETY: Per preceding safety comment, `MaybeUninit<T>` and `T` have
473473
// the same size, and so this cast preserves referent size.
474-
#[allow(clippy::multiple_unsafe_ops_per_block)]
475-
unsafe {
476-
cast!(t)
477-
}
474+
unsafe { cast!(t => _) }
478475
}
479476
}
480477

@@ -484,10 +481,7 @@ unsafe impl<T> SizeEq<MaybeUninit<T>> for T {
484481
fn cast_from_raw(t: PtrInner<'_, MaybeUninit<T>>) -> PtrInner<'_, T> {
485482
// SAFETY: Per preceding safety comment, `MaybeUninit<T>` and `T` have
486483
// the same size, and so this cast preserves referent size.
487-
#[allow(clippy::multiple_unsafe_ops_per_block)]
488-
unsafe {
489-
cast!(t)
490-
}
484+
unsafe { cast!(t => _) }
491485
}
492486
}
493487

src/util/macro_util.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ where
635635
// `Src`.
636636
// - `p as *mut Dst` is a provenance-preserving cast
637637
#[allow(clippy::multiple_unsafe_ops_per_block)]
638-
let c_ptr = unsafe { src.cast_unsized(|p| cast!(p)) };
638+
let c_ptr = unsafe { src.cast_unsized(|p| cast!(p => _)) };
639639

640640
match c_ptr.try_into_valid() {
641641
Ok(ptr) => Ok(ptr),
@@ -649,7 +649,7 @@ where
649649
// to the size of `Src`.
650650
// - `p as *mut Src` is a provenance-preserving cast
651651
#[allow(clippy::multiple_unsafe_ops_per_block)]
652-
let ptr = unsafe { ptr.cast_unsized(|p| cast!(p)) };
652+
let ptr = unsafe { ptr.cast_unsized(|p| cast!(p => _)) };
653653
// SAFETY: `ptr` is `src`, and has the same alignment invariant.
654654
let ptr = unsafe { ptr.assume_alignment::<I::Alignment>() };
655655
// SAFETY: `ptr` is `src` and has the same validity invariant.

src/util/macros.rs

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -698,24 +698,15 @@ macro_rules! static_assert_dst_is_not_zst {
698698
/// The caller must ensure that the cast does not grow the size of the referent.
699699
/// Preserving or shrinking the size of the referent are both acceptable.
700700
macro_rules! cast {
701-
($p:expr) => {{
702-
let ptr: crate::pointer::PtrInner<'_, _> = $p;
703-
let ptr = ptr.as_non_null();
704-
let ptr = ptr.as_ptr();
705-
#[allow(clippy::as_conversions)]
706-
let ptr = ptr as *mut _;
707-
#[allow(unused_unsafe)]
708-
// SAFETY: `NonNull::as_ptr` returns a non-null pointer, so the argument
709-
// to `NonNull::new_unchecked` is also non-null.
710-
let ptr = unsafe { core::ptr::NonNull::new_unchecked(ptr) };
711-
// SAFETY: The caller promises that the cast preserves or shrinks
712-
// referent size. By invariant on `$p: PtrInner` (guaranteed by type
713-
// annotation above), `$p` refers to a byte range entirely contained
714-
// inside of a single allocation, has provenance for that whole byte
715-
// range, and will not outlive the allocation. All of these conditions
716-
// are preserved when preserving or shrinking referent size.
717-
crate::pointer::PtrInner::new(ptr)
718-
}};
701+
($dst:ty) => {
702+
$crate::pointer::FnCast::new(|src| {
703+
#[allow(clippy::as_conversions)]
704+
return src as *mut $dst;
705+
})
706+
};
707+
($src:expr => $dst:ty) => {
708+
$src.cast(cast!($dst))
709+
};
719710
}
720711

721712
/// Implements `TransmuteFrom` and `SizeEq` for `T` and `$wrapper<T>`.
@@ -741,15 +732,15 @@ macro_rules! unsafe_impl_for_transparent_wrapper {
741732
#[inline(always)]
742733
fn cast_from_raw(t: PtrInner<'_, T>) -> PtrInner<'_, $wrapper<T>> {
743734
// SAFETY: See previous safety comment.
744-
unsafe { cast!(t) }
735+
unsafe { cast!(t => $wrapper<T>) }
745736
}
746737
}
747738
// SAFETY: See previous safety comment.
748739
unsafe impl<T $(: ?$optbound)?> SizeEq<$wrapper<T>> for T {
749740
#[inline(always)]
750741
fn cast_from_raw(t: PtrInner<'_, $wrapper<T>>) -> PtrInner<'_, T> {
751742
// SAFETY: See previous safety comment.
752-
unsafe { cast!(t) }
743+
unsafe { cast!(t => _) }
753744
}
754745
}
755746
}};
@@ -815,7 +806,7 @@ macro_rules! impl_size_eq {
815806
// cast is guaranteed to preserve address and referent size.
816807
// It trivially preserves provenance.
817808
#[allow(clippy::multiple_unsafe_ops_per_block)]
818-
unsafe { cast!(t) }
809+
unsafe { cast!(t => _) }
819810
}
820811
}
821812
// SAFETY: See previous safety comment.
@@ -824,7 +815,7 @@ macro_rules! impl_size_eq {
824815
fn cast_from_raw(u: PtrInner<'_, $u>) -> PtrInner<'_, $t> {
825816
// SAFETY: See previous safety comment.
826817
#[allow(clippy::multiple_unsafe_ops_per_block)]
827-
unsafe { cast!(u) }
818+
unsafe { cast!(u => _) }
828819
}
829820
}
830821
};
@@ -895,12 +886,12 @@ macro_rules! unsafe_with_size_eq {
895886
// SAFETY: By the preceding safety comment, this cast preserves
896887
// referent size.
897888
#[allow(clippy::multiple_unsafe_ops_per_block)]
898-
let src: PtrInner<'_, T> = unsafe { cast!(src) };
889+
let src: PtrInner<'_, T> = unsafe { cast!(src => _) };
899890
let dst: PtrInner<'_, U> = crate::layout::cast_from_raw(src);
900891
// SAFETY: By the preceding safety comment, this cast preserves
901892
// referent size.
902893
#[allow(clippy::multiple_unsafe_ops_per_block)]
903-
unsafe { cast!(dst) }
894+
unsafe { cast!(dst => _) }
904895
}
905896
}
906897

@@ -915,7 +906,7 @@ macro_rules! unsafe_with_size_eq {
915906
#[allow(unused_unsafe)]
916907
// SAFETY: This call is never executed.
917908
#[allow(clippy::multiple_unsafe_ops_per_block)]
918-
let ptr = unsafe { cast!(ptr) };
909+
let ptr = unsafe { cast!(ptr => _) };
919910
let _ = <$dst<$u> as SizeEq<$src<$t>>>::cast_from_raw(ptr);
920911
}
921912

@@ -945,4 +936,5 @@ macro_rules! unsafe_with_size_eq {
945936
///
946937
/// Calling this function in a macro expansion ensures that the macro's caller
947938
/// must wrap the call in `unsafe { ... }`.
939+
#[inline(always)]
948940
pub(crate) const unsafe fn __unsafe() {}

zerocopy-derive/src/enum.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,8 @@ pub(crate) fn derive_is_bit_valid(
297297

298298
#[inline(always)]
299299
fn project(slf: #zerocopy_crate::PtrInner<'_, Self>) -> #zerocopy_crate::PtrInner<'_, Self::Type> {
300-
// SAFETY: By invariant on `___ZerocopyRawEnum`,
301-
// `___ZerocopyRawEnum` has the same layout as `Self`.
302-
let slf = unsafe { slf.cast::<___ZerocopyRawEnum #ty_generics>() };
303-
304-
slf.project::<_, 0, { #zerocopy_crate::ident_id!(variants) }>()
300+
slf.cast_sized::<___ZerocopyRawEnum #ty_generics>()
301+
.project::<_, 0, { #zerocopy_crate::ident_id!(variants) }>()
305302
.project::<_, 0, { #zerocopy_crate::ident_id!(#variants_union_field_ident) }>()
306303
.project::<_, 0, { #zerocopy_crate::ident_id!(value) }>()
307304
.project::<_, 0, { #zerocopy_crate::ident_id!(#variant_struct_field_index) }>()

0 commit comments

Comments
 (0)