@@ -222,14 +222,27 @@ pub struct MethodDef<'a> {
222
222
223
223
pub attributes : ast:: AttrVec ,
224
224
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 ,
229
226
230
227
pub combine_substructure : RefCell < CombineSubstructureFunc < ' a > > ,
231
228
}
232
229
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
+
233
246
/// All the data about the data structure/method being derived upon.
234
247
pub struct Substructure < ' a > {
235
248
/// ident of self
@@ -264,9 +277,14 @@ pub enum StaticFields {
264
277
265
278
/// A summary of the possible sets of fields.
266
279
pub enum SubstructureFields < ' a > {
267
- /// A non-static method with `Self` is a struct.
280
+ /// A non-static method where `Self` is a struct.
268
281
Struct ( & ' a ast:: VariantData , Vec < FieldInfo > ) ,
269
282
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
+
270
288
/// Matching variants of the enum: variant index, variant count, ast::Variant,
271
289
/// fields: the field name is only non-`None` in the case of a struct
272
290
/// variant.
@@ -1086,8 +1104,8 @@ impl<'a> MethodDef<'a> {
1086
1104
/// ```
1087
1105
/// Creates a tag check combined with a match for a tuple of all
1088
1106
/// `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.
1091
1109
fn expand_enum_method_body < ' b > (
1092
1110
& self ,
1093
1111
cx : & mut ExtCtxt < ' _ > ,
@@ -1101,7 +1119,8 @@ impl<'a> MethodDef<'a> {
1101
1119
let variants = & enum_def. variants ;
1102
1120
1103
1121
// 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 ;
1105
1124
1106
1125
// There is no sensible code to be generated for *any* deriving on a
1107
1126
// zero-variant enum. So we just generate a failing expression.
@@ -1161,23 +1180,35 @@ impl<'a> MethodDef<'a> {
1161
1180
// match is necessary.
1162
1181
let all_fieldless = variants. iter ( ) . all ( |v| v. data . fields ( ) . is_empty ( ) ) ;
1163
1182
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 {
1181
1212
// If there is a single variant, we don't need an operation on
1182
1213
// the tag(s). Just use the most degenerate result.
1183
1214
return self . call_substructure_method (
@@ -1187,7 +1218,7 @@ impl<'a> MethodDef<'a> {
1187
1218
nonselflike_args,
1188
1219
& EnumMatching ( 0 , 1 , & variants[ 0 ] , Vec :: new ( ) ) ,
1189
1220
) ;
1190
- } ;
1221
+ }
1191
1222
}
1192
1223
1193
1224
// These arms are of the form:
@@ -1198,7 +1229,7 @@ impl<'a> MethodDef<'a> {
1198
1229
let mut match_arms: Vec < ast:: Arm > = variants
1199
1230
. iter ( )
1200
1231
. enumerate ( )
1201
- . filter ( |& ( _, v) | !( self . unify_fieldless_variants && v. data . fields ( ) . is_empty ( ) ) )
1232
+ . filter ( |& ( _, v) | !( unify_fieldless_variants && v. data . fields ( ) . is_empty ( ) ) )
1202
1233
. map ( |( index, variant) | {
1203
1234
// A single arm has form (&VariantK, &VariantK, ...) => BodyK
1204
1235
// (see "Final wrinkle" note below for why.)
@@ -1249,7 +1280,7 @@ impl<'a> MethodDef<'a> {
1249
1280
// Add a default arm to the match, if necessary.
1250
1281
let first_fieldless = variants. iter ( ) . find ( |v| v. data . fields ( ) . is_empty ( ) ) ;
1251
1282
let default = match first_fieldless {
1252
- Some ( v) if self . unify_fieldless_variants => {
1283
+ Some ( v) if unify_fieldless_variants => {
1253
1284
// We need a default case that handles all the fieldless
1254
1285
// variants. The index and actual variant aren't meaningful in
1255
1286
// this case, so just use dummy values.
@@ -1296,7 +1327,7 @@ impl<'a> MethodDef<'a> {
1296
1327
// If the trait uses the tag and there are multiple variants, we need
1297
1328
// to add a tag check operation before the match. Otherwise, the match
1298
1329
// is enough.
1299
- if uses_tags && variants. len ( ) > 1 {
1330
+ if unify_fieldless_variants && variants. len ( ) > 1 {
1300
1331
let ( tag_field, mut tag_let_stmts) = get_tag_pieces ( cx) ;
1301
1332
1302
1333
// Combine a tag check with the match.
@@ -1580,5 +1611,6 @@ where
1580
1611
}
1581
1612
}
1582
1613
StaticEnum ( ..) | StaticStruct ( ..) => cx. span_bug ( trait_span, "static function in `derive`" ) ,
1614
+ AllFieldlessEnum ( ..) => cx. span_bug ( trait_span, "fieldless enum in `derive`" ) ,
1583
1615
}
1584
1616
}
0 commit comments