Skip to content

Commit fdd14f8

Browse files
committed
Re-introduce type argument (nested = <Type>) of #[enumcapsulate(discriminant(nested))] variant attribute, but as an optional argument
(required for fields using the parent enum's generic parameters)
1 parent dcb65ca commit fdd14f8

File tree

9 files changed

+211
-116
lines changed

9 files changed

+211
-116
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Please make sure to add your changes to the appropriate categories:
2424

2525
### Changed
2626

27-
- n/a
27+
- Re-introduced type argument (`nested = <Type>`) of `#[enumcapsulate(discriminant(nested))]` variant attribute, but as an optional argument (required for fields using the parent enum's generic parameters).
2828

2929
### Deprecated
3030

macros/README.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,37 @@ enum Enum {
310310
311311
There is limited support for generic enums:
312312
313+
### `VariantDiscriminant`
314+
315+
When using the `VariantDiscriminant` derive macro with a generic enum you have to
316+
provide an explicit type path for any nested discriminant that uses any of the parent
317+
enum's generic parameters in its own type.
318+
319+
```rust
320+
#[derive(VariantDiscriminant)]
321+
enum VariantA<T> {
322+
VariantA1(T),
323+
// ...
324+
}
325+
326+
#[derive(VariantDiscriminant)]
327+
enum VariantB {
328+
VariantB1,
329+
// ...
330+
}
331+
332+
#[derive(VariantDiscriminant)]
333+
enum Enum<T> {
334+
#[enumcapsulate(discriminant(nested = VariantADiscriminant))]
335+
VariantA(VariantA<T>),
336+
#[enumcapsulate(discriminant(nested))]
337+
VariantB(VariantB),
338+
// ...
339+
}
340+
```
341+
342+
### Generic traits
343+
313344
Variants using generic const/type parameters are always excluded when deriving generic traits with `enumcapsulate`'s derive macros.
314345
315346
The reason for this behavior is that implementing generic traits for variants that use any of the generic parameters of the enum tends to result in conflicting implementations in Rust, as shown by the following example program:

macros/src/config/for_variant/discriminant.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,17 @@ use syn::meta::ParseNestedMeta;
22

33
use crate::attr::{NAME, NESTED, VALUE};
44

5+
#[derive(Clone)]
6+
pub enum NestedDiscriminantType {
7+
Default,
8+
Custom(syn::Type),
9+
}
10+
511
#[derive(Clone, Default)]
612
pub(crate) struct DiscriminantConfig {
713
value: Option<syn::Expr>,
814
name: Option<syn::Ident>,
9-
nested: bool,
15+
nested: Option<NestedDiscriminantType>,
1016
}
1117

1218
impl DiscriminantConfig {
@@ -19,7 +25,7 @@ impl DiscriminantConfig {
1925
if meta.path.is_ident(VALUE) {
2026
if self.value.is_some() {
2127
return Err(meta.error("`value = …` already specified"));
22-
} else if self.nested {
28+
} else if self.nested.is_some() {
2329
return Err(meta.error("conflicting with use of `nesting`"));
2430
}
2531

@@ -33,13 +39,18 @@ impl DiscriminantConfig {
3339
} else if meta.path.is_ident(NESTED) {
3440
if matches!(variant.fields, syn::Fields::Unit) {
3541
return Err(meta.error("no field found on variant"));
36-
} else if self.nested {
42+
} else if self.nested.is_some() {
3743
return Err(meta.error("`nested` already specified"));
3844
} else if self.value.is_some() {
3945
return Err(meta.error("conflicting with use of `value = …`"));
4046
}
4147

42-
self.nested = true;
48+
if meta.input.peek(syn::Token![=]) {
49+
let custom_type = meta.value()?.parse()?;
50+
self.nested = Some(NestedDiscriminantType::Custom(custom_type));
51+
} else {
52+
self.nested = Some(NestedDiscriminantType::Default);
53+
}
4354
} else {
4455
return Err(meta.error("unsupported discriminant attribute"));
4556
}
@@ -56,7 +67,7 @@ impl DiscriminantConfig {
5667
self.name.as_ref()
5768
}
5869

59-
pub(crate) fn nested(&self) -> bool {
60-
self.nested
70+
pub(crate) fn nested(&self) -> Option<&NestedDiscriminantType> {
71+
self.nested.as_ref()
6172
}
6273
}

macros/src/enum_deriver.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use proc_macro2::TokenStream as TokenStream2;
22
use quote::quote;
3-
use syn::{parse_quote, parse_quote_spanned, visit::Visit as _, Fields, Type, Variant};
3+
use syn::{
4+
parse_quote, parse_quote_spanned, spanned::Spanned, visit::Visit as _, Fields, Type, Variant,
5+
};
46

57
use crate::*;
68

@@ -579,13 +581,28 @@ impl EnumDeriver {
579581
discriminant_variant_expr = Some(expr);
580582
}
581583

582-
if discriminant_config.nested() {
583-
let (field, _) = field_selection.expect("no selected field found");
584-
let ty = &field.ty;
585-
let nested_type = parse_quote! {
586-
<#ty as ::enumcapsulate::VariantDiscriminant>::Discriminant
587-
};
588-
discriminant_variant_nested = Some(nested_type);
584+
if let Some(nested) = discriminant_config.nested() {
585+
match nested {
586+
NestedDiscriminantType::Default => {
587+
let (field, _) = field_selection.expect("no selected field found");
588+
let field_type = &field.ty;
589+
590+
if self.uses_generic_const_or_type(field_type) {
591+
return Err(syn::Error::new(
592+
field.span(),
593+
"generic fields require an explicit nested discriminant type",
594+
));
595+
}
596+
597+
let nested_type = parse_quote! {
598+
<#field_type as ::enumcapsulate::VariantDiscriminant>::Discriminant
599+
};
600+
discriminant_variant_nested = Some(nested_type);
601+
}
602+
NestedDiscriminantType::Custom(custom_type) => {
603+
discriminant_variant_nested = Some(custom_type.clone());
604+
}
605+
}
589606
}
590607
}
591608

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
error: generic fields require an explicit nested discriminant type
2+
--> /tests/derive-tests/variant_discriminant/fail/nested_generic_field.rs:12:14
3+
|
4+
12 | VariantA(VariantA<T>),
5+
| ^^^^^^^^
6+
error: could not compile `<CRATE>` (bin "<BIN>") due to 1 previous error
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use enumcapsulate::VariantDiscriminant;
2+
pub enum VariantA<T> {
3+
Variant(T),
4+
}
5+
pub enum VariantADiscriminant {
6+
Variant,
7+
}
8+
#[automatically_derived]
9+
impl ::core::marker::Copy for VariantADiscriminant {}
10+
#[automatically_derived]
11+
impl ::core::clone::Clone for VariantADiscriminant {
12+
#[inline]
13+
fn clone(&self) -> VariantADiscriminant {
14+
*self
15+
}
16+
}
17+
#[automatically_derived]
18+
impl ::core::cmp::Ord for VariantADiscriminant {
19+
#[inline]
20+
fn cmp(&self, other: &VariantADiscriminant) -> ::core::cmp::Ordering {
21+
::core::cmp::Ordering::Equal
22+
}
23+
}
24+
#[automatically_derived]
25+
impl ::core::cmp::PartialOrd for VariantADiscriminant {
26+
#[inline]
27+
fn partial_cmp(
28+
&self,
29+
other: &VariantADiscriminant,
30+
) -> ::core::option::Option<::core::cmp::Ordering> {
31+
::core::option::Option::Some(::core::cmp::Ordering::Equal)
32+
}
33+
}
34+
#[automatically_derived]
35+
impl ::core::cmp::Eq for VariantADiscriminant {
36+
#[inline]
37+
#[doc(hidden)]
38+
#[coverage(off)]
39+
fn assert_receiver_is_total_eq(&self) -> () {}
40+
}
41+
#[automatically_derived]
42+
impl ::core::marker::StructuralPartialEq for VariantADiscriminant {}
43+
#[automatically_derived]
44+
impl ::core::cmp::PartialEq for VariantADiscriminant {
45+
#[inline]
46+
fn eq(&self, other: &VariantADiscriminant) -> bool {
47+
true
48+
}
49+
}
50+
#[automatically_derived]
51+
impl ::core::hash::Hash for VariantADiscriminant {
52+
#[inline]
53+
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () {}
54+
}
55+
#[automatically_derived]
56+
impl ::core::fmt::Debug for VariantADiscriminant {
57+
#[inline]
58+
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
59+
::core::fmt::Formatter::write_str(f, "Variant")
60+
}
61+
}
62+
impl<T> ::enumcapsulate::VariantDiscriminant for VariantA<T> {
63+
type Discriminant = VariantADiscriminant;
64+
fn variant_discriminant(&self) -> Self::Discriminant {
65+
match self {
66+
VariantA::Variant(..) => VariantADiscriminant::Variant,
67+
_ => ::core::panicking::panic("internal error: entered unreachable code"),
68+
}
69+
}
70+
}
71+
#[enumcapsulate(discriminant(repr = u8))]
72+
pub enum Enum<T> {
73+
#[enumcapsulate(discriminant(nested))]
74+
VariantA(VariantA<T>),
75+
}
76+
fn main() {}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use enumcapsulate::VariantDiscriminant;
2+
3+
#[derive(VariantDiscriminant)]
4+
pub enum VariantA<T> {
5+
Variant(T),
6+
}
7+
8+
#[derive(VariantDiscriminant)]
9+
#[enumcapsulate(discriminant(repr = u8))]
10+
pub enum Enum<T> {
11+
#[enumcapsulate(discriminant(nested))]
12+
VariantA(VariantA<T>),
13+
}
14+
15+
fn main() {}

0 commit comments

Comments
 (0)