@@ -222,14 +222,27 @@ pub struct MethodDef<'a> {
222222
223223 pub attributes : ast:: AttrVec ,
224224
225- /// Can we combine fieldless variants for enums into a single match arm?
226- /// If true, indicates that the trait operation uses the enum tag in some
227- /// way.
228- pub unify_fieldless_variants : bool ,
225+ pub fieldless_variants_strategy : FieldlessVariantsStrategy ,
229226
230227 pub combine_substructure : RefCell < CombineSubstructureFunc < ' a > > ,
231228}
232229
230+ /// How to handle fieldless enum variants.
231+ #[ derive( PartialEq ) ]
232+ pub enum FieldlessVariantsStrategy {
233+ /// Combine fieldless variants into a single match arm.
234+ /// This assumes that relevant information has been handled
235+ /// by looking at the enum's discriminant.
236+ Unify ,
237+ /// Don't do anything special about fieldless variants. They are
238+ /// handled like any other variant.
239+ Default ,
240+ /// If all variants of the enum are fieldless, expand the special
241+ /// `AllFieldLessEnum` substructure, so that the entire enum can be handled
242+ /// at once.
243+ SpecializeIfAllVariantsFieldless ,
244+ }
245+
233246/// All the data about the data structure/method being derived upon.
234247pub struct Substructure < ' a > {
235248 /// ident of self
@@ -264,9 +277,14 @@ pub enum StaticFields {
264277
265278/// A summary of the possible sets of fields.
266279pub enum SubstructureFields < ' a > {
267- /// A non-static method with `Self` is a struct.
280+ /// A non-static method where `Self` is a struct.
268281 Struct ( & ' a ast:: VariantData , Vec < FieldInfo > ) ,
269282
283+ /// A non-static method handling the entire enum at once
284+ /// (after it has been determined that none of the enum
285+ /// variants has any fields).
286+ AllFieldlessEnum ( & ' a ast:: EnumDef ) ,
287+
270288 /// Matching variants of the enum: variant index, variant count, ast::Variant,
271289 /// fields: the field name is only non-`None` in the case of a struct
272290 /// variant.
@@ -1086,8 +1104,8 @@ impl<'a> MethodDef<'a> {
10861104 /// ```
10871105 /// Creates a tag check combined with a match for a tuple of all
10881106 /// `selflike_args`, with an arm for each variant with fields, possibly an
1089- /// arm for each fieldless variant (if `! unify_fieldless_variants` is not
1090- /// true ), and possibly a default arm.
1107+ /// arm for each fieldless variant (if `unify_fieldless_variants` is not
1108+ /// `Unify` ), and possibly a default arm.
10911109 fn expand_enum_method_body < ' b > (
10921110 & self ,
10931111 cx : & mut ExtCtxt < ' _ > ,
@@ -1101,7 +1119,8 @@ impl<'a> MethodDef<'a> {
11011119 let variants = & enum_def. variants ;
11021120
11031121 // Traits that unify fieldless variants always use the tag(s).
1104- let uses_tags = self . unify_fieldless_variants ;
1122+ let unify_fieldless_variants =
1123+ self . fieldless_variants_strategy == FieldlessVariantsStrategy :: Unify ;
11051124
11061125 // There is no sensible code to be generated for *any* deriving on a
11071126 // zero-variant enum. So we just generate a failing expression.
@@ -1161,23 +1180,35 @@ impl<'a> MethodDef<'a> {
11611180 // match is necessary.
11621181 let all_fieldless = variants. iter ( ) . all ( |v| v. data . fields ( ) . is_empty ( ) ) ;
11631182 if all_fieldless {
1164- if uses_tags && variants. len ( ) > 1 {
1165- // If the type is fieldless and the trait uses the tag and
1166- // there are multiple variants, we need just an operation on
1167- // the tag(s).
1168- let ( tag_field, mut tag_let_stmts) = get_tag_pieces ( cx) ;
1169- let mut tag_check = self . call_substructure_method (
1170- cx,
1171- trait_,
1172- type_ident,
1173- nonselflike_args,
1174- & EnumTag ( tag_field, None ) ,
1175- ) ;
1176- tag_let_stmts. append ( & mut tag_check. 0 ) ;
1177- return BlockOrExpr ( tag_let_stmts, tag_check. 1 ) ;
1178- }
1179-
1180- if variants. len ( ) == 1 {
1183+ if variants. len ( ) > 1 {
1184+ match self . fieldless_variants_strategy {
1185+ FieldlessVariantsStrategy :: Unify => {
1186+ // If the type is fieldless and the trait uses the tag and
1187+ // there are multiple variants, we need just an operation on
1188+ // the tag(s).
1189+ let ( tag_field, mut tag_let_stmts) = get_tag_pieces ( cx) ;
1190+ let mut tag_check = self . call_substructure_method (
1191+ cx,
1192+ trait_,
1193+ type_ident,
1194+ nonselflike_args,
1195+ & EnumTag ( tag_field, None ) ,
1196+ ) ;
1197+ tag_let_stmts. append ( & mut tag_check. 0 ) ;
1198+ return BlockOrExpr ( tag_let_stmts, tag_check. 1 ) ;
1199+ }
1200+ FieldlessVariantsStrategy :: SpecializeIfAllVariantsFieldless => {
1201+ return self . call_substructure_method (
1202+ cx,
1203+ trait_,
1204+ type_ident,
1205+ nonselflike_args,
1206+ & AllFieldlessEnum ( enum_def) ,
1207+ ) ;
1208+ }
1209+ FieldlessVariantsStrategy :: Default => ( ) ,
1210+ }
1211+ } else if variants. len ( ) == 1 {
11811212 // If there is a single variant, we don't need an operation on
11821213 // the tag(s). Just use the most degenerate result.
11831214 return self . call_substructure_method (
@@ -1187,7 +1218,7 @@ impl<'a> MethodDef<'a> {
11871218 nonselflike_args,
11881219 & EnumMatching ( 0 , 1 , & variants[ 0 ] , Vec :: new ( ) ) ,
11891220 ) ;
1190- } ;
1221+ }
11911222 }
11921223
11931224 // These arms are of the form:
@@ -1198,7 +1229,7 @@ impl<'a> MethodDef<'a> {
11981229 let mut match_arms: Vec < ast:: Arm > = variants
11991230 . iter ( )
12001231 . enumerate ( )
1201- . filter ( |& ( _, v) | !( self . unify_fieldless_variants && v. data . fields ( ) . is_empty ( ) ) )
1232+ . filter ( |& ( _, v) | !( unify_fieldless_variants && v. data . fields ( ) . is_empty ( ) ) )
12021233 . map ( |( index, variant) | {
12031234 // A single arm has form (&VariantK, &VariantK, ...) => BodyK
12041235 // (see "Final wrinkle" note below for why.)
@@ -1249,7 +1280,7 @@ impl<'a> MethodDef<'a> {
12491280 // Add a default arm to the match, if necessary.
12501281 let first_fieldless = variants. iter ( ) . find ( |v| v. data . fields ( ) . is_empty ( ) ) ;
12511282 let default = match first_fieldless {
1252- Some ( v) if self . unify_fieldless_variants => {
1283+ Some ( v) if unify_fieldless_variants => {
12531284 // We need a default case that handles all the fieldless
12541285 // variants. The index and actual variant aren't meaningful in
12551286 // this case, so just use dummy values.
@@ -1296,7 +1327,7 @@ impl<'a> MethodDef<'a> {
12961327 // If the trait uses the tag and there are multiple variants, we need
12971328 // to add a tag check operation before the match. Otherwise, the match
12981329 // is enough.
1299- if uses_tags && variants. len ( ) > 1 {
1330+ if unify_fieldless_variants && variants. len ( ) > 1 {
13001331 let ( tag_field, mut tag_let_stmts) = get_tag_pieces ( cx) ;
13011332
13021333 // Combine a tag check with the match.
@@ -1580,5 +1611,6 @@ where
15801611 }
15811612 }
15821613 StaticEnum ( ..) | StaticStruct ( ..) => cx. span_bug ( trait_span, "static function in `derive`" ) ,
1614+ AllFieldlessEnum ( ..) => cx. span_bug ( trait_span, "fieldless enum in `derive`" ) ,
15831615 }
15841616}
0 commit comments