@@ -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