@@ -41,11 +41,12 @@ mod ext;
4141mod output_tests;
4242mod repr;
4343
44- use proc_macro2:: { Span , TokenStream , TokenTree } ;
44+ use proc_macro2:: { Span , TokenStream } ;
4545use quote:: { quote, ToTokens } ;
4646use syn:: {
47- parse_quote, Attribute , Data , DataEnum , DataStruct , DataUnion , DeriveInput , Error , Expr ,
48- ExprLit , ExprUnary , GenericParam , Ident , Lit , Meta , Path , Type , UnOp , WherePredicate ,
47+ parse_quote, spanned:: Spanned as _, Attribute , Data , DataEnum , DataStruct , DataUnion ,
48+ DeriveInput , Error , Expr , ExprLit , ExprUnary , GenericParam , Ident , Lit , Meta , Path , Type , UnOp ,
49+ WherePredicate ,
4950} ;
5051
5152use crate :: { ext:: * , repr:: * } ;
@@ -464,11 +465,12 @@ fn derive_known_layout_inner(
464465
465466 Ok ( match & ast. data {
466467 Data :: Struct ( strct) => {
467- let require_trait_bound_on_field_types = if self_bounds == SelfBounds :: SIZED {
468- FieldBounds :: None
469- } else {
470- FieldBounds :: TRAILING_SELF
471- } ;
468+ let require_trait_bound_on_field_types =
469+ if matches ! ( self_bounds, SelfBounds :: All ( & [ Trait :: Sized ] ) ) {
470+ FieldBounds :: None
471+ } else {
472+ FieldBounds :: TRAILING_SELF
473+ } ;
472474
473475 // A bound on the trailing field is required, since structs are
474476 // unsized if their trailing field is unsized. Reflecting the layout
@@ -737,6 +739,51 @@ fn derive_split_at_inner(
737739 . build ( ) )
738740}
739741
742+ fn derive_has_field_variant (
743+ ast : & DeriveInput ,
744+ data : & dyn DataExt ,
745+ zerocopy_crate : & Path ,
746+ ) -> TokenStream {
747+ let fields = ast. data . fields ( ) ;
748+
749+ if fields. is_empty ( ) {
750+ return quote ! { } ;
751+ }
752+
753+ let field_tokens = fields. iter ( ) . map ( |( vis, ident, _) | {
754+ let ident = Ident :: new ( & format ! ( "ẕ{ident}" ) , ident. span ( ) ) ;
755+ quote ! (
756+ #vis enum #ident { }
757+ )
758+ } ) ;
759+
760+ let has_fields = fields. iter ( ) . map ( |( _, ident, ty) | {
761+ let field_token = Ident :: new ( & format ! ( "ẕ{ident}" ) , ident. span ( ) ) ;
762+ ImplBlockBuilder :: new (
763+ ast,
764+ data,
765+ Trait :: HasField {
766+ field : parse_quote ! ( #field_token) ,
767+ field_id : parse_quote ! ( { #zerocopy_crate:: ident_id!( #ident) } ) ,
768+ } ,
769+ FieldBounds :: None ,
770+ zerocopy_crate,
771+ )
772+ . inner_extras ( quote ! {
773+ type Type = #ty;
774+ } )
775+ . build ( )
776+ } ) ;
777+
778+ quote ! {
779+ #[ allow( non_camel_case_types) ]
780+ const _: ( ) = {
781+ #( #field_tokens) *
782+ #( #has_fields) *
783+ } ;
784+ }
785+ }
786+
740787/// A struct is `TryFromBytes` if:
741788/// - all fields are `TryFromBytes`
742789fn derive_try_from_bytes_struct (
@@ -815,6 +862,7 @@ fn derive_try_from_bytes_struct(
815862 zerocopy_crate,
816863 )
817864 . inner_extras ( extras)
865+ . outer_extras ( derive_has_field_variant ( ast, strct, zerocopy_crate) )
818866 . build ( ) )
819867}
820868
@@ -894,6 +942,7 @@ fn derive_try_from_bytes_union(
894942 } ) ;
895943 ImplBlockBuilder :: new ( ast, unn, Trait :: TryFromBytes , field_type_trait_bounds, zerocopy_crate)
896944 . inner_extras ( extras)
945+ . outer_extras ( derive_has_field_variant ( ast, unn, zerocopy_crate) )
897946 . build ( )
898947}
899948
@@ -964,7 +1013,7 @@ fn try_gen_trivial_is_bit_valid(
9641013 // make this no longer true. To hedge against these, we include an explicit
9651014 // `Self: FromBytes` check in the generated `is_bit_valid`, which is
9661015 // bulletproof.
967- if top_level == Trait :: FromBytes && ast. generics . params . is_empty ( ) {
1016+ if matches ! ( top_level, Trait :: FromBytes ) && ast. generics . params . is_empty ( ) {
9681017 Some ( quote ! (
9691018 // SAFETY: See inline.
9701019 fn is_bit_valid<___ZerocopyAliasing>(
@@ -1533,9 +1582,10 @@ impl PaddingCheck {
15331582 }
15341583}
15351584
1536- #[ derive( Copy , Clone , Debug , Eq , PartialEq ) ]
1585+ #[ derive( Clone ) ]
15371586enum Trait {
15381587 KnownLayout ,
1588+ HasField { field : Type , field_id : Expr } ,
15391589 Immutable ,
15401590 TryFromBytes ,
15411591 FromZeros ,
@@ -1560,6 +1610,7 @@ impl ToTokens for Trait {
15601610 // [1] https://doc.rust-lang.org/1.81.0/std/fmt/trait.Debug.html#stability
15611611 // [2] https://doc.rust-lang.org/beta/unstable-book/compiler-flags/fmt-debug.html
15621612 let s = match self {
1613+ Trait :: HasField { .. } => "HasField" ,
15631614 Trait :: KnownLayout => "KnownLayout" ,
15641615 Trait :: Immutable => "Immutable" ,
15651616 Trait :: TryFromBytes => "TryFromBytes" ,
@@ -1573,7 +1624,25 @@ impl ToTokens for Trait {
15731624 Trait :: SplitAt => "SplitAt" ,
15741625 } ;
15751626 let ident = Ident :: new ( s, Span :: call_site ( ) ) ;
1576- tokens. extend ( core:: iter:: once ( TokenTree :: Ident ( ident) ) ) ;
1627+ let empty_args: syn:: AngleBracketedGenericArguments = parse_quote ! ( <>) ;
1628+ let arguments: syn:: AngleBracketedGenericArguments = match self {
1629+ Trait :: HasField { field, field_id } => {
1630+ parse_quote ! ( <#field, #field_id>)
1631+ }
1632+ Trait :: KnownLayout => empty_args,
1633+ Trait :: Immutable => empty_args,
1634+ Trait :: TryFromBytes => empty_args,
1635+ Trait :: FromZeros => empty_args,
1636+ Trait :: FromBytes => empty_args,
1637+ Trait :: IntoBytes => empty_args,
1638+ Trait :: Unaligned => empty_args,
1639+ Trait :: Sized => empty_args,
1640+ Trait :: ByteHash => empty_args,
1641+ Trait :: ByteEq => empty_args,
1642+ Trait :: SplitAt => empty_args,
1643+ } ;
1644+
1645+ tokens. extend ( quote ! ( #ident #arguments) ) ;
15771646 }
15781647}
15791648
@@ -1588,7 +1657,6 @@ impl Trait {
15881657 }
15891658}
15901659
1591- #[ derive( Debug , Eq , PartialEq ) ]
15921660enum TraitBound {
15931661 Slf ,
15941662 Other ( Trait ) ,
@@ -1606,7 +1674,6 @@ impl<'a> FieldBounds<'a> {
16061674 const TRAILING_SELF : FieldBounds < ' a > = FieldBounds :: Trailing ( & [ TraitBound :: Slf ] ) ;
16071675}
16081676
1609- #[ derive( Debug , Eq , PartialEq ) ]
16101677enum SelfBounds < ' a > {
16111678 None ,
16121679 All ( & ' a [ Trait ] ) ,
@@ -1620,16 +1687,19 @@ impl<'a> SelfBounds<'a> {
16201687}
16211688
16221689/// Normalizes a slice of bounds by replacing [`TraitBound::Slf`] with `slf`.
1623- fn normalize_bounds ( slf : Trait , bounds : & [ TraitBound ] ) -> impl ' _ + Iterator < Item = Trait > {
1690+ fn normalize_bounds < ' a > (
1691+ slf : & ' a Trait ,
1692+ bounds : & ' a [ TraitBound ] ,
1693+ ) -> impl ' a + Iterator < Item = Trait > {
16241694 bounds. iter ( ) . map ( move |bound| match bound {
1625- TraitBound :: Slf => slf,
1626- TraitBound :: Other ( trt) => * trt,
1695+ TraitBound :: Slf => slf. clone ( ) ,
1696+ TraitBound :: Other ( trt) => trt. clone ( ) ,
16271697 } )
16281698}
16291699
1630- struct ImplBlockBuilder < ' a , D : DataExt > {
1700+ struct ImplBlockBuilder < ' a > {
16311701 input : & ' a DeriveInput ,
1632- data : & ' a D ,
1702+ data : & ' a dyn DataExt ,
16331703 trt : Trait ,
16341704 field_type_trait_bounds : FieldBounds < ' a > ,
16351705 zerocopy_crate : & ' a Path ,
@@ -1639,10 +1709,10 @@ struct ImplBlockBuilder<'a, D: DataExt> {
16391709 outer_extras : Option < TokenStream > ,
16401710}
16411711
1642- impl < ' a , D : DataExt > ImplBlockBuilder < ' a , D > {
1712+ impl < ' a > ImplBlockBuilder < ' a > {
16431713 fn new (
16441714 input : & ' a DeriveInput ,
1645- data : & ' a D ,
1715+ data : & ' a dyn DataExt ,
16461716 trt : Trait ,
16471717 field_type_trait_bounds : FieldBounds < ' a > ,
16481718 zerocopy_crate : & ' a Path ,
@@ -1759,12 +1829,12 @@ impl<'a, D: DataExt> ImplBlockBuilder<'a, D> {
17591829 ( FieldBounds :: All ( traits) , _) => fields
17601830 . iter ( )
17611831 . map ( |( _vis, _name, ty) | {
1762- bound_tt ( ty, normalize_bounds ( self . trt , traits) , zerocopy_crate)
1832+ bound_tt ( ty, normalize_bounds ( & self . trt , traits) , zerocopy_crate)
17631833 } )
17641834 . collect ( ) ,
17651835 ( FieldBounds :: None , _) | ( FieldBounds :: Trailing ( ..) , [ ] ) => vec ! [ ] ,
17661836 ( FieldBounds :: Trailing ( traits) , [ .., last] ) => {
1767- vec ! [ bound_tt( last. 2 , normalize_bounds( self . trt, traits) , zerocopy_crate) ]
1837+ vec ! [ bound_tt( last. 2 , normalize_bounds( & self . trt, traits) , zerocopy_crate) ]
17681838 }
17691839 ( FieldBounds :: Explicit ( bounds) , _) => bounds,
17701840 } ;
@@ -1796,7 +1866,7 @@ impl<'a, D: DataExt> ImplBlockBuilder<'a, D> {
17961866 let self_bounds: Option < WherePredicate > = match self . self_type_trait_bounds {
17971867 SelfBounds :: None => None ,
17981868 SelfBounds :: All ( traits) => {
1799- Some ( bound_tt ( & parse_quote ! ( Self ) , traits. iter ( ) . copied ( ) , zerocopy_crate) )
1869+ Some ( bound_tt ( & parse_quote ! ( Self ) , traits. iter ( ) . cloned ( ) , zerocopy_crate) )
18001870 }
18011871 } ;
18021872
@@ -1855,7 +1925,7 @@ impl<'a, D: DataExt> ImplBlockBuilder<'a, D> {
18551925 }
18561926 } ;
18571927
1858- if let Some ( outer_extras) = self . outer_extras {
1928+ if let Some ( outer_extras) = self . outer_extras . filter ( |e| !e . is_empty ( ) ) {
18591929 // So that any items defined in `#outer_extras` don't conflict with
18601930 // existing names defined in this scope.
18611931 quote ! {
0 commit comments