@@ -40,7 +40,8 @@ arguments:
4040- `EnumMatching`, when `Self` is an enum and all the arguments are the
4141 same variant of the enum (e.g. `Some(1)`, `Some(3)` and `Some(4)`)
4242- `EnumNonMatching` when `Self` is an enum and the arguments are not
43- the same variant (e.g. `None`, `Some(1)` and `None`)
43+ the same variant (e.g. `None`, `Some(1)` and `None`). If
44+ `const_nonmatching` is true, this will contain an empty list.
4445
4546In the first two cases, the values from the corresponding fields in
4647all the arguments are grouped together. In the `EnumNonMatching` case
@@ -129,9 +130,11 @@ use core::prelude::*;
129130use ast;
130131
131132use ast:: {
133+
132134 and, binop, deref, enum_def, expr, expr_match, ident, impure_fn,
133- item, Generics , m_imm, meta_item, method, named_field, or, public,
134- struct_def, sty_region, ty_rptr, ty_path, variant} ;
135+ item, Generics , m_imm, meta_item, method, named_field, or,
136+ pat_wild, public, struct_def, sty_region, ty_rptr, ty_path,
137+ variant} ;
135138
136139use ast_util;
137140use ext:: base:: ext_ctxt;
@@ -177,6 +180,10 @@ pub struct MethodDef<'self> {
177180 /// Number of arguments other than `self` (all of type `&Self`)
178181 nargs : uint ,
179182
183+ /// if the value of the nonmatching enums is independent of the
184+ /// actual enums, i.e. can use _ => .. match.
185+ const_nonmatching : bool ,
186+
180187 combine_substructure : CombineSubstructureFunc < ' self >
181188}
182189
@@ -555,12 +562,13 @@ impl<'self> MethodDef<'self> {
555562 enum_def : & enum_def ,
556563 type_ident : ident )
557564 -> @expr {
558- self . build_enum_match ( cx, span, enum_def, type_ident, ~[ ] )
565+ self . build_enum_match ( cx, span, enum_def, type_ident,
566+ None , ~[ ] , 0 )
559567 }
560568
561569
562570 /**
563- Creates the nested matches for an enum definition, i.e.
571+ Creates the nested matches for an enum definition recursively , i.e.
564572
565573 ```
566574 match self {
@@ -575,14 +583,20 @@ impl<'self> MethodDef<'self> {
575583 the tree are the same. Hopefully the optimisers get rid of any
576584 repetition, otherwise derived methods with many Self arguments will be
577585 exponentially large.
586+
587+ `matching` is Some(n) if all branches in the tree above the
588+ current position are variant `n`, `None` otherwise (including on
589+ the first call).
578590 */
579591 fn build_enum_match( & self ,
580592 cx : @ext_ctxt , span : span ,
581593 enum_def : & enum_def ,
582594 type_ident : ident ,
595+ matching : Option < uint > ,
583596 matches_so_far : ~[ ( uint , variant ,
584- ~[ ( Option < ident > , @expr) ] ) ] ) -> @expr {
585- if matches_so_far. len ( ) == self . nargs + 1 {
597+ ~[ ( Option < ident > , @expr) ] ) ] ,
598+ match_count : uint ) -> @expr {
599+ if match_count == self . nargs + 1 {
586600 // we've matched against all arguments, so make the final
587601 // expression at the bottom of the match tree
588602 match matches_so_far {
@@ -594,41 +608,44 @@ impl<'self> MethodDef<'self> {
594608 // vec of tuples, where each tuple represents a
595609 // field.
596610
597- // `ref` inside let matches is buggy. Causes havoc wih rusc.
598- // let (variant_index, ref self_vec) = matches_so_far[0];
599- let ( variant_index, variant, self_vec) = match matches_so_far[ 0 ] {
600- ( i, v, ref s) => ( i, v, s)
601- } ;
602-
603611 let substructure;
604612
605613 // most arms don't have matching variants, so do a
606614 // quick check to see if they match (even though
607615 // this means iterating twice) instead of being
608616 // optimistic and doing a pile of allocations etc.
609- if matches_so_far. all ( |& ( v_i, _, _) | v_i == variant_index) {
610- let mut enum_matching_fields = vec:: from_elem ( self_vec. len ( ) , ~[ ] ) ;
611-
612- for matches_so_far. tail( ) . each |& ( _, _, other_fields) | {
613- for other_fields. eachi |i, & ( _, other_field) | {
614- enum_matching_fields[ i] . push ( other_field) ;
617+ match matching {
618+ Some ( variant_index) => {
619+ // `ref` inside let matches is buggy. Causes havoc wih rusc.
620+ // let (variant_index, ref self_vec) = matches_so_far[0];
621+ let ( variant, self_vec) = match matches_so_far[ 0 ] {
622+ ( _, v, ref s) => ( v, s)
623+ } ;
624+
625+ let mut enum_matching_fields = vec:: from_elem ( self_vec. len ( ) , ~[ ] ) ;
626+
627+ for matches_so_far. tail( ) . each |& ( _, _, other_fields) | {
628+ for other_fields. eachi |i, & ( _, other_field) | {
629+ enum_matching_fields[ i] . push ( other_field) ;
630+ }
615631 }
632+ let field_tuples =
633+ do vec:: map2( * self_vec,
634+ enum_matching_fields) |& ( id, self_f) , & other| {
635+ ( id, self_f, other)
636+ } ;
637+ substructure = EnumMatching ( variant_index, variant, field_tuples) ;
638+ }
639+ None => {
640+ substructure = EnumNonMatching ( matches_so_far) ;
616641 }
617- let field_tuples =
618- do vec:: map2 ( * self_vec,
619- enum_matching_fields) |& ( id, self_f) , & other| {
620- ( id, self_f, other)
621- } ;
622- substructure = EnumMatching ( variant_index, variant, field_tuples) ;
623- } else {
624- substructure = EnumNonMatching ( matches_so_far) ;
625642 }
626643 self . call_substructure_method( cx, span, type_ident, & substructure)
627644 }
628645 }
629646
630647 } else { // there are still matches to create
631- let ( current_match_ident, current_match_str) = if matches_so_far . is_empty ( ) {
648+ let ( current_match_ident, current_match_str) = if match_count == 0 {
632649 ( cx. ident_of( ~"self "), ~" __self")
633650 } else {
634651 let s = fmt ! ( "__other_%u" , matches_so_far. len( ) - 1 ) ;
@@ -640,8 +657,32 @@ impl<'self> MethodDef<'self> {
640657 // this is used as a stack
641658 let mut matches_so_far = matches_so_far;
642659
643- // create an arm matching on each variant
644- for enum_def. variants. eachi |index, variant| {
660+ macro_rules! mk_arm (
661+ ( $pat: expr, $expr: expr) => {
662+ {
663+ let blk = build : : mk_simple_block( cx, span, $expr) ;
664+ let arm = ast:: arm {
665+ pats : ~[ $ pat ] ,
666+ guard : None ,
667+ body : blk
668+ } ;
669+ arm
670+ }
671+ }
672+ )
673+
674+ // the code for nonmatching variants only matters when
675+ // we've seen at least one other variant already
676+ if self . const_nonmatching && match_count > 0 {
677+ // make a matching-variant match, and a _ match.
678+ let index = match matching {
679+ Some ( i) => i,
680+ None => cx. span_bug( span, ~"Non -matching variants when required to\
681+ be matching in `deriving_generic`")
682+ } ;
683+
684+ // matching-variant match
685+ let variant = & enum_def. variants[ index] ;
645686 let pattern = create_enum_variant_pattern( cx, span,
646687 variant,
647688 current_match_str) ;
@@ -653,23 +694,63 @@ impl<'self> MethodDef<'self> {
653694 }
654695 } ;
655696
656-
657697 matches_so_far. push( ( index, * variant, idents) ) ;
658698 let arm_expr = self . build_enum_match( cx, span,
659699 enum_def,
660700 type_ident,
661- matches_so_far) ;
701+ matching,
702+ matches_so_far,
703+ match_count + 1 ) ;
662704 matches_so_far. pop( ) ;
663-
664- let arm_block = build:: mk_simple_block ( cx, span, arm_expr) ;
665- let arm = ast:: arm {
666- pats : ~[ pattern ] ,
667- guard : None ,
668- body : arm_block
669- } ;
705+ let arm = mk_arm ! ( pattern, arm_expr) ;
670706 arms. push( arm) ;
671- }
672707
708+ if enum_def. variants. len( ) > 1 {
709+ // _ match, if necessary
710+ let wild_pat = @ast:: pat {
711+ id : cx. next_id( ) ,
712+ node : pat_wild,
713+ span : span
714+ } ;
715+
716+ let wild_expr = self . call_substructure_method( cx, span, type_ident,
717+ & EnumNonMatching ( ~[ ] ) ) ;
718+ let wild_arm = mk_arm ! ( wild_pat, wild_expr) ;
719+ arms. push( wild_arm) ;
720+ }
721+ } else {
722+ // create an arm matching on each variant
723+ for enum_def. variants. eachi |index, variant| {
724+ let pattern = create_enum_variant_pattern( cx, span,
725+ variant,
726+ current_match_str) ;
727+
728+ let idents = do vec:: build |push| {
729+ for each_variant_arg_ident( cx, span, variant) |i, field_id| {
730+ let id = cx. ident_of( fmt ! ( "%s_%u" , current_match_str, i) ) ;
731+ push( ( field_id, build:: mk_path( cx, span, ~[ id ] ) ) ) ;
732+ }
733+ } ;
734+
735+ matches_so_far. push( ( index, * variant, idents) ) ;
736+ let new_matching =
737+ match matching {
738+ _ if match_count == 0 => Some ( index) ,
739+ Some ( i) if index == i => Some ( i) ,
740+ _ => None
741+ } ;
742+ let arm_expr = self . build_enum_match( cx, span,
743+ enum_def,
744+ type_ident,
745+ new_matching,
746+ matches_so_far,
747+ match_count + 1 ) ;
748+ matches_so_far. pop( ) ;
749+
750+ let arm = mk_arm ! ( pattern, arm_expr) ;
751+ arms. push( arm) ;
752+ }
753+ }
673754 let deref_expr = build:: mk_unary( cx, span, deref,
674755 build:: mk_path( cx, span,
675756 ~[ current_match_ident ] ) ) ;
0 commit comments