Skip to content

Commit a524fca

Browse files
committed
Add HasField::project; simplify is_bit_valid
This method projects from `PtrInner<_, Self>` to a `PtrInner` of a field of `Self`. In this commit, we use `HasField::project` to considerably simplify `is_bit_valid` implementations. gherrit-pr-id: G9e7039c715e1ec53e6d860909bb0d618898fd46b
1 parent d8e3bf8 commit a524fca

File tree

4 files changed

+1516
-345
lines changed

4 files changed

+1516
-345
lines changed

src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ use core::{
372372
use std::io;
373373

374374
use crate::pointer::invariant::{self, BecauseExclusive};
375+
#[doc(hidden)]
376+
pub use crate::pointer::PtrInner;
375377
pub use crate::{
376378
byte_slice::*,
377379
byteorder::*,
@@ -1109,6 +1111,9 @@ pub unsafe trait HasField<Field, const VARIANT_ID: u128, const FIELD_ID: u128> {
11091111

11101112
/// The type of the field.
11111113
type Type: ?Sized;
1114+
1115+
/// Projects from `slf` to the field.
1116+
fn project(slf: PtrInner<'_, Self>) -> PtrInner<'_, Self::Type>;
11121117
}
11131118

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

zerocopy-derive/src/enum.rs

Lines changed: 72 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
use proc_macro2::TokenStream;
1010
use quote::quote;
1111
use syn::{
12-
parse_quote, spanned::Spanned as _, DataEnum, DeriveInput, Error, Fields, Generics, Ident, Path,
12+
parse_quote, spanned::Spanned as _, DataEnum, DeriveInput, Error, Fields, Generics, Ident,
13+
Index, Path,
1314
};
1415

1516
use crate::{
@@ -162,6 +163,13 @@ fn generate_variant_structs(
162163
}
163164
}
164165

166+
fn variants_union_field_ident(ident: &Ident) -> Ident {
167+
let variant_ident_str = crate::ext::to_ident_str(ident);
168+
// Field names are prefixed with `__field_` to prevent name collision
169+
// with the `__nonempty` field.
170+
Ident::new(&format!("__field_{}", variant_ident_str), ident.span())
171+
}
172+
165173
fn generate_variants_union(generics: &Generics, data: &DataEnum) -> TokenStream {
166174
let (_, ty_generics, _) = generics.split_for_impl();
167175

@@ -172,10 +180,7 @@ fn generate_variants_union(generics: &Generics, data: &DataEnum) -> TokenStream
172180
return None;
173181
}
174182

175-
// Field names are prefixed with `__field_` to prevent name collision
176-
// with the `__nonempty` field.
177-
let field_name_str = crate::ext::to_ident_str(&variant.ident);
178-
let field_name = Ident::new(&format!("__field_{}", field_name_str), variant.ident.span());
183+
let field_name = variants_union_field_ident(&variant.ident);
179184
let variant_struct_ident = variant_struct_ident(&variant.ident);
180185

181186
Some(quote! {
@@ -248,16 +253,19 @@ pub(crate) fn derive_is_bit_valid(
248253
let variant_structs = generate_variant_structs(enum_ident, generics, data, zerocopy_crate);
249254
let variants_union = generate_variants_union(generics, data);
250255

251-
let (_, ty_generics, _) = generics.split_for_impl();
256+
let (_, ref ty_generics, _) = generics.split_for_impl();
252257

253258
let has_fields = data.variants().into_iter().flat_map(|(variant, fields)| {
254259
let variant_ident = &variant.unwrap().ident;
260+
let variant_struct_ident = variant_struct_ident(variant_ident);
261+
let variants_union_field_ident = variants_union_field_ident(variant_ident);
255262
let field: Box<syn::Type> = parse_quote!(());
256-
fields.into_iter().map(move |(vis, ident, ty)| {
263+
fields.into_iter().enumerate().map(move |(idx, (vis, ident, ty))| {
257264
// Rust does not presently support explicit visibility modifiers on
258265
// enum fields, but we guard against the possibility to ensure this
259266
// derive remains sound.
260267
assert!(matches!(vis, syn::Visibility::Inherited));
268+
let variant_struct_field_index = Index::from(idx + 1);
261269
ImplBlockBuilder::new(
262270
ast,
263271
data,
@@ -274,6 +282,63 @@ pub(crate) fn derive_is_bit_valid(
274282
)
275283
.inner_extras(quote! {
276284
type Type = #ty;
285+
286+
#[inline(always)]
287+
fn project(slf: #zerocopy_crate::PtrInner<'_, Self>) -> #zerocopy_crate::PtrInner<'_, Self::Type> {
288+
// SAFETY: By invariant on `___ZerocopyRawEnum`,
289+
// `___ZerocopyRawEnum` has the same layout as `Self`.
290+
let slf = unsafe { slf.cast::<___ZerocopyRawEnum #ty_generics>() };
291+
let slf = slf.as_non_null().as_ptr();
292+
293+
// Project into the variant. SAFETY: `PtrInner` promises it
294+
// references either a zero-sized byte range, or else will
295+
// reference a byte range that is entirely contained within
296+
// an allocated object. In either case, this guarantees that
297+
// `(*slf).variants.#variants_union_field_ident` is
298+
// in-bounds of `slf`, and `variant` will maintain these
299+
// same invariants for its referent type.
300+
let variant = unsafe {
301+
#zerocopy_crate::util::macro_util::core_reexport::ptr::addr_of_mut!((*slf).variants.#variants_union_field_ident)
302+
};
303+
304+
// Unwrap the variant from `ManuallyDrop`. This cast is
305+
// size-preserving; by invariant on `ManuallyDrop<T>`,
306+
// `ManuallyDrop<T>` and `T` have identical layouts and
307+
// maintains the referent invariants of `variant`.
308+
let variant = variant as *mut #variant_struct_ident #ty_generics;
309+
310+
// Project the field. SAFETY: `variant` promises it
311+
// references either a zero-sized byte range, or else will
312+
// reference a byte range that is entirely contained within
313+
// an allocated object. In either case, this guarantees that
314+
// `(*variant).#variant_struct_field_index` is in-bounds of
315+
// `variant`, and `field` will maintain these same
316+
// invariants for its referent type.
317+
let field = unsafe {
318+
#zerocopy_crate::util::macro_util::core_reexport::ptr::addr_of_mut!((*variant).#variant_struct_field_index)
319+
};
320+
321+
// SAFETY: `field` promises it references either a
322+
// zero-sized byte range, or else will reference a byte
323+
// range that is entirely contained within an allocated
324+
// object. In either case, this guarantees that
325+
// `(*variant).#variant_struct_field_index` is in-bounds of
326+
// `variant`, and does not wrap around the address space.
327+
// Consequently, `field` is non-null, and `ptr` inherits
328+
// these same invariants.
329+
let ptr = unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(field) };
330+
331+
// SAFETY:
332+
// 0. `ptr` addresses a subset of the bytes of `slf`, so by
333+
// invariant on `slf: PtrInner`, if `ptr`'s referent is
334+
// not zero sized, then `ptr` has valid provenance for
335+
// its referent, which is entirely contained in some Rust
336+
// allocation, `A`.
337+
// 1. By invariant on `slf: PtrInner`, if `ptr`'s referent
338+
// is not zero sized, `A` is guaranteed to live for at
339+
// least `'a`.
340+
unsafe { #zerocopy_crate::PtrInner::new(ptr) }
341+
}
277342
})
278343
.build()
279344
})

zerocopy-derive/src/lib.rs

Lines changed: 30 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,34 @@ fn derive_has_field_struct_union(
774774
)
775775
.inner_extras(quote! {
776776
type Type = #ty;
777+
778+
#[inline(always)]
779+
fn project(slf: #zerocopy_crate::PtrInner<'_, Self>) -> #zerocopy_crate::PtrInner<'_, Self::Type> {
780+
let slf = slf.as_non_null().as_ptr();
781+
// SAFETY: `PtrInner` promises it references either a zero-sized
782+
// byte range, or else will reference a byte range that is
783+
// entirely contained within an allocated object. In either
784+
// case, this guarantees that `(*slf).#ident` is in-bounds of
785+
// `slf`.
786+
let field = unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::addr_of_mut!((*slf).#ident) };
787+
// SAFETY: `PtrInner` promises it references either a zero-sized
788+
// byte range, or else will reference a byte range that is
789+
// entirely contained within an allocated object. In either
790+
// case, this guarantees that field projection will not wrap
791+
// around the address space, and so `field` will be non-null.
792+
let ptr = unsafe { #zerocopy_crate::util::macro_util::core_reexport::ptr::NonNull::new_unchecked(field) };
793+
// SAFETY:
794+
// 0. `ptr` addresses a subset of the bytes of
795+
// `slf`, so by invariant on `slf: PtrInner`,
796+
// if `ptr`'s referent is not zero sized,
797+
// then `ptr` has valid provenance for its
798+
// referent, which is entirely contained in
799+
// some Rust allocation, `A`.
800+
// 1. By invariant on `slf: PtrInner`, if
801+
// `ptr`'s referent is not zero sized, `A` is
802+
// guaranteed to live for at least `'a`.
803+
unsafe { #zerocopy_crate::PtrInner::new(ptr) }
804+
}
777805
})
778806
.build()
779807
});
@@ -816,6 +844,7 @@ fn derive_try_from_bytes_struct(
816844
use #zerocopy_crate::pointer::PtrInner;
817845

818846
true #(&& {
847+
let project = <Self as #zerocopy_crate::HasField<_, 0, { #zerocopy_crate::ident_id!(#field_names) }>>::project;
819848
// SAFETY:
820849
// - `project` is a field projection, and so it
821850
// addresses a subset of the bytes addressed by `slf`
@@ -824,31 +853,6 @@ fn derive_try_from_bytes_struct(
824853
// at the same byte ranges in the returned pointer's
825854
// referent as they do in `*slf`
826855
let field_candidate = unsafe {
827-
let project = |slf: PtrInner<'_, Self>| {
828-
let slf = slf.as_non_null().as_ptr();
829-
let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
830-
// SAFETY: `cast_unsized_unchecked` promises
831-
// that `slf` will either reference a zero-sized
832-
// byte range, or else will reference a byte
833-
// range that is entirely contained within an
834-
// allocated object. In either case, this
835-
// guarantees that field projection will not
836-
// wrap around the address space, and so `field`
837-
// will be non-null.
838-
let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(field) };
839-
// SAFETY:
840-
// 0. `ptr` addresses a subset of the bytes of
841-
// `slf`, so by invariant on `slf: PtrInner`,
842-
// if `ptr`'s referent is not zero sized,
843-
// then `ptr` has valid provenance for its
844-
// referent, which is entirely contained in
845-
// some Rust allocation, `A`.
846-
// 1. By invariant on `slf: PtrInner`, if
847-
// `ptr`'s referent is not zero sized, `A` is
848-
// guaranteed to live for at least `'a`.
849-
unsafe { PtrInner::new(ptr) }
850-
};
851-
852856
candidate.reborrow().cast_unsized_unchecked(project)
853857
};
854858

@@ -901,6 +905,7 @@ fn derive_try_from_bytes_union(
901905
use #zerocopy_crate::pointer::PtrInner;
902906

903907
false #(|| {
908+
let project = <Self as #zerocopy_crate::HasField<_, 0, { #zerocopy_crate::ident_id!(#field_names) }>>::project;
904909
// SAFETY:
905910
// - `project` is a field projection, and so it
906911
// addresses a subset of the bytes addressed by `slf`
@@ -910,31 +915,6 @@ fn derive_try_from_bytes_union(
910915
// returned pointer's referent contain any
911916
// `UnsafeCell`s
912917
let field_candidate = unsafe {
913-
let project = |slf: PtrInner<'_, Self>| {
914-
let slf = slf.as_non_null().as_ptr();
915-
let field = core_reexport::ptr::addr_of_mut!((*slf).#field_names);
916-
// SAFETY: `cast_unsized_unchecked` promises
917-
// that `slf` will either reference a zero-sized
918-
// byte range, or else will reference a byte
919-
// range that is entirely contained within an
920-
// allocated object. In either case, this
921-
// guarantees that field projection will not
922-
// wrap around the address space, and so `field`
923-
// will be non-null.
924-
let ptr = unsafe { core_reexport::ptr::NonNull::new_unchecked(field) };
925-
// SAFETY:
926-
// 0. `ptr` addresses a subset of the bytes of
927-
// `slf`, so by invariant on `slf: PtrInner`,
928-
// if `ptr`'s referent is not zero sized,
929-
// then `ptr` has valid provenance for its
930-
// referent, which is entirely contained in
931-
// some Rust allocation, `A`.
932-
// 1. By invariant on `slf: PtrInner`, if
933-
// `ptr`'s referent is not zero sized, `A` is
934-
// guaranteed to live for at least `'a`.
935-
unsafe { PtrInner::new(ptr) }
936-
};
937-
938918
candidate.reborrow().cast_unsized_unchecked(project)
939919
};
940920

0 commit comments

Comments
 (0)