99use proc_macro2:: TokenStream ;
1010use quote:: quote;
1111use 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
1516use 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+
165173fn 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 } )
0 commit comments