@@ -311,7 +311,6 @@ use super::{Pat, PatKind};
311
311
use super :: { PatternFoldable , PatternFolder } ;
312
312
313
313
use rustc_data_structures:: captures:: Captures ;
314
- use rustc_data_structures:: fx:: FxHashSet ;
315
314
use rustc_data_structures:: sync:: OnceCell ;
316
315
317
316
use rustc_arena:: TypedArena ;
@@ -626,11 +625,82 @@ impl<'p, 'tcx> FromIterator<PatStack<'p, 'tcx>> for Matrix<'p, 'tcx> {
626
625
}
627
626
}
628
627
628
+ /// Represents a set of `Span`s closed under the containment relation. That is, if a `Span` is
629
+ /// contained in the set then all `Span`s contained in it are also implicitly contained in the set.
630
+ /// In particular this means that when intersecting two sets, taking the intersection of some span
631
+ /// and one of its subspans returns the subspan, whereas a simple `HashSet` would have returned an
632
+ /// empty intersection.
633
+ /// It is assumed that two spans don't overlap without one being contained in the other; in other
634
+ /// words, that the inclusion structure forms a tree and not a DAG.
635
+ /// Intersection is not very efficient. It compares everything pairwise. If needed it could be made
636
+ /// faster by sorting the `Span`s and merging cleverly.
637
+ #[ derive( Debug , Clone , Default ) ]
638
+ pub ( crate ) struct SpanSet {
639
+ /// The minimal set of `Span`s required to represent the whole set. If A and B are `Span`s in
640
+ /// the `SpanSet`, and A is a descendant of B, then only B will be in `root_spans`.
641
+ /// Invariant: the spans are disjoint.
642
+ root_spans : Vec < Span > ,
643
+ }
644
+
645
+ impl SpanSet {
646
+ /// Creates an empty set.
647
+ fn new ( ) -> Self {
648
+ Self :: default ( )
649
+ }
650
+
651
+ /// Tests whether the set is empty.
652
+ pub ( crate ) fn is_empty ( & self ) -> bool {
653
+ self . root_spans . is_empty ( )
654
+ }
655
+
656
+ /// Iterate over the disjoint list of spans at the roots of this set.
657
+ pub ( crate ) fn iter < ' a > ( & ' a self ) -> impl Iterator < Item = Span > + Captures < ' a > {
658
+ self . root_spans . iter ( ) . copied ( )
659
+ }
660
+
661
+ /// Tests whether the set contains a given Span.
662
+ fn contains ( & self , span : Span ) -> bool {
663
+ self . iter ( ) . any ( |root_span| root_span. contains ( span) )
664
+ }
665
+
666
+ /// Add a span to the set if we know the span has no intersection in this set.
667
+ fn push_nonintersecting ( & mut self , new_span : Span ) {
668
+ self . root_spans . push ( new_span) ;
669
+ }
670
+
671
+ fn intersection_mut ( & mut self , other : & Self ) {
672
+ if self . is_empty ( ) || other. is_empty ( ) {
673
+ * self = Self :: new ( ) ;
674
+ return ;
675
+ }
676
+ // Those that were in `self` but not contained in `other`
677
+ let mut leftover = SpanSet :: new ( ) ;
678
+ // We keep the elements in `self` that are also in `other`.
679
+ self . root_spans . retain ( |span| {
680
+ let retain = other. contains ( * span) ;
681
+ if !retain {
682
+ leftover. root_spans . push ( * span) ;
683
+ }
684
+ retain
685
+ } ) ;
686
+ // We keep the elements in `other` that are also in the original `self`. You might think
687
+ // this is not needed because `self` already contains the intersection. But those aren't
688
+ // just sets of things. If `self = [a]`, `other = [b]` and `a` contains `b`, then `b`
689
+ // belongs in the intersection but we didn't catch it in the filtering above. We look at
690
+ // `leftover` instead of the full original `self` to avoid duplicates.
691
+ for span in other. iter ( ) {
692
+ if leftover. contains ( span) {
693
+ self . root_spans . push ( span) ;
694
+ }
695
+ }
696
+ }
697
+ }
698
+
629
699
#[ derive( Clone , Debug ) ]
630
700
crate enum Usefulness < ' tcx > {
631
- /// Carries, for each column in the matrix, a set of sub-branches that have been found to be
632
- /// unreachable. Used only in the presence of or-patterns, otherwise it stays empty.
633
- Useful ( Vec < FxHashSet < Span > > ) ,
701
+ /// Pontentially carries a set of sub-branches that have been found to be unreachable. Used
702
+ /// only in the presence of or-patterns, otherwise it stays empty.
703
+ Useful ( SpanSet ) ,
634
704
/// Carries a list of witnesses of non-exhaustiveness.
635
705
UsefulWithWitness ( Vec < Witness < ' tcx > > ) ,
636
706
NotUseful ,
@@ -640,14 +710,97 @@ impl<'tcx> Usefulness<'tcx> {
640
710
fn new_useful ( preference : WitnessPreference ) -> Self {
641
711
match preference {
642
712
ConstructWitness => UsefulWithWitness ( vec ! [ Witness ( vec![ ] ) ] ) ,
643
- LeaveOutWitness => Useful ( vec ! [ ] ) ,
713
+ LeaveOutWitness => Useful ( Default :: default ( ) ) ,
644
714
}
645
715
}
646
716
647
- fn is_useful ( & self ) -> bool {
648
- !matches ! ( * self , NotUseful )
717
+ /// When trying several branches and each returns a `Usefulness`, we need to combine the
718
+ /// results together.
719
+ fn merge ( usefulnesses : impl Iterator < Item = Self > ) -> Self {
720
+ // If we have detected some unreachable sub-branches, we only want to keep them when they
721
+ // were unreachable in _all_ branches. Eg. in the following, the last `true` is unreachable
722
+ // in the second branch of the first or-pattern, but not otherwise. Therefore we don't want
723
+ // to lint that it is unreachable.
724
+ // ```
725
+ // match (true, true) {
726
+ // (true, true) => {}
727
+ // (false | true, false | true) => {}
728
+ // }
729
+ // ```
730
+ // Here however we _do_ want to lint that the last `false` is unreachable. So we don't want
731
+ // to intersect the spans that come directly from the or-pattern, since each branch of the
732
+ // or-pattern brings a new disjoint pattern.
733
+ // ```
734
+ // match None {
735
+ // Some(false) => {}
736
+ // None | Some(true | false) => {}
737
+ // }
738
+ // ```
739
+
740
+ // Is `None` when no branch was useful. Will often be `Some(Spanset::new())` because the
741
+ // sets are only non-empty in the presence of or-patterns.
742
+ let mut unreachables: Option < SpanSet > = None ;
743
+ // Witnesses of usefulness, if any.
744
+ let mut witnesses = Vec :: new ( ) ;
745
+
746
+ for u in usefulnesses {
747
+ match u {
748
+ Useful ( spans) if spans. is_empty ( ) => {
749
+ // Once we reach the empty set, more intersections won't change the result.
750
+ return Useful ( SpanSet :: new ( ) ) ;
751
+ }
752
+ Useful ( spans) => {
753
+ if let Some ( unreachables) = & mut unreachables {
754
+ if !unreachables. is_empty ( ) {
755
+ unreachables. intersection_mut ( & spans) ;
756
+ }
757
+ if unreachables. is_empty ( ) {
758
+ return Useful ( SpanSet :: new ( ) ) ;
759
+ }
760
+ } else {
761
+ unreachables = Some ( spans) ;
762
+ }
763
+ }
764
+ NotUseful => { }
765
+ UsefulWithWitness ( wits) => {
766
+ witnesses. extend ( wits) ;
767
+ }
768
+ }
769
+ }
770
+
771
+ if !witnesses. is_empty ( ) {
772
+ UsefulWithWitness ( witnesses)
773
+ } else if let Some ( unreachables) = unreachables {
774
+ Useful ( unreachables)
775
+ } else {
776
+ NotUseful
777
+ }
649
778
}
650
779
780
+ /// After calculating the usefulness for a branch of an or-pattern, call this to make this
781
+ /// usefulness mergeable with those from the other branches.
782
+ fn unsplit_or_pat ( self , this_span : Span , or_pat_spans : & [ Span ] ) -> Self {
783
+ match self {
784
+ Useful ( mut spans) => {
785
+ // We register the spans of the other branches of this or-pattern as being
786
+ // unreachable from this one. This ensures that intersecting together the sets of
787
+ // spans returns what we want.
788
+ // Until we optimize `SpanSet` however, intersecting this entails a number of
789
+ // comparisons quadratic in the number of branches.
790
+ for & span in or_pat_spans {
791
+ if span != this_span {
792
+ spans. push_nonintersecting ( span) ;
793
+ }
794
+ }
795
+ Useful ( spans)
796
+ }
797
+ x => x,
798
+ }
799
+ }
800
+
801
+ /// After calculating usefulness after a specialization, call this to recontruct a usefulness
802
+ /// that makes sense for the matrix pre-specialization. This new usefulness can then be merged
803
+ /// with the results of specializing with the other constructors.
651
804
fn apply_constructor < ' p > (
652
805
self ,
653
806
pcx : PatCtxt < ' _ , ' p , ' tcx > ,
@@ -677,23 +830,6 @@ impl<'tcx> Usefulness<'tcx> {
677
830
} ;
678
831
UsefulWithWitness ( new_witnesses)
679
832
}
680
- Useful ( mut unreachables) => {
681
- if !unreachables. is_empty ( ) {
682
- // When we apply a constructor, there are `arity` columns of the matrix that
683
- // corresponded to its arguments. All the unreachables found in these columns
684
- // will, after `apply`, come from the first column. So we take the union of all
685
- // the corresponding sets and put them in the first column.
686
- // Note that `arity` may be 0, in which case we just push a new empty set.
687
- let len = unreachables. len ( ) ;
688
- let arity = ctor_wild_subpatterns. len ( ) ;
689
- let mut unioned = FxHashSet :: default ( ) ;
690
- for set in unreachables. drain ( ( len - arity) ..) {
691
- unioned. extend ( set)
692
- }
693
- unreachables. push ( unioned) ;
694
- }
695
- Useful ( unreachables)
696
- }
697
833
x => x,
698
834
}
699
835
}
@@ -829,112 +965,47 @@ fn is_useful<'p, 'tcx>(
829
965
830
966
assert ! ( rows. iter( ) . all( |r| r. len( ) == v. len( ) ) ) ;
831
967
968
+ // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
969
+ let ty = matrix. heads ( ) . next ( ) . map ( |r| r. ty ) . unwrap_or ( v. head ( ) . ty ) ;
970
+ let pcx = PatCtxt { cx, matrix, ty, span : v. head ( ) . span , is_top_level } ;
971
+
972
+ debug ! ( "is_useful_expand_first_col: ty={:#?}, expanding {:#?}" , pcx. ty, v. head( ) ) ;
973
+
832
974
// If the first pattern is an or-pattern, expand it.
833
- if let Some ( vs) = v. expand_or_pat ( ) {
975
+ let ret = if let Some ( vs) = v. expand_or_pat ( ) {
976
+ let subspans: Vec < _ > = vs. iter ( ) . map ( |v| v. head ( ) . span ) . collect ( ) ;
834
977
// We expand the or pattern, trying each of its branches in turn and keeping careful track
835
978
// of possible unreachable sub-branches.
836
- //
837
- // If two branches have detected some unreachable sub-branches, we need to be careful. If
838
- // they were detected in columns that are not the current one, we want to keep only the
839
- // sub-branches that were unreachable in _all_ branches. Eg. in the following, the last
840
- // `true` is unreachable in the second branch of the first or-pattern, but not otherwise.
841
- // Therefore we don't want to lint that it is unreachable.
842
- //
843
- // ```
844
- // match (true, true) {
845
- // (true, true) => {}
846
- // (false | true, false | true) => {}
847
- // }
848
- // ```
849
- // If however the sub-branches come from the current column, they come from the inside of
850
- // the current or-pattern, and we want to keep them all. Eg. in the following, we _do_ want
851
- // to lint that the last `false` is unreachable.
852
- // ```
853
- // match None {
854
- // Some(false) => {}
855
- // None | Some(true | false) => {}
856
- // }
857
- // ```
858
-
859
979
let mut matrix = matrix. clone ( ) ;
860
- // We keep track of sub-branches separately depending on whether they come from this column
861
- // or from others.
862
- let mut unreachables_this_column: FxHashSet < Span > = FxHashSet :: default ( ) ;
863
- let mut unreachables_other_columns: Vec < FxHashSet < Span > > = Vec :: default ( ) ;
864
- // Whether at least one branch is reachable.
865
- let mut any_is_useful = false ;
866
-
867
- for v in vs {
868
- let res = is_useful ( cx, & matrix, & v, witness_preference, hir_id, is_under_guard, false ) ;
869
- match res {
870
- Useful ( unreachables) => {
871
- if let Some ( ( this_column, other_columns) ) = unreachables. split_last ( ) {
872
- // We keep the union of unreachables found in the first column.
873
- unreachables_this_column. extend ( this_column) ;
874
- // We keep the intersection of unreachables found in other columns.
875
- if unreachables_other_columns. is_empty ( ) {
876
- unreachables_other_columns = other_columns. to_vec ( ) ;
877
- } else {
878
- unreachables_other_columns = unreachables_other_columns
879
- . into_iter ( )
880
- . zip ( other_columns)
881
- . map ( |( x, y) | x. intersection ( & y) . copied ( ) . collect ( ) )
882
- . collect ( ) ;
883
- }
884
- }
885
- any_is_useful = true ;
886
- }
887
- NotUseful => {
888
- unreachables_this_column. insert ( v. head ( ) . span ) ;
889
- }
890
- UsefulWithWitness ( _) => bug ! (
891
- "encountered or-pat in the expansion of `_` during exhaustiveness checking"
892
- ) ,
893
- }
894
-
980
+ let usefulnesses = vs. into_iter ( ) . map ( |v| {
981
+ let v_span = v. head ( ) . span ;
982
+ let usefulness =
983
+ is_useful ( cx, & matrix, & v, witness_preference, hir_id, is_under_guard, false ) ;
895
984
// If pattern has a guard don't add it to the matrix.
896
985
if !is_under_guard {
897
986
// We push the already-seen patterns into the matrix in order to detect redundant
898
987
// branches like `Some(_) | Some(0)`.
899
988
matrix. push ( v) ;
900
989
}
901
- }
902
-
903
- return if any_is_useful {
904
- let mut unreachables = if unreachables_other_columns. is_empty ( ) {
905
- let n_columns = v. len ( ) ;
906
- ( 0 ..n_columns - 1 ) . map ( |_| FxHashSet :: default ( ) ) . collect ( )
907
- } else {
908
- unreachables_other_columns
909
- } ;
910
- unreachables. push ( unreachables_this_column) ;
911
- Useful ( unreachables)
912
- } else {
913
- NotUseful
914
- } ;
915
- }
916
-
917
- // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).
918
- let ty = matrix. heads ( ) . next ( ) . map ( |r| r. ty ) . unwrap_or ( v. head ( ) . ty ) ;
919
- let pcx = PatCtxt { cx, matrix, ty, span : v. head ( ) . span , is_top_level } ;
920
-
921
- debug ! ( "is_useful_expand_first_col: ty={:#?}, expanding {:#?}" , pcx. ty, v. head( ) ) ;
922
-
923
- let ret = v
924
- . head_ctor ( cx)
925
- . split ( pcx, Some ( hir_id) )
926
- . into_iter ( )
927
- . map ( |ctor| {
990
+ usefulness. unsplit_or_pat ( v_span, & subspans)
991
+ } ) ;
992
+ Usefulness :: merge ( usefulnesses)
993
+ } else {
994
+ // We split the head constructor of `v`.
995
+ let ctors = v. head_ctor ( cx) . split ( pcx, Some ( hir_id) ) ;
996
+ // For each constructor, we compute whether there's a value that starts with it that would
997
+ // witness the usefulness of `v`.
998
+ let usefulnesses = ctors. into_iter ( ) . map ( |ctor| {
928
999
// We cache the result of `Fields::wildcards` because it is used a lot.
929
1000
let ctor_wild_subpatterns = Fields :: wildcards ( pcx, & ctor) ;
930
1001
let matrix = pcx. matrix . specialize_constructor ( pcx, & ctor, & ctor_wild_subpatterns) ;
931
1002
let v = v. pop_head_constructor ( & ctor_wild_subpatterns) ;
932
1003
let usefulness =
933
1004
is_useful ( pcx. cx , & matrix, & v, witness_preference, hir_id, is_under_guard, false ) ;
934
1005
usefulness. apply_constructor ( pcx, & ctor, & ctor_wild_subpatterns)
935
- } )
936
- . find ( |result| result . is_useful ( ) )
937
- . unwrap_or ( NotUseful ) ;
1006
+ } ) ;
1007
+ Usefulness :: merge ( usefulnesses )
1008
+ } ;
938
1009
debug ! ( "is_useful::returns({:#?}, {:#?}) = {:?}" , matrix, v, ret) ;
939
1010
ret
940
1011
}
0 commit comments