139
139
//!
140
140
//! It is computed as follows. We look at the pattern `p_1` on top of the stack,
141
141
//! and we have three cases:
142
- //! 1 .1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
143
- //! 1 .2. `p_1 = _`. We return the rest of the stack:
142
+ //! 2 .1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
143
+ //! 2 .2. `p_1 = _`. We return the rest of the stack:
144
144
//! p_2, .., p_n
145
- //! 1 .3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
145
+ //! 2 .3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
146
146
//! stack.
147
147
//! D((r_1, p_2, .., p_n))
148
148
//! D((r_2, p_2, .., p_n))
@@ -509,6 +509,14 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
509
509
#[ derive( Clone , Debug ) ]
510
510
enum SpecializationCache {
511
511
/// Patterns consist of only enum variants.
512
+ /// Variant patterns does not intersect with each other (in contrast to range patterns),
513
+ /// so it is possible to precompute the result of `Matrix::specialize_constructor` at a
514
+ /// lower computational complexity.
515
+ /// `lookup` is responsible for holding the precomputed result of
516
+ /// `Matrix::specialize_constructor`, while `wilds` is used for two purposes: the first one is
517
+ /// the precomputed result of `Matrix::specialize_wildcard`, and the second is to be used as a
518
+ /// fallback for `Matrix::specialize_constructor` when it tries to apply a constructor that
519
+ /// has not been seen in the `Matrix`. See `update_cache` for further explanations.
512
520
Variants { lookup : FxHashMap < DefId , SmallVec < [ usize ; 1 ] > > , wilds : SmallVec < [ usize ; 1 ] > } ,
513
521
/// Does not belong to the cases above, use the slow path.
514
522
Incompatible ,
@@ -523,7 +531,8 @@ crate struct Matrix<'p, 'tcx> {
523
531
524
532
impl < ' p , ' tcx > Matrix < ' p , ' tcx > {
525
533
crate fn empty ( ) -> Self {
526
- // Use SpecializationCache::Incompatible as a placeholder; the initialization is in push().
534
+ // Use `SpecializationCache::Incompatible` as a placeholder; we will initialize it on the
535
+ // first call to `push`. See the first half of `update_cache`.
527
536
Matrix { patterns : vec ! [ ] , cache : SpecializationCache :: Incompatible }
528
537
}
529
538
@@ -536,47 +545,71 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
536
545
self . push ( row)
537
546
}
538
547
} else {
539
- if self . patterns . is_empty ( ) {
540
- self . cache = if row. is_empty ( ) {
541
- SpecializationCache :: Incompatible
542
- } else {
543
- match * row. head ( ) . kind {
544
- PatKind :: Variant { .. } => SpecializationCache :: Variants {
545
- lookup : FxHashMap :: default ( ) ,
546
- wilds : SmallVec :: new ( ) ,
547
- } ,
548
- // Note: If the first pattern is a wildcard, then all patterns after that is not
549
- // useful. The check is simple enough so we treat it as the same as unsupported
550
- // patterns.
551
- _ => SpecializationCache :: Incompatible ,
552
- }
553
- } ;
554
- }
555
- let idx_to_insert = self . patterns . len ( ) ;
556
- match & mut self . cache {
557
- SpecializationCache :: Variants { ref mut lookup, ref mut wilds } => {
558
- let head = row. head ( ) ;
559
- match * head. kind {
560
- _ if head. is_wildcard ( ) => {
561
- for ( _, v) in lookup. iter_mut ( ) {
562
- v. push ( idx_to_insert) ;
563
- }
564
- wilds. push ( idx_to_insert) ;
565
- }
566
- PatKind :: Variant { adt_def, variant_index, .. } => {
567
- lookup
568
- . entry ( adt_def. variants [ variant_index] . def_id )
569
- . or_insert_with ( || wilds. clone ( ) )
570
- . push ( idx_to_insert) ;
571
- }
572
- _ => {
573
- self . cache = SpecializationCache :: Incompatible ;
548
+ self . patterns . push ( row) ;
549
+ self . update_cache ( self . patterns . len ( ) - 1 ) ;
550
+ }
551
+ }
552
+
553
+ fn update_cache ( & mut self , idx : usize ) {
554
+ let row = & self . patterns [ idx] ;
555
+ // We don't know which kind of cache could be used until we see the first row; therefore an
556
+ // empty `Matrix` is initialized with `SpecializationCache::Empty`, then the cache is
557
+ // assigned the appropriate variant below on the first call to `push`.
558
+ if self . patterns . is_empty ( ) {
559
+ self . cache = if row. is_empty ( ) {
560
+ SpecializationCache :: Incompatible
561
+ } else {
562
+ match * row. head ( ) . kind {
563
+ PatKind :: Variant { .. } => SpecializationCache :: Variants {
564
+ lookup : FxHashMap :: default ( ) ,
565
+ wilds : SmallVec :: new ( ) ,
566
+ } ,
567
+ // Note: If the first pattern is a wildcard, then all patterns after that is not
568
+ // useful. The check is simple enough so we treat it as the same as unsupported
569
+ // patterns.
570
+ _ => SpecializationCache :: Incompatible ,
571
+ }
572
+ } ;
573
+ }
574
+ // Update the cache.
575
+ match & mut self . cache {
576
+ SpecializationCache :: Variants { ref mut lookup, ref mut wilds } => {
577
+ let head = row. head ( ) ;
578
+ match * head. kind {
579
+ _ if head. is_wildcard ( ) => {
580
+ // Per rule 1.3 in the top-level comments, a wildcard pattern is included in
581
+ // the result of `specialize_constructor` for *any* `Constructor`.
582
+ // We push the wildcard pattern to the precomputed result for constructors
583
+ // that we have seen before; results for constructors we have not yet seen
584
+ // defaults to `wilds`, which is updated right below.
585
+ for ( _, v) in lookup. iter_mut ( ) {
586
+ v. push ( idx) ;
574
587
}
588
+ // Per rule 2.1 and 2.2 in the top-level comments, only wildcard patterns
589
+ // are included in the result of `specialize_wildcard`.
590
+ // What we do here is to track the wildcards we have seen; so in addition to
591
+ // acting as the precomputed result of `specialize_wildcard`, `wilds` also
592
+ // serves as the default value of `specialize_constructor` for constructors
593
+ // that are not in `lookup`.
594
+ wilds. push ( idx) ;
595
+ }
596
+ PatKind :: Variant { adt_def, variant_index, .. } => {
597
+ // Handle the cases of rule 1.1 and 1.2 in the top-level comments.
598
+ // A variant pattern can only be included in the results of
599
+ // `specialize_constructor` for a particular constructor, therefore we are
600
+ // using a HashMap to track that.
601
+ lookup
602
+ . entry ( adt_def. variants [ variant_index] . def_id )
603
+ // Default to `wilds` for absent keys. See above for an explanation.
604
+ . or_insert_with ( || wilds. clone ( ) )
605
+ . push ( idx) ;
606
+ }
607
+ _ => {
608
+ self . cache = SpecializationCache :: Incompatible ;
575
609
}
576
610
}
577
- SpecializationCache :: Incompatible => { }
578
611
}
579
- self . patterns . push ( row ) ;
612
+ SpecializationCache :: Incompatible => { }
580
613
}
581
614
}
582
615
@@ -609,6 +642,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
609
642
if let Constructor :: Variant ( id) = constructor {
610
643
lookup
611
644
. get ( id)
645
+ // Default to `wilds` for absent keys. See `update_cache` for an explanation.
612
646
. unwrap_or ( & wilds)
613
647
. iter ( )
614
648
. filter_map ( |& i| {
0 commit comments