Skip to content

Commit b0b3dd7

Browse files
committed
[project] Support projecting enum tags
gherrit-pr-id: G06bb22c59364654d5c0b44cdcb9b70ad58fe8092
1 parent 1f5eea2 commit b0b3dd7

10 files changed

Lines changed: 238 additions & 10 deletions

File tree

src/impls.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,20 @@ const _: () = {
781781
{
782782
type Type = T;
783783

784+
type Tag = ();
785+
786+
// SAFETY: It is trivially sound to project any pointer to a pointer to
787+
// a type of size zero and alignment 1 (which `()` is [1]). Such a
788+
// pointer will trivially satisfy its aliasing and validity requirements
789+
// (since it has a zero-sized referent), and its alignment requirement
790+
// (since it is aligned to 1).
791+
//
792+
// [1] Per https://doc.rust-lang.org/1.92.0/reference/type-layout.html#r-layout.tuple.unit:
793+
//
794+
// [T]he unit tuple (`()`)... is guaranteed as a zero-sized type to
795+
// have a size of 0 and an alignment of 1.
796+
type ProjectToTag = crate::pointer::cast::CastToUnit;
797+
784798
#[inline]
785799
fn only_derive_is_allowed_to_implement_this_trait()
786800
where
@@ -1058,6 +1072,20 @@ mod tuples {
10581072

10591073
type Type = $CurrT;
10601074

1075+
type Tag = ();
1076+
1077+
// SAFETY: It is trivially sound to project any pointer to a
1078+
// pointer to a type of size zero and alignment 1 (which `()` is
1079+
// [1]). Such a pointer will trivially satisfy its aliasing and
1080+
// validity requirements (since it has a zero-sized referent),
1081+
// and its alignment requirement (since it is aligned to 1).
1082+
//
1083+
// [1] Per https://doc.rust-lang.org/1.92.0/reference/type-layout.html#r-layout.tuple.unit:
1084+
//
1085+
// [T]he unit tuple (`()`)... is guaranteed as a zero-sized
1086+
// type to have a size of 0 and an alignment of 1.
1087+
type ProjectToTag = crate::pointer::cast::CastToUnit;
1088+
10611089
#[inline(always)]
10621090
fn project(slf: crate::PtrInner<'_, Self>) -> *mut Self::Type {
10631091
let slf = slf.as_non_null().as_ptr();

src/lib.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,17 @@ pub unsafe trait HasField<Field, const VARIANT_ID: i128, const FIELD_ID: i128> {
11391139
/// The type of the field.
11401140
type Type: ?Sized;
11411141

1142+
/// The type's enum tag, or `()` for non-enum types.
1143+
type Tag: Immutable;
1144+
1145+
/// A pointer projection from `Self` to its tag.
1146+
///
1147+
/// # Safety
1148+
///
1149+
/// It must be the case that, for all `slf: Ptr<'_, Self, I>`, it is sound
1150+
/// to project from `slf` to `Ptr<'_, Self::Tag, I>` using this projection.
1151+
type ProjectToTag: pointer::cast::Project<Self, Self::Tag>;
1152+
11421153
/// Projects from `slf` to the field.
11431154
///
11441155
/// Users should generally not call `project` directly, and instead should
@@ -1185,6 +1196,18 @@ where
11851196
/// otherwise [`core::convert::Infallible`].
11861197
type Error;
11871198

1199+
#[must_use]
1200+
#[inline(always)]
1201+
fn project_tag<'a>(ptr: Ptr<'a, Self, I>) -> Ptr<'a, Self::Tag, I> {
1202+
// SAFETY: By invariant on `Self::ProjectToTag`, this is a sound
1203+
// projection.
1204+
let tag = unsafe { ptr.project_transmute_unchecked::<_, _, Self::ProjectToTag>() };
1205+
// SAFETY: By invariant on `Self::ProjectToTag`, the projected pointer
1206+
// has the same alignment as `ptr`.
1207+
let tag = unsafe { tag.assume_alignment() };
1208+
tag.unify_invariants()
1209+
}
1210+
11881211
/// Is the given field projectable from `ptr`?
11891212
///
11901213
/// If a field with [`Self::Invariants`] is projectable from the referent,
@@ -1194,7 +1217,7 @@ where
11941217
/// This method must be overriden if the field's projectability depends on
11951218
/// the value of the bytes in `ptr`.
11961219
#[inline(always)]
1197-
fn is_projectable<'a>(ptr: Ptr<'a, Self, I>) -> Result<Ptr<'a, Self, I>, Self::Error> {
1220+
fn is_projectable<'a>(_ptr: Ptr<'a, Self::Tag, I>) -> Result<(), Self::Error> {
11981221
trait IsInfallible {
11991222
const IS_INFALLIBLE: bool;
12001223
}
@@ -1248,7 +1271,7 @@ where
12481271
<Projection<Self, Field, I, VARIANT_ID, FIELD_ID> as IsInfallible>::IS_INFALLIBLE
12491272
);
12501273

1251-
Ok(ptr)
1274+
Ok(())
12521275
}
12531276
}
12541277

src/pointer/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,20 @@ pub mod cast {
347347

348348
// SAFETY: The `Project::project` impl preserves the set of referent bytes.
349349
unsafe impl<T: ?Sized + KnownLayout> CastExact<T, [u8]> for AsBytesCast {}
350+
351+
/// A cast from any type to `()`.
352+
#[allow(missing_copy_implementations, missing_debug_implementations)]
353+
pub struct CastToUnit;
354+
355+
// SAFETY: The `project` implementation projects to a subset of its
356+
// argument's referent using provenance-preserving operations.
357+
unsafe impl<T: ?Sized> Project<T, ()> for CastToUnit {
358+
#[inline(always)]
359+
fn project(src: PtrInner<'_, T>) -> *mut () {
360+
src.as_ptr().cast::<()>()
361+
}
362+
}
363+
364+
// SAFETY: The `project` implementation preserves referent address.
365+
unsafe impl<T: ?Sized> Cast<T, ()> for CastToUnit {}
350366
}

src/pointer/ptr.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -886,15 +886,16 @@ mod _casts {
886886

887887
#[inline(always)]
888888
pub fn project<F, const VARIANT_ID: i128, const FIELD_ID: i128>(
889-
self,
889+
mut self,
890890
) -> Result<Ptr<'a, T::Type, T::Invariants>, T::Error>
891891
where
892892
T: ProjectField<F, I, VARIANT_ID, FIELD_ID>,
893+
I::Aliasing: Reference,
893894
{
894895
use crate::pointer::cast::Projection;
895-
match T::is_projectable(self) {
896-
Ok(ptr) => {
897-
let inner = ptr.as_inner();
896+
match T::is_projectable(T::project_tag(self.reborrow())) {
897+
Ok(()) => {
898+
let inner = self.as_inner();
898899
let projected = inner.project::<_, Projection<F, VARIANT_ID, FIELD_ID>>();
899900
// SAFETY: By `T: ProjectField<F, I, VARIANT_ID, FIELD_ID>`,
900901
// for `self: Ptr<'_, T, I>` such that `T::is_projectable`

zerocopy-derive/src/derive/try_from_bytes.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,10 @@ pub(crate) fn derive_is_bit_valid(
247247
.inner_extras(quote! {
248248
type Type = #ty;
249249

250+
type Tag = ___ZerocopyTag;
251+
252+
type ProjectToTag = #zerocopy_crate::pointer::cast::CastSized;
253+
250254
#[inline(always)]
251255
fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type {
252256
use #zerocopy_crate::pointer::cast::{CastSized, Projection};
@@ -483,6 +487,10 @@ fn derive_has_field_struct_union(ctx: &Ctx, data: &dyn DataExt) -> TokenStream {
483487
.inner_extras(quote! {
484488
type Type = #ty;
485489

490+
type Tag = ();
491+
492+
type ProjectToTag = #zerocopy_crate::pointer::cast::CastToUnit;
493+
486494
#[inline(always)]
487495
fn project(slf: #zerocopy_crate::pointer::PtrInner<'_, Self>) -> *mut <Self as #has_field_path>::Type {
488496
let slf = slf.as_ptr();

zerocopy-derive/src/output_tests/expected/from_bytes_union.expected.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ const _: () = {
6767
> for Foo {
6868
fn only_derive_is_allowed_to_implement_this_trait() {}
6969
type Type = u8;
70+
type Tag = ();
71+
type ProjectToTag = ::zerocopy::pointer::cast::CastToUnit;
7072
#[inline(always)]
7173
fn project(
7274
slf: ::zerocopy::pointer::PtrInner<'_, Self>,

0 commit comments

Comments
 (0)