@@ -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,81 @@ 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
+ /// Operations on this do not need to be fast since it's only nonempty in the diagnostic path.
636
+ #[ derive( Debug , Clone , Default ) ]
637
+ pub ( crate ) struct SpanSet {
638
+ /// The minimal set of `Span`s required to represent the whole set. If A and B are `Span`s in
639
+ /// the `SpanSet`, and A is a descendant of B, then only B will be in `root_spans`.
640
+ /// Invariant: the spans are disjoint.
641
+ root_spans : Vec < Span > ,
642
+ }
643
+
644
+ impl SpanSet {
645
+ /// Creates an empty set.
646
+ fn new ( ) -> Self {
647
+ Self :: default ( )
648
+ }
649
+
650
+ /// Tests whether the set is empty.
651
+ pub ( crate ) fn is_empty ( & self ) -> bool {
652
+ self . root_spans . is_empty ( )
653
+ }
654
+
655
+ /// Iterate over the disjoint list of spans at the roots of this set.
656
+ pub ( crate ) fn iter < ' a > ( & ' a self ) -> impl Iterator < Item = Span > + Captures < ' a > {
657
+ self . root_spans . iter ( ) . copied ( )
658
+ }
659
+
660
+ /// Tests whether the set contains a given Span.
661
+ fn contains ( & self , span : Span ) -> bool {
662
+ self . iter ( ) . any ( |root_span| root_span. contains ( span) )
663
+ }
664
+
665
+ /// Add a span to the set if we know the span has no intersection in this set.
666
+ fn push_nonintersecting ( & mut self , new_span : Span ) {
667
+ self . root_spans . push ( new_span) ;
668
+ }
669
+
670
+ fn intersection_mut ( & mut self , other : & Self ) {
671
+ if self . is_empty ( ) || other. is_empty ( ) {
672
+ * self = Self :: new ( ) ;
673
+ return ;
674
+ }
675
+ // Those that were in `self` but not contained in `other`
676
+ let mut leftover = SpanSet :: new ( ) ;
677
+ // We keep the elements in `self` that are also in `other`.
678
+ self . root_spans . retain ( |span| {
679
+ let retain = other. contains ( * span) ;
680
+ if !retain {
681
+ leftover. root_spans . push ( * span) ;
682
+ }
683
+ retain
684
+ } ) ;
685
+ // We keep the elements in `other` that are also in the original `self`. You might think
686
+ // this is not needed because `self` already contains the intersection. But those aren't
687
+ // just sets of things. If `self = [a]`, `other = [b]` and `a` contains `b`, then `b`
688
+ // belongs in the intersection but we didn't catch it in the filtering above. We look at
689
+ // `leftover` instead of the full original `self` to avoid duplicates.
690
+ for span in other. iter ( ) {
691
+ if leftover. contains ( span) {
692
+ self . root_spans . push ( span) ;
693
+ }
694
+ }
695
+ }
696
+ }
697
+
629
698
#[ derive( Clone , Debug ) ]
630
699
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 > > ) ,
700
+ /// Pontentially carries a set of sub-branches that have been found to be unreachable. Used
701
+ /// only in the presence of or-patterns, otherwise it stays empty.
702
+ Useful ( SpanSet ) ,
634
703
/// Carries a list of witnesses of non-exhaustiveness.
635
704
UsefulWithWitness ( Vec < Witness < ' tcx > > ) ,
636
705
NotUseful ,
@@ -640,7 +709,7 @@ impl<'tcx> Usefulness<'tcx> {
640
709
fn new_useful ( preference : WitnessPreference ) -> Self {
641
710
match preference {
642
711
ConstructWitness => UsefulWithWitness ( vec ! [ Witness ( vec![ ] ) ] ) ,
643
- LeaveOutWitness => Useful ( vec ! [ ] ) ,
712
+ LeaveOutWitness => Useful ( Default :: default ( ) ) ,
644
713
}
645
714
}
646
715
@@ -650,58 +719,55 @@ impl<'tcx> Usefulness<'tcx> {
650
719
651
720
/// When trying several branches and each returns a `Usefulness`, we need to combine the
652
721
/// results together.
653
- fn merge ( usefulnesses : impl Iterator < Item = ( Self , Span ) > , column_count : usize ) -> Self {
654
- // If two branches have detected some unreachable sub-branches, we need to be careful. If
655
- // they were detected in columns that are not the current one, we want to keep only the
656
- // sub-branches that were unreachable in _all_ branches. Eg. in the following, the last
657
- // `true` is unreachable in the second branch of the first or-pattern, but not otherwise.
658
- // Therefore we don't want to lint that it is unreachable.
659
- //
722
+ fn merge ( usefulnesses : impl Iterator < Item = ( Self , Span ) > ) -> Self {
723
+ // If we have detected some unreachable sub-branches, we only want to keep them when they
724
+ // were unreachable in _all_ branches. Eg. in the following, the last `true` is unreachable
725
+ // in the second branch of the first or-pattern, but not otherwise. Therefore we don't want
726
+ // to lint that it is unreachable.
660
727
// ```
661
728
// match (true, true) {
662
729
// (true, true) => {}
663
730
// (false | true, false | true) => {}
664
731
// }
665
732
// ```
666
- // If however the sub-branches come from the current column, they come from the inside of
667
- // the current or-pattern, and we want to keep them all. Eg. in the following, we _do_ want
668
- // to lint that the last `false` is unreachable .
733
+ // Here however we _do_ want to lint that the last `false` is unreachable. So we don't want
734
+ // to intersect the spans that come directly from the or-pattern, since each branch of the
735
+ // or-pattern brings a new disjoint pattern .
669
736
// ```
670
737
// match None {
671
738
// Some(false) => {}
672
739
// None | Some(true | false) => {}
673
740
// }
674
741
// ```
675
742
676
- // We keep track of sub-branches separately depending on whether they come from this column
677
- // or from others .
678
- let mut unreachables_this_column : FxHashSet < Span > = FxHashSet :: default ( ) ;
679
- let mut unreachables_other_columns : Vec < FxHashSet < Span > > = Vec :: default ( ) ;
680
- // Whether at least one branch is reachable .
681
- let mut any_is_useful = false ;
743
+ // Is `None` when no branch was useful. Will often be `Some(Spanset::new())` because the
744
+ // sets are only non-empty in the diagnostic path .
745
+ let mut unreachables : Option < SpanSet > = None ;
746
+ // In case of or-patterns we don't want to intersect subpatterns that come from the first
747
+ // column. Invariant: contains a list of disjoint spans .
748
+ let mut unreachables_this_column = Vec :: new ( ) ;
682
749
683
- for ( u, span ) in usefulnesses {
750
+ for ( u, branch_span ) in usefulnesses {
684
751
match u {
685
- Useful ( unreachables) => {
686
- if let Some ( ( this_column, other_columns) ) = unreachables. split_last ( ) {
687
- // We keep the union of unreachables found in the first column.
688
- unreachables_this_column. extend ( this_column) ;
689
- // We keep the intersection of unreachables found in other columns.
690
- if unreachables_other_columns. is_empty ( ) {
691
- unreachables_other_columns = other_columns. to_vec ( ) ;
692
- } else {
693
- unreachables_other_columns = unreachables_other_columns
694
- . into_iter ( )
695
- . zip ( other_columns)
696
- . map ( |( x, y) | x. intersection ( & y) . copied ( ) . collect ( ) )
697
- . collect ( ) ;
752
+ Useful ( spans) if spans. is_empty ( ) => {
753
+ // Hot path: `spans` is only non-empty in the diagnostic path.
754
+ unreachables = Some ( SpanSet :: new ( ) ) ;
755
+ }
756
+ Useful ( spans) => {
757
+ for span in spans. iter ( ) {
758
+ if branch_span. contains ( span) {
759
+ unreachables_this_column. push ( span)
698
760
}
699
761
}
700
- any_is_useful = true ;
701
- }
702
- NotUseful => {
703
- unreachables_this_column. insert ( span) ;
762
+ if let Some ( set) = & mut unreachables {
763
+ if !set. is_empty ( ) {
764
+ set. intersection_mut ( & spans) ;
765
+ }
766
+ } else {
767
+ unreachables = Some ( spans) ;
768
+ }
704
769
}
770
+ NotUseful => unreachables_this_column. push ( branch_span) ,
705
771
UsefulWithWitness ( _) => {
706
772
bug ! (
707
773
"encountered or-pat in the expansion of `_` during exhaustiveness checking"
@@ -710,13 +776,13 @@ impl<'tcx> Usefulness<'tcx> {
710
776
}
711
777
}
712
778
713
- if any_is_useful {
714
- let mut unreachables = if unreachables_other_columns . is_empty ( ) {
715
- ( 0 ..column_count - 1 ) . map ( |_| FxHashSet :: default ( ) ) . collect ( )
716
- } else {
717
- unreachables_other_columns
718
- } ;
719
- unreachables . push ( unreachables_this_column ) ;
779
+ if let Some ( mut unreachables ) = unreachables {
780
+ for span in unreachables_this_column {
781
+ // `unreachables` contained no spans from the first column, and
782
+ // `unreachables_this_column` contains only disjoint spans. Therefore it is valid
783
+ // to call `push_nonintersecting`.
784
+ unreachables . push_nonintersecting ( span ) ;
785
+ }
720
786
Useful ( unreachables)
721
787
} else {
722
788
NotUseful
@@ -752,23 +818,6 @@ impl<'tcx> Usefulness<'tcx> {
752
818
} ;
753
819
UsefulWithWitness ( new_witnesses)
754
820
}
755
- Useful ( mut unreachables) => {
756
- if !unreachables. is_empty ( ) {
757
- // When we apply a constructor, there are `arity` columns of the matrix that
758
- // corresponded to its arguments. All the unreachables found in these columns
759
- // will, after `apply`, come from the first column. So we take the union of all
760
- // the corresponding sets and put them in the first column.
761
- // Note that `arity` may be 0, in which case we just push a new empty set.
762
- let len = unreachables. len ( ) ;
763
- let arity = ctor_wild_subpatterns. len ( ) ;
764
- let mut unioned = FxHashSet :: default ( ) ;
765
- for set in unreachables. drain ( ( len - arity) ..) {
766
- unioned. extend ( set)
767
- }
768
- unreachables. push ( unioned) ;
769
- }
770
- Useful ( unreachables)
771
- }
772
821
x => x,
773
822
}
774
823
}
@@ -926,7 +975,7 @@ fn is_useful<'p, 'tcx>(
926
975
}
927
976
( u, span)
928
977
} ) ;
929
- Usefulness :: merge ( usefulnesses, v . len ( ) )
978
+ Usefulness :: merge ( usefulnesses)
930
979
} else {
931
980
v. head_ctor ( cx)
932
981
. split ( pcx, Some ( hir_id) )
0 commit comments