@@ -40,7 +40,8 @@ arguments:
40
40
- `EnumMatching`, when `Self` is an enum and all the arguments are the
41
41
same variant of the enum (e.g. `Some(1)`, `Some(3)` and `Some(4)`)
42
42
- `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.
44
45
45
46
In the first two cases, the values from the corresponding fields in
46
47
all the arguments are grouped together. In the `EnumNonMatching` case
@@ -129,9 +130,11 @@ use core::prelude::*;
129
130
use ast;
130
131
131
132
use ast:: {
133
+
132
134
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} ;
135
138
136
139
use ast_util;
137
140
use ext:: base:: ext_ctxt;
@@ -177,6 +180,10 @@ pub struct MethodDef<'self> {
177
180
/// Number of arguments other than `self` (all of type `&Self`)
178
181
nargs : uint ,
179
182
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
+
180
187
combine_substructure : CombineSubstructureFunc < ' self >
181
188
}
182
189
@@ -555,12 +562,13 @@ impl<'self> MethodDef<'self> {
555
562
enum_def : & enum_def ,
556
563
type_ident : ident )
557
564
-> @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 )
559
567
}
560
568
561
569
562
570
/**
563
- Creates the nested matches for an enum definition, i.e.
571
+ Creates the nested matches for an enum definition recursively , i.e.
564
572
565
573
```
566
574
match self {
@@ -575,14 +583,20 @@ impl<'self> MethodDef<'self> {
575
583
the tree are the same. Hopefully the optimisers get rid of any
576
584
repetition, otherwise derived methods with many Self arguments will be
577
585
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).
578
590
*/
579
591
fn build_enum_match( & self ,
580
592
cx : @ext_ctxt , span : span ,
581
593
enum_def : & enum_def ,
582
594
type_ident : ident ,
595
+ matching : Option < uint > ,
583
596
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 {
586
600
// we've matched against all arguments, so make the final
587
601
// expression at the bottom of the match tree
588
602
match matches_so_far {
@@ -594,41 +608,44 @@ impl<'self> MethodDef<'self> {
594
608
// vec of tuples, where each tuple represents a
595
609
// field.
596
610
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
-
603
611
let substructure;
604
612
605
613
// most arms don't have matching variants, so do a
606
614
// quick check to see if they match (even though
607
615
// this means iterating twice) instead of being
608
616
// 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
+ }
615
631
}
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) ;
616
641
}
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) ;
625
642
}
626
643
self . call_substructure_method( cx, span, type_ident, & substructure)
627
644
}
628
645
}
629
646
630
647
} 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 {
632
649
( cx. ident_of( ~"self "), ~" __self")
633
650
} else {
634
651
let s = fmt ! ( "__other_%u" , matches_so_far. len( ) - 1 ) ;
@@ -640,8 +657,32 @@ impl<'self> MethodDef<'self> {
640
657
// this is used as a stack
641
658
let mut matches_so_far = matches_so_far;
642
659
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] ;
645
686
let pattern = create_enum_variant_pattern( cx, span,
646
687
variant,
647
688
current_match_str) ;
@@ -653,23 +694,63 @@ impl<'self> MethodDef<'self> {
653
694
}
654
695
} ;
655
696
656
-
657
697
matches_so_far. push( ( index, * variant, idents) ) ;
658
698
let arm_expr = self . build_enum_match( cx, span,
659
699
enum_def,
660
700
type_ident,
661
- matches_so_far) ;
701
+ matching,
702
+ matches_so_far,
703
+ match_count + 1 ) ;
662
704
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) ;
670
706
arms. push( arm) ;
671
- }
672
707
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
+ }
673
754
let deref_expr = build:: mk_unary( cx, span, deref,
674
755
build:: mk_path( cx, span,
675
756
~[ current_match_ident ] ) ) ;
0 commit comments