299
299
//!
300
300
//!
301
301
//!
302
+ //! # `Missing` and relevant constructors
303
+ //!
304
+ //! Take the following example:
305
+ //!
306
+ //! ```
307
+ //! enum Direction { North, South, East, West }
308
+ //! # let wind = (Direction::North, 0u8);
309
+ //! match wind {
310
+ //! (Direction::North, _) => {} // arm 1
311
+ //! (_, 50..) => {} // arm 2
312
+ //! }
313
+ //! ```
314
+ //!
315
+ //! Remember that we represent the "everything else" cases with [`Constructor::Missing`]. When we
316
+ //! specialize with `Missing` in the first column, we have one arm left:
317
+ //!
318
+ //! ```ignore(partial code)
319
+ //! (50..) => {} // arm 2
320
+ //! ```
321
+ //!
322
+ //! We then conclude that arm 2 is useful, and that the match is non-exhaustive with witness
323
+ //! `(Missing, 0..50)` (which we would display to the user as `(_, 0..50)`).
324
+ //!
325
+ //! When we then specialize with `North`, we have two arms left:
326
+ //!
327
+ //! ```ignore(partial code)
328
+ //! (_) => {} // arm 1
329
+ //! (50..) => {} // arm 2
330
+ //! ```
331
+ //!
332
+ //! Because `Missing` only matches wildcard rows, specializing with `Missing` is guaranteed to
333
+ //! result in a subset of the rows obtained from specializing with anything else. This means that
334
+ //! any row with a wildcard found useful when specializing with anything else would also be found
335
+ //! useful in the `Missing` case. In our example, after specializing with `North` here we will not
336
+ //! gain new information regarding the usefulness of arm 2 or of the fake wildcard row used for
337
+ //! exhaustiveness. This allows us to skip cases.
338
+ //!
339
+ //! When specializing, if there is a `Missing` case we call the other constructors "irrelevant".
340
+ //! When there is no `Missing` case there are no irrelevant constructors.
341
+ //!
342
+ //! What happens then is: when we specialize a wildcard with an irrelevant constructor, we know we
343
+ //! won't get new info for this row; we consider that row "irrelevant". Whenever all the rows are
344
+ //! found irrelevant, we can safely skip the case entirely.
345
+ //!
346
+ //! In the example above, we will entirely skip the `(North, 50..)` case. This skipping was
347
+ //! developped as a solution to #118437. It doesn't look like much but it can save us from
348
+ //! exponential blowup.
349
+ //!
350
+ //! There's a subtlety regarding exhaustiveness: while this shortcutting doesn't affect correctness,
351
+ //! it can affect which witnesses are reported. For example, in the following:
352
+ //!
353
+ //! ```
354
+ //! # let foo = (true, true);
355
+ //! match foo {
356
+ //! (true, _, true) => {}
357
+ //! (_, true, _) => {}
358
+ //! }
359
+ //! ```
360
+ //!
361
+ //! In this example we will skip the `(true, true, _)` case entirely. Thus `(true, true, false)`
362
+ //! will not be reported as missing. In fact we go further than this: we deliberately do not report
363
+ //! any cases that are irrelevant for the fake wildcard row. For example, in `match ... { (true,
364
+ //! true) => {} }` we will not report `(true, false)` as missing. This was a deliberate choice made
365
+ //! early in the development of rust; it so happens that it is beneficial for performance reasons
366
+ //! too.
367
+ //!
368
+ //!
369
+ //!
302
370
//! # Or-patterns
303
371
//!
304
372
//! What we have described so far works well if there are no or-patterns. To handle them, if the
@@ -710,11 +778,15 @@ impl fmt::Display for ValidityConstraint {
710
778
struct PatStack < ' p , ' tcx > {
711
779
// Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well.
712
780
pats : SmallVec < [ & ' p DeconstructedPat < ' p , ' tcx > ; 2 ] > ,
781
+ /// Sometimes we know that as far as this row is concerned, the current case is already handled
782
+ /// by a different, more general, case. When all rows are irrelevant this allows us to skip many
783
+ /// branches. This is purely an optimization. See at the top for details.
784
+ relevant : bool ,
713
785
}
714
786
715
787
impl < ' p , ' tcx > PatStack < ' p , ' tcx > {
716
788
fn from_pattern ( pat : & ' p DeconstructedPat < ' p , ' tcx > ) -> Self {
717
- PatStack { pats : smallvec ! [ pat] }
789
+ PatStack { pats : smallvec ! [ pat] , relevant : true }
718
790
}
719
791
720
792
fn is_empty ( & self ) -> bool {
@@ -749,12 +821,17 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
749
821
& self ,
750
822
pcx : & PatCtxt < ' _ , ' p , ' tcx > ,
751
823
ctor : & Constructor < ' tcx > ,
824
+ ctor_is_relevant : bool ,
752
825
) -> PatStack < ' p , ' tcx > {
753
826
// We pop the head pattern and push the new fields extracted from the arguments of
754
827
// `self.head()`.
755
828
let mut new_pats = self . head ( ) . specialize ( pcx, ctor) ;
756
829
new_pats. extend_from_slice ( & self . pats [ 1 ..] ) ;
757
- PatStack { pats : new_pats }
830
+ // `ctor` is relevant for this row if it is the actual constructor of this row, or if the
831
+ // row has a wildcard and `ctor` is relevant for wildcards.
832
+ let ctor_is_relevant =
833
+ !matches ! ( self . head( ) . ctor( ) , Constructor :: Wildcard ) || ctor_is_relevant;
834
+ PatStack { pats : new_pats, relevant : self . relevant && ctor_is_relevant }
758
835
}
759
836
}
760
837
@@ -820,10 +897,11 @@ impl<'p, 'tcx> MatrixRow<'p, 'tcx> {
820
897
& self ,
821
898
pcx : & PatCtxt < ' _ , ' p , ' tcx > ,
822
899
ctor : & Constructor < ' tcx > ,
900
+ ctor_is_relevant : bool ,
823
901
parent_row : usize ,
824
902
) -> MatrixRow < ' p , ' tcx > {
825
903
MatrixRow {
826
- pats : self . pats . pop_head_constructor ( pcx, ctor) ,
904
+ pats : self . pats . pop_head_constructor ( pcx, ctor, ctor_is_relevant ) ,
827
905
parent_row,
828
906
is_under_guard : self . is_under_guard ,
829
907
useful : false ,
@@ -952,8 +1030,9 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
952
1030
& self ,
953
1031
pcx : & PatCtxt < ' _ , ' p , ' tcx > ,
954
1032
ctor : & Constructor < ' tcx > ,
1033
+ ctor_is_relevant : bool ,
955
1034
) -> Matrix < ' p , ' tcx > {
956
- let wildcard_row = self . wildcard_row . pop_head_constructor ( pcx, ctor) ;
1035
+ let wildcard_row = self . wildcard_row . pop_head_constructor ( pcx, ctor, ctor_is_relevant ) ;
957
1036
let new_validity = self . place_validity [ 0 ] . specialize ( pcx, ctor) ;
958
1037
let new_place_validity = std:: iter:: repeat ( new_validity)
959
1038
. take ( ctor. arity ( pcx) )
@@ -963,7 +1042,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
963
1042
Matrix { rows : Vec :: new ( ) , wildcard_row, place_validity : new_place_validity } ;
964
1043
for ( i, row) in self . rows ( ) . enumerate ( ) {
965
1044
if ctor. is_covered_by ( pcx, row. head ( ) . ctor ( ) ) {
966
- let new_row = row. pop_head_constructor ( pcx, ctor, i) ;
1045
+ let new_row = row. pop_head_constructor ( pcx, ctor, ctor_is_relevant , i) ;
967
1046
matrix. expand_and_push ( new_row) ;
968
1047
}
969
1048
}
@@ -1161,7 +1240,10 @@ impl<'tcx> WitnessMatrix<'tcx> {
1161
1240
if matches ! ( ctor, Constructor :: Missing ) {
1162
1241
// We got the special `Missing` constructor that stands for the constructors not present
1163
1242
// in the match.
1164
- if !report_individual_missing_ctors {
1243
+ if missing_ctors. is_empty ( ) {
1244
+ // Nothing to report.
1245
+ * self = Self :: empty ( ) ;
1246
+ } else if !report_individual_missing_ctors {
1165
1247
// Report `_` as missing.
1166
1248
let pat = WitnessPat :: wild_from_ctor ( pcx, Constructor :: Wildcard ) ;
1167
1249
self . push_pattern ( pat) ;
@@ -1220,6 +1302,15 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
1220
1302
) -> WitnessMatrix < ' tcx > {
1221
1303
debug_assert ! ( matrix. rows( ) . all( |r| r. len( ) == matrix. column_count( ) ) ) ;
1222
1304
1305
+ if !matrix. wildcard_row . relevant && matrix. rows ( ) . all ( |r| !r. pats . relevant ) {
1306
+ // Here we know that nothing will contribute further to exhaustiveness or usefulness. This
1307
+ // is purely an optimization: skipping this check doesn't affect correctness. This check
1308
+ // does change runtime behavior from exponential to quadratic on some matches found in the
1309
+ // wild, so it's pretty important. It also affects which missing patterns will be reported.
1310
+ // See the top of the file for details.
1311
+ return WitnessMatrix :: empty ( ) ;
1312
+ }
1313
+
1223
1314
let Some ( ty) = matrix. head_ty ( ) else {
1224
1315
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
1225
1316
// A row is useful iff it has no (unguarded) rows above it.
@@ -1232,8 +1323,14 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
1232
1323
return WitnessMatrix :: empty ( ) ;
1233
1324
}
1234
1325
}
1235
- // No (unguarded) rows, so the match is not exhaustive. We return a new witness.
1236
- return WitnessMatrix :: unit_witness ( ) ;
1326
+ // No (unguarded) rows, so the match is not exhaustive. We return a new witness unless
1327
+ // irrelevant.
1328
+ return if matrix. wildcard_row . relevant {
1329
+ WitnessMatrix :: unit_witness ( )
1330
+ } else {
1331
+ // We can omit the witness without affecting correctness, so we do.
1332
+ WitnessMatrix :: empty ( )
1333
+ } ;
1237
1334
} ;
1238
1335
1239
1336
debug ! ( "ty: {ty:?}" ) ;
@@ -1274,32 +1371,21 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
1274
1371
1275
1372
let mut ret = WitnessMatrix :: empty ( ) ;
1276
1373
for ctor in split_ctors {
1277
- debug ! ( "specialize({:?})" , ctor) ;
1278
1374
// Dig into rows that match `ctor`.
1279
- let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor) ;
1375
+ debug ! ( "specialize({:?})" , ctor) ;
1376
+ // `ctor` is *irrelevant* if there's another constructor in `split_ctors` that matches
1377
+ // strictly fewer rows. In that case we can sometimes skip it. See the top of the file for
1378
+ // details.
1379
+ let ctor_is_relevant = matches ! ( ctor, Constructor :: Missing ) || missing_ctors. is_empty ( ) ;
1380
+ let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor, ctor_is_relevant) ;
1280
1381
let mut witnesses = ensure_sufficient_stack ( || {
1281
1382
compute_exhaustiveness_and_usefulness ( cx, & mut spec_matrix, false )
1282
1383
} ) ;
1283
1384
1284
- let counts_for_exhaustiveness = match ctor {
1285
- Constructor :: Missing => !missing_ctors. is_empty ( ) ,
1286
- // If there are missing constructors we'll report those instead. Since `Missing` matches
1287
- // only the wildcard rows, it matches fewer rows than this constructor, and is therefore
1288
- // guaranteed to result in the same or more witnesses. So skipping this does not
1289
- // jeopardize correctness.
1290
- _ => missing_ctors. is_empty ( ) ,
1291
- } ;
1292
- if counts_for_exhaustiveness {
1293
- // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
1294
- witnesses. apply_constructor (
1295
- pcx,
1296
- & missing_ctors,
1297
- & ctor,
1298
- report_individual_missing_ctors,
1299
- ) ;
1300
- // Accumulate the found witnesses.
1301
- ret. extend ( witnesses) ;
1302
- }
1385
+ // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
1386
+ witnesses. apply_constructor ( pcx, & missing_ctors, & ctor, report_individual_missing_ctors) ;
1387
+ // Accumulate the found witnesses.
1388
+ ret. extend ( witnesses) ;
1303
1389
1304
1390
// A parent row is useful if any of its children is.
1305
1391
for child_row in spec_matrix. rows ( ) {
0 commit comments