@@ -619,34 +619,45 @@ impl<'p, 'tcx> FromIterator<PatStack<'p, 'tcx>> for Matrix<'p, 'tcx> {
619619    } 
620620} 
621621
622- /// Given a pattern or a pattern-stack, this struct captures a set of its subpattern branches. We 
623- /// use that to track unreachable sub-patterns arising from or-patterns. In the absence of 
624- /// or-patterns this will always be either `Empty` or `Full`. 
625- /// We support a limited set of operations, so not all possible sets of subpatterns can be 
626- /// represented. That's ok, we only want the ones that make sense to capture unreachable 
627- /// subpatterns. 
628- /// What we're trying to do is illustrated by this: 
622+ /// Given a pattern or a pattern-stack, this struct captures a set of its subpatterns. We use that 
623+ /// to track reachable sub-patterns arising from or-patterns. In the absence of or-patterns this 
624+ /// will always be either `Empty` (the whole pattern is unreachable) or `Full` (the whole pattern 
625+ /// is reachable). When there are or-patterns, some subpatterns may be reachable while others 
626+ /// aren't. In this case the whole pattern still counts as reachable, but we will lint the 
627+ /// unreachable subpatterns. 
628+ /// 
629+ /// This supports a limited set of operations, so not all possible sets of subpatterns can be 
630+ /// represented. That's ok, we only want the ones that make sense for our usage. 
631+ /// 
632+ /// What we're doing is illustrated by this: 
629633/// ``` 
630- /// match (true, true) { 
631- ///     (true, true) => {} 
632- ///     (true | false, true | false) => {} 
634+ /// match (true, 0) { 
635+ ///     (true, 0) => {} 
636+ ///     (_, 1) => {} 
637+ ///     (true | false, 0 | 1) => {} 
633638/// } 
634639/// ``` 
635- /// When we try the alternatives of the first or-pattern, the last `true` is unreachable in the 
636- /// first alternative but no the other. So we don't want to report it as unreachable. Therefore we 
637- /// intersect sets of unreachable patterns coming from different alternatives in order to figure 
638- /// out which subpatterns are overall unreachable. 
640+ /// When we try the alternatives of the `true | false` or-pattern, the last `0` is reachable in the 
641+ /// `false` alternative but not the `true`. So overall it is reachable. By contrast, the last `1` 
642+ /// is not reachable in either alternative, so we want to signal this to the user. 
643+ /// Therefore we take the union of sets of reachable patterns coming from different alternatives in 
644+ /// order to figure out which subpatterns are overall reachable. 
645+ /// 
646+ /// Invariant: we try to construct the smallest representation we can. In particular if 
647+ /// `self.is_empty()` we ensure that `self` is `Empty`, and same with `Full`. This is not important 
648+ /// for correctness currently. 
639649#[ derive( Debug ,  Clone ) ]  
640650enum  SubPatSet < ' p ,  ' tcx >  { 
651+     /// The empty set. This means the pattern is unreachable. 
652+      Empty , 
641653    /// The set containing the full pattern. 
642654     Full , 
643-     /// The empty set. 
644-      Empty , 
645655    /// If the pattern is a pattern with a constructor or a pattern-stack, we store a set for each 
646-      /// of its subpatterns. Missing entries in the map are implicitly empty. 
656+      /// of its subpatterns. Missing entries in the map are implicitly full, because that's the 
657+      /// common case. 
647658     Seq  {  subpats :  FxHashMap < usize ,  SubPatSet < ' p ,  ' tcx > >  } , 
648659    /// If the pattern is an or-pattern, we store a set for each of its alternatives. Missing 
649-      /// entries in the map are implicitly full . Note: we always flatten nested or-patterns. 
660+      /// entries in the map are implicitly empty . Note: we always flatten nested or-patterns. 
650661     Alt  { 
651662        subpats :  FxHashMap < usize ,  SubPatSet < ' p ,  ' tcx > > , 
652663        /// Counts the total number of alternatives in the pattern 
@@ -657,88 +668,91 @@ enum SubPatSet<'p, 'tcx> {
657668} 
658669
659670impl < ' p ,  ' tcx >  SubPatSet < ' p ,  ' tcx >  { 
660-     fn  empty ( )  -> Self  { 
661-         SubPatSet :: Empty 
662-     } 
663671    fn  full ( )  -> Self  { 
664672        SubPatSet :: Full 
665673    } 
674+     fn  empty ( )  -> Self  { 
675+         SubPatSet :: Empty 
676+     } 
666677
667-     fn  is_full ( & self )  -> bool  { 
678+     fn  is_empty ( & self )  -> bool  { 
668679        match  self  { 
669-             SubPatSet :: Full  => true , 
670-             SubPatSet :: Empty  => false , 
680+             SubPatSet :: Empty  => true , 
681+             SubPatSet :: Full  => false , 
671682            // If any subpattern in a sequence is unreachable, the whole pattern is unreachable. 
672-             SubPatSet :: Seq  {  subpats }  => subpats. values ( ) . any ( |set| set. is_full ( ) ) , 
673-             SubPatSet :: Alt  {  subpats,  .. }  => subpats. values ( ) . all ( |set| set. is_full ( ) ) , 
683+             SubPatSet :: Seq  {  subpats }  => subpats. values ( ) . any ( |set| set. is_empty ( ) ) , 
684+             // An or-pattern is reachable if any of its alternatives is. 
685+             SubPatSet :: Alt  {  subpats,  .. }  => subpats. values ( ) . all ( |set| set. is_empty ( ) ) , 
674686        } 
675687    } 
676688
677-     fn  is_empty ( & self )  -> bool  { 
689+     fn  is_full ( & self )  -> bool  { 
678690        match  self  { 
679-             SubPatSet :: Full  => false , 
680-             SubPatSet :: Empty  => true , 
681-             SubPatSet :: Seq  {  subpats }  => subpats. values ( ) . all ( |sub_set| sub_set. is_empty ( ) ) , 
691+             SubPatSet :: Empty  => false , 
692+             SubPatSet :: Full  => true , 
693+             // The whole pattern is reachable only when all its alternatives are. 
694+             SubPatSet :: Seq  {  subpats }  => subpats. values ( ) . all ( |sub_set| sub_set. is_full ( ) ) , 
695+             // The whole or-pattern is reachable only when all its alternatives are. 
682696            SubPatSet :: Alt  {  subpats,  alt_count,  .. }  => { 
683-                 subpats. len ( )  == * alt_count && subpats. values ( ) . all ( |set| set. is_empty ( ) ) 
697+                 subpats. len ( )  == * alt_count && subpats. values ( ) . all ( |set| set. is_full ( ) ) 
684698            } 
685699        } 
686700    } 
687701
688-     /// Intersect  `self` with `other`, mutating `self`. 
689-      fn  intersect ( & mut  self ,  other :  Self )  { 
702+     /// Union  `self` with `other`, mutating `self`. 
703+      fn  union ( & mut  self ,  other :  Self )  { 
690704        use  SubPatSet :: * ; 
691-         // Intersecting  with empty  stays empty; intersecting  with full  changes nothing. 
692-         if  self . is_empty ( )  || other. is_full ( )  { 
705+         // Union  with full  stays full; union  with empty  changes nothing. 
706+         if  self . is_full ( )  || other. is_empty ( )  { 
693707            return ; 
694-         }  else  if  self . is_full ( )  { 
708+         }  else  if  self . is_empty ( )  { 
695709            * self  = other; 
696710            return ; 
697-         }  else  if  other. is_empty ( )  { 
698-             * self  = Empty ; 
711+         }  else  if  other. is_full ( )  { 
712+             * self  = Full ; 
699713            return ; 
700714        } 
701715
702716        match  ( & mut  * self ,  other)  { 
703717            ( Seq  {  subpats :  s_set } ,  Seq  {  subpats :  mut  o_set } )  => { 
704-                 s_set. retain ( |i,  s_sub_set| { 
705-                     // Missing entries count as empty. 
706-                     let  o_sub_set = o_set. remove ( & i) . unwrap_or ( Empty ) ; 
707-                     s_sub_set. intersect ( o_sub_set) ; 
708-                     // We drop empty entries. 
709-                     !s_sub_set. is_empty ( ) 
710-                 } ) ; 
711-                 // Everything left in `o_set` is missing from `s_set`, i.e. counts as empty. Since 
712-                 // intersecting with empty returns empty, we can drop those entries. 
713-             } 
714-             ( Alt  {  subpats :  s_set,  .. } ,  Alt  {  subpats :  mut  o_set,  .. } )  => { 
715718                s_set. retain ( |i,  s_sub_set| { 
716719                    // Missing entries count as full. 
717720                    let  o_sub_set = o_set. remove ( & i) . unwrap_or ( Full ) ; 
718-                     s_sub_set. intersect ( o_sub_set) ; 
721+                     s_sub_set. union ( o_sub_set) ; 
719722                    // We drop full entries. 
720723                    !s_sub_set. is_full ( ) 
721724                } ) ; 
722725                // Everything left in `o_set` is missing from `s_set`, i.e. counts as full. Since 
723-                 // intersecting with full changes nothing, we can take those entries as is. 
726+                 // unioning with full returns full, we can drop those entries. 
727+             } 
728+             ( Alt  {  subpats :  s_set,  .. } ,  Alt  {  subpats :  mut  o_set,  .. } )  => { 
729+                 s_set. retain ( |i,  s_sub_set| { 
730+                     // Missing entries count as empty. 
731+                     let  o_sub_set = o_set. remove ( & i) . unwrap_or ( Empty ) ; 
732+                     s_sub_set. union ( o_sub_set) ; 
733+                     // We drop empty entries. 
734+                     !s_sub_set. is_empty ( ) 
735+                 } ) ; 
736+                 // Everything left in `o_set` is missing from `s_set`, i.e. counts as empty. Since 
737+                 // unioning with empty changes nothing, we can take those entries as is. 
724738                s_set. extend ( o_set) ; 
725739            } 
726740            _ => bug ! ( ) , 
727741        } 
728742
729-         if  self . is_empty ( )  { 
730-             * self  = Empty ; 
743+         if  self . is_full ( )  { 
744+             * self  = Full ; 
731745        } 
732746    } 
733747
734-     /// Returns a list of the spans of the unreachable subpatterns. If `self` is full we return  
735-      /// `None`. 
736-      fn  to_spans ( & self )  -> Option < Vec < Span > >  { 
737-         /// Panics if `set.is_full ()`. 
748+     /// Returns a list of the spans of the unreachable subpatterns. If `self` is empty (i.e. the  
749+      /// whole pattern is unreachable) we return  `None`. 
750+      fn  list_unreachable_spans ( & self )  -> Option < Vec < Span > >  { 
751+         /// Panics if `set.is_empty ()`. 
738752         fn  fill_spans ( set :  & SubPatSet < ' _ ,  ' _ > ,  spans :  & mut  Vec < Span > )  { 
739753            match  set { 
740-                 SubPatSet :: Full  => bug ! ( ) , 
741-                 SubPatSet :: Empty  => { } 
754+                 SubPatSet :: Empty  => bug ! ( ) , 
755+                 SubPatSet :: Full  => { } 
742756                SubPatSet :: Seq  {  subpats }  => { 
743757                    for  ( _,  sub_set)  in  subpats { 
744758                        fill_spans ( sub_set,  spans) ; 
@@ -747,8 +761,9 @@ impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
747761                SubPatSet :: Alt  {  subpats,  pat,  alt_count,  .. }  => { 
748762                    let  expanded = pat. expand_or_pat ( ) ; 
749763                    for  i in  0 ..* alt_count { 
750-                         let  sub_set = subpats. get ( & i) . unwrap_or ( & SubPatSet :: Full ) ; 
751-                         if  sub_set. is_full ( )  { 
764+                         let  sub_set = subpats. get ( & i) . unwrap_or ( & SubPatSet :: Empty ) ; 
765+                         if  sub_set. is_empty ( )  { 
766+                             // Found a unreachable subpattern. 
752767                            spans. push ( expanded[ i] . span ) ; 
753768                        }  else  { 
754769                            fill_spans ( sub_set,  spans) ; 
@@ -758,10 +773,11 @@ impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
758773            } 
759774        } 
760775
761-         if  self . is_full ( )  { 
776+         if  self . is_empty ( )  { 
762777            return  None ; 
763778        } 
764-         if  self . is_empty ( )  { 
779+         if  self . is_full ( )  { 
780+             // No subpatterns are unreachable. 
765781            return  Some ( Vec :: new ( ) ) ; 
766782        } 
767783        let  mut  spans = Vec :: new ( ) ; 
@@ -790,6 +806,7 @@ impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
790806                        new_subpats. insert ( i - arity + 1 ,  sub_set) ; 
791807                    } 
792808                } 
809+                 // If `new_subpats_first_col` has no entries it counts as full, so we can omit it. 
793810                if  !new_subpats_first_col. is_empty ( )  { 
794811                    new_subpats. insert ( 0 ,  Seq  {  subpats :  new_subpats_first_col } ) ; 
795812                } 
@@ -802,54 +819,58 @@ impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
802819    /// When `self` refers to a patstack that was obtained from splitting an or-pattern, after 
803820     /// running `unspecialize` it will refer to the original patstack before splitting. 
804821     /// 
805-      /// This case is subtle. Consider : 
822+      /// For example : 
806823     /// ``` 
807824     /// match Some(true) { 
808825     ///     Some(true) => {} 
809826     ///     None | Some(true | false) => {} 
810827     /// } 
811828     /// ``` 
812-      /// Imagine we naively preserved the sets of unreachable subpatterns. Here `None` would return 
813-      /// the empty set and `Some(true | false)` would return the set containing `true`. Intersecting 
814-      /// those two would return the empty set, so we'd miss that the last `true` is unreachable. 
815-      /// To fix that, when specializing a given alternative of an or-pattern, we consider all other 
816-      /// alternatives as unreachable. That way, intersecting the results will not unduly discard 
817-      /// unreachable subpatterns coming from the other alternatives. This is what this function does 
818-      /// (remember that missing entries in the `Alt` case count as full; in other words alternatives 
819-      /// other than `alt_id` count as unreachable). 
829+      /// Here `None` would return the full set and `Some(true | false)` would return the set 
830+      /// containing `false`. After `unsplit_or_pat`, we want the set to contain `None` and `false`. 
831+      /// This is what this function does. 
820832     fn  unsplit_or_pat ( mut  self ,  alt_id :  usize ,  alt_count :  usize ,  pat :  & ' p  Pat < ' tcx > )  -> Self  { 
821833        use  SubPatSet :: * ; 
822-         if  self . is_full ( )  { 
823-             return  Full ; 
834+         if  self . is_empty ( )  { 
835+             return  Empty ; 
824836        } 
825837
838+         // Subpatterns coming from inside the or-pattern alternative itself, e.g. in `None | Some(0 
839+         // | 1)`. 
826840        let  set_first_col = match  & mut  self  { 
827-             Empty  => Empty , 
828-             Seq  {  subpats }  => subpats. remove ( & 0 ) . unwrap_or ( Empty ) , 
829-             Full  => unreachable ! ( ) , 
841+             Full  => Full , 
842+             Seq  {  subpats }  => subpats. remove ( & 0 ) . unwrap_or ( Full ) , 
843+             Empty  => unreachable ! ( ) , 
830844            Alt  {  .. }  => bug ! ( ) ,  // `self` is a patstack 
831845        } ; 
832846        let  mut  subpats_first_col = FxHashMap :: default ( ) ; 
833847        subpats_first_col. insert ( alt_id,  set_first_col) ; 
834848        let  set_first_col = Alt  {  subpats :  subpats_first_col,  pat,  alt_count } ; 
835849
836850        let  mut  subpats = match  self  { 
837-             Empty  => FxHashMap :: default ( ) , 
851+             Full  => FxHashMap :: default ( ) , 
838852            Seq  {  subpats }  => subpats, 
839-             Full  => unreachable ! ( ) , 
853+             Empty  => unreachable ! ( ) , 
840854            Alt  {  .. }  => bug ! ( ) ,  // `self` is a patstack 
841855        } ; 
842856        subpats. insert ( 0 ,  set_first_col) ; 
843857        Seq  {  subpats } 
844858    } 
845859} 
846860
861+ /// This carries the results of computing usefulness, as described at the top of the file. When 
862+ /// checking usefulness of a match branch, we use the `NoWitnesses` variant, which also keeps track 
863+ /// of potential unreachable sub-patterns (in the presence of or-patterns). When checking 
864+ /// exhaustiveness of a whole match, we use the `WithWitnesses` variant, which carries a list of 
865+ /// witnesses of non-exhaustiveness when there are any. 
866+ /// Which variant to use is dictated by `WitnessPreference`. 
847867#[ derive( Clone ,  Debug ) ]  
848868enum  Usefulness < ' p ,  ' tcx >  { 
849-     /// Carries a set of subpatterns that have been found to be unreachable. If full, this 
850-      /// indicates the whole pattern is unreachable. If not, this indicates that the pattern is 
851-      /// reachable but has some unreachable sub-patterns (due to or-patterns). In the absence of 
852-      /// or-patterns, this is either `Empty` or `Full`. 
869+     /// Carries a set of subpatterns that have been found to be reachable. If empty, this indicates 
870+      /// the whole pattern is unreachable. If not, this indicates that the pattern is reachable but 
871+      /// that some sub-patterns may be unreachable (due to or-patterns). In the absence of 
872+      /// or-patterns this will always be either `Empty` (the whole pattern is unreachable) or `Full` 
873+      /// (the whole pattern is reachable). 
853874     NoWitnesses ( SubPatSet < ' p ,  ' tcx > ) , 
854875    /// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole 
855876     /// pattern is unreachable. 
@@ -860,13 +881,13 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
860881    fn  new_useful ( preference :  WitnessPreference )  -> Self  { 
861882        match  preference { 
862883            ConstructWitness  => WithWitnesses ( vec ! [ Witness ( vec![ ] ) ] ) , 
863-             LeaveOutWitness  => NoWitnesses ( SubPatSet :: empty ( ) ) , 
884+             LeaveOutWitness  => NoWitnesses ( SubPatSet :: full ( ) ) , 
864885        } 
865886    } 
866887    fn  new_not_useful ( preference :  WitnessPreference )  -> Self  { 
867888        match  preference { 
868889            ConstructWitness  => WithWitnesses ( vec ! [ ] ) , 
869-             LeaveOutWitness  => NoWitnesses ( SubPatSet :: full ( ) ) , 
890+             LeaveOutWitness  => NoWitnesses ( SubPatSet :: empty ( ) ) , 
870891        } 
871892    } 
872893
@@ -876,7 +897,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
876897            ( WithWitnesses ( _) ,  WithWitnesses ( o) )  if  o. is_empty ( )  => { } 
877898            ( WithWitnesses ( s) ,  WithWitnesses ( o) )  if  s. is_empty ( )  => * self  = WithWitnesses ( o) , 
878899            ( WithWitnesses ( s) ,  WithWitnesses ( o) )  => s. extend ( o) , 
879-             ( NoWitnesses ( s) ,  NoWitnesses ( o) )  => s. intersect ( o) , 
900+             ( NoWitnesses ( s) ,  NoWitnesses ( o) )  => s. union ( o) , 
880901            _ => unreachable ! ( ) , 
881902        } 
882903    } 
@@ -888,8 +909,8 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
888909        for  u in  usefulnesses { 
889910            ret. extend ( u) ; 
890911            if  let  NoWitnesses ( subpats)  = & ret { 
891-                 if  subpats. is_empty ( )  { 
892-                     // Once we reach the empty  set, more intersections  won't change the result. 
912+                 if  subpats. is_full ( )  { 
913+                     // Once we reach the full  set, more unions  won't change the result. 
893914                    return  ret; 
894915                } 
895916            } 
@@ -1098,8 +1119,7 @@ fn is_useful<'p, 'tcx>(
10981119        let  v_head = v. head ( ) ; 
10991120        let  vs:  Vec < _ >  = v. expand_or_pat ( ) . collect ( ) ; 
11001121        let  alt_count = vs. len ( ) ; 
1101-         // We expand the or pattern, trying each of its branches in turn and keeping careful track 
1102-         // of possible unreachable sub-branches. 
1122+         // We try each or-pattern branch in turn. 
11031123        let  mut  matrix = matrix. clone ( ) ; 
11041124        let  usefulnesses = vs. into_iter ( ) . enumerate ( ) . map ( |( i,  v) | { 
11051125            let  usefulness =
@@ -1155,11 +1175,14 @@ crate struct MatchArm<'p, 'tcx> {
11551175    crate  has_guard :  bool , 
11561176} 
11571177
1178+ /// Indicates whether or not a given arm is reachable. 
11581179#[ derive( Clone ,  Debug ) ]  
11591180crate  enum  Reachability  { 
1160-     /// Potentially carries a set of sub-branches that have been found to be unreachable. Used only 
1161-      /// in the presence of or-patterns, otherwise it stays empty. 
1181+     /// The arm is reachable. This additionally carries a set of or-pattern branches that have been 
1182+      /// found to be unreachable despite the overall arm being reachable. Used only in the presence 
1183+      /// of or-patterns, otherwise it stays empty. 
11621184     Reachable ( Vec < Span > ) , 
1185+     /// The arm is unreachable. 
11631186     Unreachable , 
11641187} 
11651188
@@ -1195,8 +1218,10 @@ crate fn compute_match_usefulness<'p, 'tcx>(
11951218                matrix. push ( v) ; 
11961219            } 
11971220            let  reachability = match  usefulness { 
1198-                 NoWitnesses ( subpats)  if  subpats. is_full ( )  => Reachability :: Unreachable , 
1199-                 NoWitnesses ( subpats)  => Reachability :: Reachable ( subpats. to_spans ( ) . unwrap ( ) ) , 
1221+                 NoWitnesses ( subpats)  if  subpats. is_empty ( )  => Reachability :: Unreachable , 
1222+                 NoWitnesses ( subpats)  => { 
1223+                     Reachability :: Reachable ( subpats. list_unreachable_spans ( ) . unwrap ( ) ) 
1224+                 } 
12001225                WithWitnesses ( ..)  => bug ! ( ) , 
12011226            } ; 
12021227            ( arm,  reachability) 
0 commit comments