Skip to content

Commit ef5927d

Browse files
committed
Support deriving FromZeros on data-carrying enums
1 parent cc30cde commit ef5927d

File tree

1 file changed

+24
-18
lines changed

1 file changed

+24
-18
lines changed

zerocopy-derive/src/lib.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use {
3737
quote::quote,
3838
syn::{
3939
parse_quote, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit,
40-
GenericParam, Ident, Lit,
40+
GenericParam, Ident, Lit, WherePredicate,
4141
},
4242
};
4343

@@ -446,41 +446,46 @@ fn derive_from_zeros_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro
446446
}
447447

448448
// An enum is `FromZeros` if:
449-
// - all of its variants are fieldless
450449
// - one of the variants has a discriminant of `0`
450+
// - that discriminant's fields all implement `FromZeros`
451451

452452
fn derive_from_zeros_enum(ast: &DeriveInput, enm: &DataEnum) -> proc_macro2::TokenStream {
453-
if !enm.is_fieldless() {
454-
return Error::new_spanned(ast, "only field-less enums can implement FromZeros")
455-
.to_compile_error();
456-
}
457-
458453
// We don't actually care what the repr is; we just care that it's one of
459454
// the allowed ones.
460455
try_or_print!(ENUM_FROM_ZEROS_AS_BYTES_CFG.validate_reprs(ast));
461456

462-
let has_explicit_zero_discriminant =
463-
enm.variants.iter().filter_map(|v| v.discriminant.as_ref()).any(|(_, e)| {
464-
if let Expr::Lit(ExprLit { lit: Lit::Int(i), .. }) = e {
465-
i.base10_parse::<usize>().ok() == Some(0)
466-
} else {
467-
false
457+
let explicit_zero_variant = enm.variants.iter().position(|v| {
458+
if let Some((_, Expr::Lit(ExprLit { lit: Lit::Int(i), .. }))) = v.discriminant.as_ref() {
459+
if i.base10_parse::<usize>().ok() == Some(0) {
460+
return true;
468461
}
469-
});
462+
}
463+
false
464+
});
470465
// If the first variant of an enum does not specify its discriminant, it is set to zero:
471-
// https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations
466+
// https://doc.rust-lang.org/reference/items/enumerations.html#implicit-discriminants
472467
let has_implicit_zero_discriminant =
473468
enm.variants.iter().next().map(|v| v.discriminant.is_none()) == Some(true);
474469

475-
if !has_explicit_zero_discriminant && !has_implicit_zero_discriminant {
470+
if !explicit_zero_variant.is_some() && !has_implicit_zero_discriminant {
476471
return Error::new_spanned(
477472
ast,
478473
"FromZeros only supported on enums with a variant that has a discriminant of `0`",
479474
)
480475
.to_compile_error();
481476
}
482477

483-
simple_impl_block(ast, enm, Trait::FromZeros, RequireBoundedFields::Yes)
478+
let zero_variant = enm.variants.iter().nth(explicit_zero_variant.unwrap_or(0)).unwrap();
479+
let explicit_bounds = zero_variant
480+
.fields
481+
.iter()
482+
.map(|field| {
483+
let ty = &field.ty;
484+
parse_quote! { #ty: ::zerocopy::FromZeros }
485+
})
486+
.collect::<Vec<WherePredicate>>();
487+
488+
simple_impl_block(ast, enm, Trait::FromZeros, RequireBoundedFields::Explicit(explicit_bounds))
484489
}
485490

486491
// Like structs, unions are `FromZeros` if
@@ -820,11 +825,11 @@ impl Trait {
820825
}
821826
}
822827

823-
#[derive(Debug, Eq, PartialEq)]
824828
enum RequireBoundedFields {
825829
No,
826830
Yes,
827831
Trailing,
832+
Explicit(Vec<WherePredicate>),
828833
}
829834

830835
impl From<bool> for RequireBoundedFields {
@@ -945,6 +950,7 @@ fn impl_block_with_context<D: DataExt>(
945950
(RequireBoundedFields::Yes, _) => fields.iter().map(|(_name, ty)| bound_tt(ty)).collect(),
946951
(RequireBoundedFields::No, _) | (RequireBoundedFields::Trailing, []) => vec![],
947952
(RequireBoundedFields::Trailing, [.., last]) => vec![bound_tt(&last.1)],
953+
(RequireBoundedFields::Explicit(bounds), _) => bounds,
948954
};
949955

950956
// Don't bother emitting a padding check if there are no fields.

0 commit comments

Comments
 (0)