@@ -311,7 +311,6 @@ use super::{Pat, PatKind};
311311use super :: { PatternFoldable , PatternFolder } ;
312312
313313use rustc_data_structures:: captures:: Captures ;
314- use rustc_data_structures:: fx:: FxHashSet ;
315314use rustc_data_structures:: sync:: OnceCell ;
316315
317316use rustc_arena:: TypedArena ;
@@ -626,11 +625,81 @@ impl<'p, 'tcx> FromIterator<PatStack<'p, 'tcx>> for Matrix<'p, 'tcx> {
626625 }
627626}
628627
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+
629698#[ derive( Clone , Debug ) ]
630699crate 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 ) ,
634703 /// Carries a list of witnesses of non-exhaustiveness.
635704 UsefulWithWitness ( Vec < Witness < ' tcx > > ) ,
636705 NotUseful ,
@@ -640,7 +709,7 @@ impl<'tcx> Usefulness<'tcx> {
640709 fn new_useful ( preference : WitnessPreference ) -> Self {
641710 match preference {
642711 ConstructWitness => UsefulWithWitness ( vec ! [ Witness ( vec![ ] ) ] ) ,
643- LeaveOutWitness => Useful ( vec ! [ ] ) ,
712+ LeaveOutWitness => Useful ( Default :: default ( ) ) ,
644713 }
645714 }
646715
@@ -650,58 +719,55 @@ impl<'tcx> Usefulness<'tcx> {
650719
651720 /// When trying several branches and each returns a `Usefulness`, we need to combine the
652721 /// 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.
660727 // ```
661728 // match (true, true) {
662729 // (true, true) => {}
663730 // (false | true, false | true) => {}
664731 // }
665732 // ```
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 .
669736 // ```
670737 // match None {
671738 // Some(false) => {}
672739 // None | Some(true | false) => {}
673740 // }
674741 // ```
675742
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 ( ) ;
682749
683- for ( u, span ) in usefulnesses {
750+ for ( u, branch_span ) in usefulnesses {
684751 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)
698760 }
699761 }
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+ }
704769 }
770+ NotUseful => unreachables_this_column. push ( branch_span) ,
705771 UsefulWithWitness ( _) => {
706772 bug ! (
707773 "encountered or-pat in the expansion of `_` during exhaustiveness checking"
@@ -710,13 +776,13 @@ impl<'tcx> Usefulness<'tcx> {
710776 }
711777 }
712778
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+ }
720786 Useful ( unreachables)
721787 } else {
722788 NotUseful
@@ -752,23 +818,6 @@ impl<'tcx> Usefulness<'tcx> {
752818 } ;
753819 UsefulWithWitness ( new_witnesses)
754820 }
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- }
772821 x => x,
773822 }
774823 }
@@ -926,7 +975,7 @@ fn is_useful<'p, 'tcx>(
926975 }
927976 ( u, span)
928977 } ) ;
929- Usefulness :: merge ( usefulnesses, v . len ( ) )
978+ Usefulness :: merge ( usefulnesses)
930979 } else {
931980 v. head_ctor ( cx)
932981 . split ( pcx, Some ( hir_id) )
0 commit comments