|
37 | 37 | quote::quote,
|
38 | 38 | syn::{
|
39 | 39 | parse_quote, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit,
|
40 |
| - GenericParam, Ident, Lit, |
| 40 | + GenericParam, Ident, Lit, WherePredicate, |
41 | 41 | },
|
42 | 42 | };
|
43 | 43 |
|
@@ -446,41 +446,46 @@ fn derive_from_zeros_struct(ast: &DeriveInput, strct: &DataStruct) -> proc_macro
|
446 | 446 | }
|
447 | 447 |
|
448 | 448 | // An enum is `FromZeros` if:
|
449 |
| -// - all of its variants are fieldless |
450 | 449 | // - one of the variants has a discriminant of `0`
|
| 450 | +// - that discriminant's fields all implement `FromZeros` |
451 | 451 |
|
452 | 452 | 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 |
| - |
458 | 453 | // We don't actually care what the repr is; we just care that it's one of
|
459 | 454 | // the allowed ones.
|
460 | 455 | try_or_print!(ENUM_FROM_ZEROS_AS_BYTES_CFG.validate_reprs(ast));
|
461 | 456 |
|
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; |
468 | 461 | }
|
469 |
| - }); |
| 462 | + } |
| 463 | + false |
| 464 | + }); |
470 | 465 | // 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 |
472 | 467 | let has_implicit_zero_discriminant =
|
473 | 468 | enm.variants.iter().next().map(|v| v.discriminant.is_none()) == Some(true);
|
474 | 469 |
|
475 |
| - if !has_explicit_zero_discriminant && !has_implicit_zero_discriminant { |
| 470 | + if !explicit_zero_variant.is_some() && !has_implicit_zero_discriminant { |
476 | 471 | return Error::new_spanned(
|
477 | 472 | ast,
|
478 | 473 | "FromZeros only supported on enums with a variant that has a discriminant of `0`",
|
479 | 474 | )
|
480 | 475 | .to_compile_error();
|
481 | 476 | }
|
482 | 477 |
|
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)) |
484 | 489 | }
|
485 | 490 |
|
486 | 491 | // Like structs, unions are `FromZeros` if
|
@@ -820,11 +825,11 @@ impl Trait {
|
820 | 825 | }
|
821 | 826 | }
|
822 | 827 |
|
823 |
| -#[derive(Debug, Eq, PartialEq)] |
824 | 828 | enum RequireBoundedFields {
|
825 | 829 | No,
|
826 | 830 | Yes,
|
827 | 831 | Trailing,
|
| 832 | + Explicit(Vec<WherePredicate>), |
828 | 833 | }
|
829 | 834 |
|
830 | 835 | impl From<bool> for RequireBoundedFields {
|
@@ -945,6 +950,7 @@ fn impl_block_with_context<D: DataExt>(
|
945 | 950 | (RequireBoundedFields::Yes, _) => fields.iter().map(|(_name, ty)| bound_tt(ty)).collect(),
|
946 | 951 | (RequireBoundedFields::No, _) | (RequireBoundedFields::Trailing, []) => vec![],
|
947 | 952 | (RequireBoundedFields::Trailing, [.., last]) => vec![bound_tt(&last.1)],
|
| 953 | + (RequireBoundedFields::Explicit(bounds), _) => bounds, |
948 | 954 | };
|
949 | 955 |
|
950 | 956 | // Don't bother emitting a padding check if there are no fields.
|
|
0 commit comments