139139//!
140140//! It is computed as follows. We look at the pattern `p_1` on top of the stack,
141141//! 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:
144144//! 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
146146//! stack.
147147//! D((r_1, p_2, .., p_n))
148148//! D((r_2, p_2, .., p_n))
@@ -509,6 +509,14 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
509509#[ derive( Clone , Debug ) ]
510510enum SpecializationCache {
511511 /// 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.
512520 Variants { lookup : FxHashMap < DefId , SmallVec < [ usize ; 1 ] > > , wilds : SmallVec < [ usize ; 1 ] > } ,
513521 /// Does not belong to the cases above, use the slow path.
514522 Incompatible ,
@@ -523,7 +531,8 @@ crate struct Matrix<'p, 'tcx> {
523531
524532impl < ' p , ' tcx > Matrix < ' p , ' tcx > {
525533 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`.
527536 Matrix { patterns : vec ! [ ] , cache : SpecializationCache :: Incompatible }
528537 }
529538
@@ -536,47 +545,71 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
536545 self . push ( row)
537546 }
538547 } 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) ;
574587 }
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 ;
575609 }
576610 }
577- SpecializationCache :: Incompatible => { }
578611 }
579- self . patterns . push ( row ) ;
612+ SpecializationCache :: Incompatible => { }
580613 }
581614 }
582615
@@ -609,6 +642,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
609642 if let Constructor :: Variant ( id) = constructor {
610643 lookup
611644 . get ( id)
645+ // Default to `wilds` for absent keys. See `update_cache` for an explanation.
612646 . unwrap_or ( & wilds)
613647 . iter ( )
614648 . filter_map ( |& i| {
0 commit comments