@@ -619,34 +619,45 @@ impl<'p, 'tcx> FromIterator<PatStack<'p, 'tcx>> for Matrix<'p, 'tcx> {
619
619
}
620
620
}
621
621
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:
629
633
/// ```
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) => {}
633
638
/// }
634
639
/// ```
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.
639
649
#[ derive( Debug , Clone ) ]
640
650
enum SubPatSet < ' p , ' tcx > {
651
+ /// The empty set. This means the pattern is unreachable.
652
+ Empty ,
641
653
/// The set containing the full pattern.
642
654
Full ,
643
- /// The empty set.
644
- Empty ,
645
655
/// 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.
647
658
Seq { subpats : FxHashMap < usize , SubPatSet < ' p , ' tcx > > } ,
648
659
/// 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.
650
661
Alt {
651
662
subpats : FxHashMap < usize , SubPatSet < ' p , ' tcx > > ,
652
663
/// Counts the total number of alternatives in the pattern
@@ -657,88 +668,91 @@ enum SubPatSet<'p, 'tcx> {
657
668
}
658
669
659
670
impl < ' p , ' tcx > SubPatSet < ' p , ' tcx > {
660
- fn empty ( ) -> Self {
661
- SubPatSet :: Empty
662
- }
663
671
fn full ( ) -> Self {
664
672
SubPatSet :: Full
665
673
}
674
+ fn empty ( ) -> Self {
675
+ SubPatSet :: Empty
676
+ }
666
677
667
- fn is_full ( & self ) -> bool {
678
+ fn is_empty ( & self ) -> bool {
668
679
match self {
669
- SubPatSet :: Full => true ,
670
- SubPatSet :: Empty => false ,
680
+ SubPatSet :: Empty => true ,
681
+ SubPatSet :: Full => false ,
671
682
// 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 ( ) ) ,
674
686
}
675
687
}
676
688
677
- fn is_empty ( & self ) -> bool {
689
+ fn is_full ( & self ) -> bool {
678
690
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.
682
696
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 ( ) )
684
698
}
685
699
}
686
700
}
687
701
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 ) {
690
704
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 ( ) {
693
707
return ;
694
- } else if self . is_full ( ) {
708
+ } else if self . is_empty ( ) {
695
709
* self = other;
696
710
return ;
697
- } else if other. is_empty ( ) {
698
- * self = Empty ;
711
+ } else if other. is_full ( ) {
712
+ * self = Full ;
699
713
return ;
700
714
}
701
715
702
716
match ( & mut * self , other) {
703
717
( 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, .. } ) => {
715
718
s_set. retain ( |i, s_sub_set| {
716
719
// Missing entries count as full.
717
720
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) ;
719
722
// We drop full entries.
720
723
!s_sub_set. is_full ( )
721
724
} ) ;
722
725
// 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.
724
738
s_set. extend ( o_set) ;
725
739
}
726
740
_ => bug ! ( ) ,
727
741
}
728
742
729
- if self . is_empty ( ) {
730
- * self = Empty ;
743
+ if self . is_full ( ) {
744
+ * self = Full ;
731
745
}
732
746
}
733
747
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 ()`.
738
752
fn fill_spans ( set : & SubPatSet < ' _ , ' _ > , spans : & mut Vec < Span > ) {
739
753
match set {
740
- SubPatSet :: Full => bug ! ( ) ,
741
- SubPatSet :: Empty => { }
754
+ SubPatSet :: Empty => bug ! ( ) ,
755
+ SubPatSet :: Full => { }
742
756
SubPatSet :: Seq { subpats } => {
743
757
for ( _, sub_set) in subpats {
744
758
fill_spans ( sub_set, spans) ;
@@ -747,8 +761,9 @@ impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
747
761
SubPatSet :: Alt { subpats, pat, alt_count, .. } => {
748
762
let expanded = pat. expand_or_pat ( ) ;
749
763
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.
752
767
spans. push ( expanded[ i] . span ) ;
753
768
} else {
754
769
fill_spans ( sub_set, spans) ;
@@ -758,10 +773,11 @@ impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
758
773
}
759
774
}
760
775
761
- if self . is_full ( ) {
776
+ if self . is_empty ( ) {
762
777
return None ;
763
778
}
764
- if self . is_empty ( ) {
779
+ if self . is_full ( ) {
780
+ // No subpatterns are unreachable.
765
781
return Some ( Vec :: new ( ) ) ;
766
782
}
767
783
let mut spans = Vec :: new ( ) ;
@@ -790,6 +806,7 @@ impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
790
806
new_subpats. insert ( i - arity + 1 , sub_set) ;
791
807
}
792
808
}
809
+ // If `new_subpats_first_col` has no entries it counts as full, so we can omit it.
793
810
if !new_subpats_first_col. is_empty ( ) {
794
811
new_subpats. insert ( 0 , Seq { subpats : new_subpats_first_col } ) ;
795
812
}
@@ -802,54 +819,58 @@ impl<'p, 'tcx> SubPatSet<'p, 'tcx> {
802
819
/// When `self` refers to a patstack that was obtained from splitting an or-pattern, after
803
820
/// running `unspecialize` it will refer to the original patstack before splitting.
804
821
///
805
- /// This case is subtle. Consider :
822
+ /// For example :
806
823
/// ```
807
824
/// match Some(true) {
808
825
/// Some(true) => {}
809
826
/// None | Some(true | false) => {}
810
827
/// }
811
828
/// ```
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.
820
832
fn unsplit_or_pat ( mut self , alt_id : usize , alt_count : usize , pat : & ' p Pat < ' tcx > ) -> Self {
821
833
use SubPatSet :: * ;
822
- if self . is_full ( ) {
823
- return Full ;
834
+ if self . is_empty ( ) {
835
+ return Empty ;
824
836
}
825
837
838
+ // Subpatterns coming from inside the or-pattern alternative itself, e.g. in `None | Some(0
839
+ // | 1)`.
826
840
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 ! ( ) ,
830
844
Alt { .. } => bug ! ( ) , // `self` is a patstack
831
845
} ;
832
846
let mut subpats_first_col = FxHashMap :: default ( ) ;
833
847
subpats_first_col. insert ( alt_id, set_first_col) ;
834
848
let set_first_col = Alt { subpats : subpats_first_col, pat, alt_count } ;
835
849
836
850
let mut subpats = match self {
837
- Empty => FxHashMap :: default ( ) ,
851
+ Full => FxHashMap :: default ( ) ,
838
852
Seq { subpats } => subpats,
839
- Full => unreachable ! ( ) ,
853
+ Empty => unreachable ! ( ) ,
840
854
Alt { .. } => bug ! ( ) , // `self` is a patstack
841
855
} ;
842
856
subpats. insert ( 0 , set_first_col) ;
843
857
Seq { subpats }
844
858
}
845
859
}
846
860
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`.
847
867
#[ derive( Clone , Debug ) ]
848
868
enum 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).
853
874
NoWitnesses ( SubPatSet < ' p , ' tcx > ) ,
854
875
/// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole
855
876
/// pattern is unreachable.
@@ -860,13 +881,13 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
860
881
fn new_useful ( preference : WitnessPreference ) -> Self {
861
882
match preference {
862
883
ConstructWitness => WithWitnesses ( vec ! [ Witness ( vec![ ] ) ] ) ,
863
- LeaveOutWitness => NoWitnesses ( SubPatSet :: empty ( ) ) ,
884
+ LeaveOutWitness => NoWitnesses ( SubPatSet :: full ( ) ) ,
864
885
}
865
886
}
866
887
fn new_not_useful ( preference : WitnessPreference ) -> Self {
867
888
match preference {
868
889
ConstructWitness => WithWitnesses ( vec ! [ ] ) ,
869
- LeaveOutWitness => NoWitnesses ( SubPatSet :: full ( ) ) ,
890
+ LeaveOutWitness => NoWitnesses ( SubPatSet :: empty ( ) ) ,
870
891
}
871
892
}
872
893
@@ -876,7 +897,7 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
876
897
( WithWitnesses ( _) , WithWitnesses ( o) ) if o. is_empty ( ) => { }
877
898
( WithWitnesses ( s) , WithWitnesses ( o) ) if s. is_empty ( ) => * self = WithWitnesses ( o) ,
878
899
( WithWitnesses ( s) , WithWitnesses ( o) ) => s. extend ( o) ,
879
- ( NoWitnesses ( s) , NoWitnesses ( o) ) => s. intersect ( o) ,
900
+ ( NoWitnesses ( s) , NoWitnesses ( o) ) => s. union ( o) ,
880
901
_ => unreachable ! ( ) ,
881
902
}
882
903
}
@@ -888,8 +909,8 @@ impl<'p, 'tcx> Usefulness<'p, 'tcx> {
888
909
for u in usefulnesses {
889
910
ret. extend ( u) ;
890
911
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.
893
914
return ret;
894
915
}
895
916
}
@@ -1098,8 +1119,7 @@ fn is_useful<'p, 'tcx>(
1098
1119
let v_head = v. head ( ) ;
1099
1120
let vs: Vec < _ > = v. expand_or_pat ( ) . collect ( ) ;
1100
1121
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.
1103
1123
let mut matrix = matrix. clone ( ) ;
1104
1124
let usefulnesses = vs. into_iter ( ) . enumerate ( ) . map ( |( i, v) | {
1105
1125
let usefulness =
@@ -1155,11 +1175,14 @@ crate struct MatchArm<'p, 'tcx> {
1155
1175
crate has_guard : bool ,
1156
1176
}
1157
1177
1178
+ /// Indicates whether or not a given arm is reachable.
1158
1179
#[ derive( Clone , Debug ) ]
1159
1180
crate 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.
1162
1184
Reachable ( Vec < Span > ) ,
1185
+ /// The arm is unreachable.
1163
1186
Unreachable ,
1164
1187
}
1165
1188
@@ -1195,8 +1218,10 @@ crate fn compute_match_usefulness<'p, 'tcx>(
1195
1218
matrix. push ( v) ;
1196
1219
}
1197
1220
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
+ }
1200
1225
WithWitnesses ( ..) => bug ! ( ) ,
1201
1226
} ;
1202
1227
( arm, reachability)
0 commit comments