462
462
//! # Or-patterns
463
463
//!
464
464
//! What we have described so far works well if there are no or-patterns. To handle them, if the
465
- //! first pattern of a row in the matrix is an or-pattern, we expand it by duplicating the rest of
466
- //! the row as necessary. This is handled automatically in [`Matrix`].
465
+ //! first pattern of any row in the matrix is an or-pattern, we expand it by duplicating the rest of
466
+ //! the row as necessary. For code reuse, this is implemented as "specializing with the `Or`
467
+ //! constructor".
467
468
//!
468
469
//! This makes usefulness tracking subtle, because we also want to compute whether an alternative of
469
470
//! an or-pattern is redundant, e.g. in `Some(_) | Some(0)`. We therefore track usefulness of each
@@ -875,6 +876,11 @@ impl<Cx: PatCx> PlaceInfo<Cx> {
875
876
return Ok ( ( smallvec ! [ Constructor :: PrivateUninhabited ] , vec ! [ ] ) ) ;
876
877
}
877
878
879
+ if ctors. clone ( ) . any ( |c| matches ! ( c, Constructor :: Or ) ) {
880
+ // If any constructor is `Or`, we expand or-patterns.
881
+ return Ok ( ( smallvec ! [ Constructor :: Or ] , vec ! [ ] ) ) ;
882
+ }
883
+
878
884
let ctors_for_ty = cx. ctors_for_ty ( & self . ty ) ?;
879
885
debug ! ( ?ctors_for_ty) ;
880
886
@@ -968,10 +974,6 @@ impl<'p, Cx: PatCx> PatStack<'p, Cx> {
968
974
PatStack { pats : smallvec ! [ PatOrWild :: Pat ( pat) ] , relevant : true }
969
975
}
970
976
971
- fn is_empty ( & self ) -> bool {
972
- self . pats . is_empty ( )
973
- }
974
-
975
977
fn len ( & self ) -> usize {
976
978
self . pats . len ( )
977
979
}
@@ -984,10 +986,10 @@ impl<'p, Cx: PatCx> PatStack<'p, Cx> {
984
986
self . pats . iter ( ) . copied ( )
985
987
}
986
988
987
- // Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is
988
- // an or-pattern. Panics if `self` is empty.
989
+ // Expand the first or-pattern into its subpatterns. Only useful if the pattern is an
990
+ // or-pattern. Panics if `self` is empty.
989
991
fn expand_or_pat ( & self ) -> impl Iterator < Item = PatStack < ' p , Cx > > + Captures < ' _ > {
990
- self . head ( ) . flatten_or_pat ( ) . into_iter ( ) . map ( move |pat| {
992
+ self . head ( ) . expand_or_pat ( ) . into_iter ( ) . map ( move |pat| {
991
993
let mut new = self . clone ( ) ;
992
994
new. pats [ 0 ] = pat;
993
995
new
@@ -1057,10 +1059,6 @@ struct MatrixRow<'p, Cx: PatCx> {
1057
1059
}
1058
1060
1059
1061
impl < ' p , Cx : PatCx > MatrixRow < ' p , Cx > {
1060
- fn is_empty ( & self ) -> bool {
1061
- self . pats . is_empty ( )
1062
- }
1063
-
1064
1062
fn len ( & self ) -> usize {
1065
1063
self . pats . len ( )
1066
1064
}
@@ -1073,12 +1071,14 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> {
1073
1071
self . pats . iter ( )
1074
1072
}
1075
1073
1076
- // Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is
1077
- // an or-pattern. Panics if `self` is empty.
1078
- fn expand_or_pat ( & self ) -> impl Iterator < Item = MatrixRow < ' p , Cx > > + Captures < ' _ > {
1079
- self . pats . expand_or_pat ( ) . map ( |patstack| MatrixRow {
1074
+ // Expand the first or-pattern (if any) into its subpatterns. Panics if `self` is empty.
1075
+ fn expand_or_pat (
1076
+ & self ,
1077
+ parent_row : usize ,
1078
+ ) -> impl Iterator < Item = MatrixRow < ' p , Cx > > + Captures < ' _ > {
1079
+ self . pats . expand_or_pat ( ) . map ( move |patstack| MatrixRow {
1080
1080
pats : patstack,
1081
- parent_row : self . parent_row ,
1081
+ parent_row,
1082
1082
is_under_guard : self . is_under_guard ,
1083
1083
useful : false ,
1084
1084
intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::expand_and_push`.
@@ -1100,7 +1100,7 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> {
1100
1100
parent_row,
1101
1101
is_under_guard : self . is_under_guard ,
1102
1102
useful : false ,
1103
- intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::expand_and_push `.
1103
+ intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::push `.
1104
1104
} )
1105
1105
}
1106
1106
}
@@ -1116,7 +1116,7 @@ impl<'p, Cx: PatCx> fmt::Debug for MatrixRow<'p, Cx> {
1116
1116
/// Invariant: each row must have the same length, and each column must have the same type.
1117
1117
///
1118
1118
/// Invariant: the first column must not contain or-patterns. This is handled by
1119
- /// [`Matrix::expand_and_push `].
1119
+ /// [`Matrix::push `].
1120
1120
///
1121
1121
/// In fact each column corresponds to a place inside the scrutinee of the match. E.g. after
1122
1122
/// specializing `(,)` and `Some` on a pattern of type `(Option<u32>, bool)`, the first column of
@@ -1136,19 +1136,10 @@ struct Matrix<'p, Cx: PatCx> {
1136
1136
}
1137
1137
1138
1138
impl < ' p , Cx : PatCx > Matrix < ' p , Cx > {
1139
- /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
1140
- /// expands it. Internal method, prefer [`Matrix::new`].
1141
- fn expand_and_push ( & mut self , mut row : MatrixRow < ' p , Cx > ) {
1142
- if !row. is_empty ( ) && row. head ( ) . is_or_pat ( ) {
1143
- // Expand nested or-patterns.
1144
- for mut new_row in row. expand_or_pat ( ) {
1145
- new_row. intersects = BitSet :: new_empty ( self . rows . len ( ) ) ;
1146
- self . rows . push ( new_row) ;
1147
- }
1148
- } else {
1149
- row. intersects = BitSet :: new_empty ( self . rows . len ( ) ) ;
1150
- self . rows . push ( row) ;
1151
- }
1139
+ /// Pushes a new row to the matrix. Internal method, prefer [`Matrix::new`].
1140
+ fn push ( & mut self , mut row : MatrixRow < ' p , Cx > ) {
1141
+ row. intersects = BitSet :: new_empty ( self . rows . len ( ) ) ;
1142
+ self . rows . push ( row) ;
1152
1143
}
1153
1144
1154
1145
/// Build a new matrix from an iterator of `MatchArm`s.
@@ -1170,9 +1161,9 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> {
1170
1161
parent_row : arm_id,
1171
1162
is_under_guard : arm. has_guard ,
1172
1163
useful : false ,
1173
- intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::expand_and_push `.
1164
+ intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::push `.
1174
1165
} ;
1175
- matrix. expand_and_push ( v) ;
1166
+ matrix. push ( v) ;
1176
1167
}
1177
1168
matrix
1178
1169
}
@@ -1209,22 +1200,38 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> {
1209
1200
ctor : & Constructor < Cx > ,
1210
1201
ctor_is_relevant : bool ,
1211
1202
) -> Result < Matrix < ' p , Cx > , Cx :: Error > {
1212
- let subfield_place_info = self . place_info [ 0 ] . specialize ( pcx. cx , ctor) ;
1213
- let arity = subfield_place_info. len ( ) ;
1214
- let specialized_place_info =
1215
- subfield_place_info. chain ( self . place_info [ 1 ..] . iter ( ) . cloned ( ) ) . collect ( ) ;
1216
- let mut matrix = Matrix {
1217
- rows : Vec :: new ( ) ,
1218
- place_info : specialized_place_info,
1219
- wildcard_row_is_relevant : self . wildcard_row_is_relevant && ctor_is_relevant,
1220
- } ;
1221
- for ( i, row) in self . rows ( ) . enumerate ( ) {
1222
- if ctor. is_covered_by ( pcx. cx , row. head ( ) . ctor ( ) ) ? {
1223
- let new_row = row. pop_head_constructor ( pcx. cx , ctor, arity, ctor_is_relevant, i) ?;
1224
- matrix. expand_and_push ( new_row) ;
1203
+ if matches ! ( ctor, Constructor :: Or ) {
1204
+ // Specializing with `Or` means expanding rows with or-patterns.
1205
+ let mut matrix = Matrix {
1206
+ rows : Vec :: new ( ) ,
1207
+ place_info : self . place_info . clone ( ) ,
1208
+ wildcard_row_is_relevant : self . wildcard_row_is_relevant ,
1209
+ } ;
1210
+ for ( i, row) in self . rows ( ) . enumerate ( ) {
1211
+ for new_row in row. expand_or_pat ( i) {
1212
+ matrix. push ( new_row) ;
1213
+ }
1225
1214
}
1215
+ Ok ( matrix)
1216
+ } else {
1217
+ let subfield_place_info = self . place_info [ 0 ] . specialize ( pcx. cx , ctor) ;
1218
+ let arity = subfield_place_info. len ( ) ;
1219
+ let specialized_place_info =
1220
+ subfield_place_info. chain ( self . place_info [ 1 ..] . iter ( ) . cloned ( ) ) . collect ( ) ;
1221
+ let mut matrix = Matrix {
1222
+ rows : Vec :: new ( ) ,
1223
+ place_info : specialized_place_info,
1224
+ wildcard_row_is_relevant : self . wildcard_row_is_relevant && ctor_is_relevant,
1225
+ } ;
1226
+ for ( i, row) in self . rows ( ) . enumerate ( ) {
1227
+ if ctor. is_covered_by ( pcx. cx , row. head ( ) . ctor ( ) ) ? {
1228
+ let new_row =
1229
+ row. pop_head_constructor ( pcx. cx , ctor, arity, ctor_is_relevant, i) ?;
1230
+ matrix. push ( new_row) ;
1231
+ }
1232
+ }
1233
+ Ok ( matrix)
1226
1234
}
1227
- Ok ( matrix)
1228
1235
}
1229
1236
1230
1237
/// Recover row usefulness and intersection information from a processed specialized matrix.
@@ -1465,7 +1472,9 @@ impl<Cx: PatCx> WitnessMatrix<Cx> {
1465
1472
missing_ctors : & [ Constructor < Cx > ] ,
1466
1473
ctor : & Constructor < Cx > ,
1467
1474
) {
1468
- if self . is_empty ( ) {
1475
+ // The `Or` constructor indicates that we expanded or-patterns. This doesn't affect
1476
+ // witnesses.
1477
+ if self . is_empty ( ) || matches ! ( ctor, Constructor :: Or ) {
1469
1478
return ;
1470
1479
}
1471
1480
if matches ! ( ctor, Constructor :: Missing ) {
@@ -1715,48 +1724,6 @@ pub enum Usefulness<'p, Cx: PatCx> {
1715
1724
Redundant ,
1716
1725
}
1717
1726
1718
- /// Report whether this pattern was found useful, and its subpatterns that were not useful if any.
1719
- fn collect_pattern_usefulness < ' p , Cx : PatCx > (
1720
- useful_subpatterns : & FxHashSet < PatId > ,
1721
- pat : & ' p DeconstructedPat < Cx > ,
1722
- ) -> Usefulness < ' p , Cx > {
1723
- fn pat_is_useful < ' p , Cx : PatCx > (
1724
- useful_subpatterns : & FxHashSet < PatId > ,
1725
- pat : & ' p DeconstructedPat < Cx > ,
1726
- ) -> bool {
1727
- if useful_subpatterns. contains ( & pat. uid ) {
1728
- true
1729
- } else if pat. is_or_pat ( )
1730
- && pat. iter_fields ( ) . any ( |f| pat_is_useful ( useful_subpatterns, & f. pat ) )
1731
- {
1732
- // We always expand or patterns in the matrix, so we will never see the actual
1733
- // or-pattern (the one with constructor `Or`) in the column. As such, it will not be
1734
- // marked as useful itself, only its children will. We recover this information here.
1735
- true
1736
- } else {
1737
- false
1738
- }
1739
- }
1740
-
1741
- let mut redundant_subpats = Vec :: new ( ) ;
1742
- pat. walk ( & mut |p| {
1743
- if pat_is_useful ( useful_subpatterns, p) {
1744
- // The pattern is useful, so we recurse to find redundant subpatterns.
1745
- true
1746
- } else {
1747
- // The pattern is redundant.
1748
- redundant_subpats. push ( p) ;
1749
- false // stop recursing
1750
- }
1751
- } ) ;
1752
-
1753
- if pat_is_useful ( useful_subpatterns, pat) {
1754
- Usefulness :: Useful ( redundant_subpats)
1755
- } else {
1756
- Usefulness :: Redundant
1757
- }
1758
- }
1759
-
1760
1727
/// The output of checking a match for exhaustiveness and arm usefulness.
1761
1728
pub struct UsefulnessReport < ' p , Cx : PatCx > {
1762
1729
/// For each arm of the input, whether that arm is useful after the arms above it.
@@ -1793,25 +1760,26 @@ pub fn compute_match_usefulness<'p, Cx: PatCx>(
1793
1760
. copied ( )
1794
1761
. map ( |arm| {
1795
1762
debug ! ( ?arm) ;
1796
- let usefulness = collect_pattern_usefulness ( & cx. useful_subpatterns , arm. pat ) ;
1763
+ let usefulness = if cx. useful_subpatterns . contains ( & arm. pat . uid ) {
1764
+ let mut redundant_subpats = Vec :: new ( ) ;
1765
+ arm. pat . walk ( & mut |subpat| {
1766
+ if cx. useful_subpatterns . contains ( & subpat. uid ) {
1767
+ true // keep recursing
1768
+ } else {
1769
+ redundant_subpats. push ( subpat) ;
1770
+ false // stop recursing
1771
+ }
1772
+ } ) ;
1773
+ Usefulness :: Useful ( redundant_subpats)
1774
+ } else {
1775
+ Usefulness :: Redundant
1776
+ } ;
1797
1777
debug ! ( ?usefulness) ;
1798
1778
( arm, usefulness)
1799
1779
} )
1800
1780
. collect ( ) ;
1801
1781
1802
- let mut arm_intersections: Vec < _ > =
1803
- arms. iter ( ) . enumerate ( ) . map ( |( i, _) | BitSet :: new_empty ( i) ) . collect ( ) ;
1804
- for row in matrix. rows ( ) {
1805
- let arm_id = row. parent_row ;
1806
- for intersection in row. intersects . iter ( ) {
1807
- // Convert the matrix row ids into arm ids (they can differ because we expand or-patterns).
1808
- let arm_intersection = matrix. rows [ intersection] . parent_row ;
1809
- // Note: self-intersection can happen with or-patterns.
1810
- if arm_intersection != arm_id {
1811
- arm_intersections[ arm_id] . insert ( arm_intersection) ;
1812
- }
1813
- }
1814
- }
1782
+ let arm_intersections: Vec < _ > = matrix. rows ( ) . map ( |row| row. intersects . clone ( ) ) . collect ( ) ;
1815
1783
1816
1784
Ok ( UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses, arm_intersections } )
1817
1785
}
0 commit comments