300
300
//!
301
301
//!
302
302
//!
303
+ //! # `Missing` and relevancy
304
+ //!
305
+ //! ## Relevant values
306
+ //!
307
+ //! Take the following example:
308
+ //!
309
+ //! ```compile_fail,E0004
310
+ //! # let foo = (true, true);
311
+ //! match foo {
312
+ //! (true, _) => 1,
313
+ //! (_, true) => 2,
314
+ //! };
315
+ //! ```
316
+ //!
317
+ //! Consider the value `(true, true)`:
318
+ //! - Row 2 does not distinguish `(true, true)` and `(false, true)`;
319
+ //! - `false` does not show up in the first column of the match, so without knowing anything else we
320
+ //! can deduce that `(false, true)` matches the same or fewer rows than `(true, true)`.
321
+ //!
322
+ //! Using those two facts together, we deduce that `(true, true)` will not give us more usefulness
323
+ //! information about row 2 than `(false, true)` would. We say that "`(true, true)` is made
324
+ //! irrelevant for row 2 by `(false, true)`". We will use this idea to prune the search tree.
325
+ //!
326
+ //!
327
+ //! ## Computing relevancy
328
+ //!
329
+ //! We now generalize from the above example to approximate relevancy in a simple way. Note that we
330
+ //! will only compute an approximation: we can sometimes determine when a case is irrelevant, but
331
+ //! computing this precisely is at least as hard as computing usefulness.
332
+ //!
333
+ //! Our computation of relevancy relies on the `Missing` constructor. As explained in
334
+ //! [`crate::constructor`], `Missing` represents the constructors not present in a given column. For
335
+ //! example in the following:
336
+ //!
337
+ //! ```compile_fail,E0004
338
+ //! enum Direction { North, South, East, West }
339
+ //! # let wind = (Direction::North, 0u8);
340
+ //! match wind {
341
+ //! (Direction::North, _) => 1,
342
+ //! (_, 50..) => 2,
343
+ //! };
344
+ //! ```
345
+ //!
346
+ //! Here `South`, `East` and `West` are missing in the first column, and `0..50` is missing in the
347
+ //! second. Both of these sets are represented by `Constructor::Missing` in their corresponding
348
+ //! column.
349
+ //!
350
+ //! We then compute relevancy as follows: during the course of the algorithm, for a row `r`:
351
+ //! - if `r` has a wildcard in the first column;
352
+ //! - and some constructors are missing in that column;
353
+ //! - then any `c != Missing` is considered irrelevant for row `r`.
354
+ //!
355
+ //! By this we mean that continuing the algorithm by specializing with `c` is guaranteed not to
356
+ //! contribute more information about the usefulness of row `r` than what we would get by
357
+ //! specializing with `Missing`. The argument is the same as in the previous subsection.
358
+ //!
359
+ //! Once we've specialized by a constructor `c` that is irrelevant for row `r`, we're guaranteed to
360
+ //! only explore values irrelevant for `r`. If we then ever reach a point where we're only exploring
361
+ //! values that are irrelevant to all of the rows (including the virtual wildcard row used for
362
+ //! exhaustiveness), we skip that case entirely.
363
+ //!
364
+ //!
365
+ //! ## Example
366
+ //!
367
+ //! Let's go through a variation on the first example:
368
+ //!
369
+ //! ```compile_fail,E0004
370
+ //! # let foo = (true, true, true);
371
+ //! match foo {
372
+ //! (true, _, true) => 1,
373
+ //! (_, true, _) => 2,
374
+ //! };
375
+ //! ```
376
+ //!
377
+ //! ```text
378
+ //! ┐ Patterns:
379
+ //! │ 1. `[(true, _, true)]`
380
+ //! │ 2. `[(_, true, _)]`
381
+ //! │ 3. `[_]` // virtual extra wildcard row
382
+ //! │
383
+ //! │ Specialize with `(,,)`:
384
+ //! ├─┐ Patterns:
385
+ //! │ │ 1. `[true, _, true]`
386
+ //! │ │ 2. `[_, true, _]`
387
+ //! │ │ 3. `[_, _, _]`
388
+ //! │ │
389
+ //! │ │ There are missing constructors in the first column (namely `false`), hence
390
+ //! │ │ `true` is irrelevant for rows 2 and 3.
391
+ //! │ │
392
+ //! │ │ Specialize with `true`:
393
+ //! │ ├─┐ Patterns:
394
+ //! │ │ │ 1. `[_, true]`
395
+ //! │ │ │ 2. `[true, _]` // now exploring irrelevant cases
396
+ //! │ │ │ 3. `[_, _]` // now exploring irrelevant cases
397
+ //! │ │ │
398
+ //! │ │ │ There are missing constructors in the first column (namely `false`), hence
399
+ //! │ │ │ `true` is irrelevant for rows 1 and 3.
400
+ //! │ │ │
401
+ //! │ │ │ Specialize with `true`:
402
+ //! │ │ ├─┐ Patterns:
403
+ //! │ │ │ │ 1. `[true]` // now exploring irrelevant cases
404
+ //! │ │ │ │ 2. `[_]` // now exploring irrelevant cases
405
+ //! │ │ │ │ 3. `[_]` // now exploring irrelevant cases
406
+ //! │ │ │ │
407
+ //! │ │ │ │ The current case is irrelevant for all rows: we backtrack immediately.
408
+ //! │ │ ├─┘
409
+ //! │ │ │
410
+ //! │ │ │ Specialize with `false`:
411
+ //! │ │ ├─┐ Patterns:
412
+ //! │ │ │ │ 1. `[true]`
413
+ //! │ │ │ │ 3. `[_]` // now exploring irrelevant cases
414
+ //! │ │ │ │
415
+ //! │ │ │ │ Specialize with `true`:
416
+ //! │ │ │ ├─┐ Patterns:
417
+ //! │ │ │ │ │ 1. `[]`
418
+ //! │ │ │ │ │ 3. `[]` // now exploring irrelevant cases
419
+ //! │ │ │ │ │
420
+ //! │ │ │ │ │ Row 1 is therefore useful.
421
+ //! │ │ │ ├─┘
422
+ //! <etc...>
423
+ //! ```
424
+ //!
425
+ //! Relevancy allowed us to skip the case `(true, true, _)` entirely. In some cases this pruning can
426
+ //! give drastic speedups. The case this was built for is the following (#118437):
427
+ //!
428
+ //! ```ignore(illustrative)
429
+ //! match foo {
430
+ //! (true, _, _, _, ..) => 1,
431
+ //! (_, true, _, _, ..) => 2,
432
+ //! (_, _, true, _, ..) => 3,
433
+ //! (_, _, _, true, ..) => 4,
434
+ //! ...
435
+ //! }
436
+ //! ```
437
+ //!
438
+ //! Without considering relevancy, we would explore all 2^n combinations of the `true` and `Missing`
439
+ //! constructors. Relevancy tells us that e.g. `(true, true, false, false, false, ...)` is
440
+ //! irrelevant for all the rows. This allows us to skip all cases with more than one `true`
441
+ //! constructor, changing the runtime from exponential to linear.
442
+ //!
443
+ //!
444
+ //! ## Relevancy and exhaustiveness
445
+ //!
446
+ //! For exhaustiveness, we do something slightly different w.r.t relevancy: we do not report
447
+ //! witnesses of non-exhaustiveness that are irrelevant for the virtual wildcard row. For example,
448
+ //! in:
449
+ //!
450
+ //! ```ignore(illustrative)
451
+ //! match foo {
452
+ //! (true, true) => {}
453
+ //! }
454
+ //! ```
455
+ //!
456
+ //! we only report `(false, _)` as missing. This was a deliberate choice made early in the
457
+ //! development of rust, for diagnostic and performance purposes. As showed in the previous section,
458
+ //! ignoring irrelevant cases preserves usefulness, so this choice still correctly computes whether
459
+ //! a match is exhaustive.
460
+ //!
461
+ //!
462
+ //!
303
463
//! # Or-patterns
304
464
//!
305
465
//! What we have described so far works well if there are no or-patterns. To handle them, if the
@@ -674,11 +834,15 @@ impl fmt::Display for ValidityConstraint {
674
834
struct PatStack < ' a , ' p , Cx : TypeCx > {
675
835
// Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well.
676
836
pats : SmallVec < [ & ' a DeconstructedPat < ' p , Cx > ; 2 ] > ,
837
+ /// Sometimes we know that as far as this row is concerned, the current case is already handled
838
+ /// by a different, more general, case. When the case is irrelevant for all rows this allows us
839
+ /// to skip a case entirely. This is purely an optimization. See at the top for details.
840
+ relevant : bool ,
677
841
}
678
842
679
843
impl < ' a , ' p , Cx : TypeCx > PatStack < ' a , ' p , Cx > {
680
844
fn from_pattern ( pat : & ' a DeconstructedPat < ' p , Cx > ) -> Self {
681
- PatStack { pats : smallvec ! [ pat] }
845
+ PatStack { pats : smallvec ! [ pat] , relevant : true }
682
846
}
683
847
684
848
fn is_empty ( & self ) -> bool {
@@ -713,12 +877,17 @@ impl<'a, 'p, Cx: TypeCx> PatStack<'a, 'p, Cx> {
713
877
& self ,
714
878
pcx : & PlaceCtxt < ' a , ' p , Cx > ,
715
879
ctor : & Constructor < Cx > ,
880
+ ctor_is_relevant : bool ,
716
881
) -> PatStack < ' a , ' p , Cx > {
717
882
// We pop the head pattern and push the new fields extracted from the arguments of
718
883
// `self.head()`.
719
884
let mut new_pats = self . head ( ) . specialize ( pcx, ctor) ;
720
885
new_pats. extend_from_slice ( & self . pats [ 1 ..] ) ;
721
- PatStack { pats : new_pats }
886
+ // `ctor` is relevant for this row if it is the actual constructor of this row, or if the
887
+ // row has a wildcard and `ctor` is relevant for wildcards.
888
+ let ctor_is_relevant =
889
+ !matches ! ( self . head( ) . ctor( ) , Constructor :: Wildcard ) || ctor_is_relevant;
890
+ PatStack { pats : new_pats, relevant : self . relevant && ctor_is_relevant }
722
891
}
723
892
}
724
893
@@ -784,10 +953,11 @@ impl<'a, 'p, Cx: TypeCx> MatrixRow<'a, 'p, Cx> {
784
953
& self ,
785
954
pcx : & PlaceCtxt < ' a , ' p , Cx > ,
786
955
ctor : & Constructor < Cx > ,
956
+ ctor_is_relevant : bool ,
787
957
parent_row : usize ,
788
958
) -> MatrixRow < ' a , ' p , Cx > {
789
959
MatrixRow {
790
- pats : self . pats . pop_head_constructor ( pcx, ctor) ,
960
+ pats : self . pats . pop_head_constructor ( pcx, ctor, ctor_is_relevant ) ,
791
961
parent_row,
792
962
is_under_guard : self . is_under_guard ,
793
963
useful : false ,
@@ -913,8 +1083,9 @@ impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> {
913
1083
& self ,
914
1084
pcx : & PlaceCtxt < ' a , ' p , Cx > ,
915
1085
ctor : & Constructor < Cx > ,
1086
+ ctor_is_relevant : bool ,
916
1087
) -> Matrix < ' a , ' p , Cx > {
917
- let wildcard_row = self . wildcard_row . pop_head_constructor ( pcx, ctor) ;
1088
+ let wildcard_row = self . wildcard_row . pop_head_constructor ( pcx, ctor, ctor_is_relevant ) ;
918
1089
let new_validity = self . place_validity [ 0 ] . specialize ( ctor) ;
919
1090
let new_place_validity = std:: iter:: repeat ( new_validity)
920
1091
. take ( ctor. arity ( pcx) )
@@ -924,7 +1095,7 @@ impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> {
924
1095
Matrix { rows : Vec :: new ( ) , wildcard_row, place_validity : new_place_validity } ;
925
1096
for ( i, row) in self . rows ( ) . enumerate ( ) {
926
1097
if ctor. is_covered_by ( pcx, row. head ( ) . ctor ( ) ) {
927
- let new_row = row. pop_head_constructor ( pcx, ctor, i) ;
1098
+ let new_row = row. pop_head_constructor ( pcx, ctor, ctor_is_relevant , i) ;
928
1099
matrix. expand_and_push ( new_row) ;
929
1100
}
930
1101
}
@@ -1122,7 +1293,10 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
1122
1293
if matches ! ( ctor, Constructor :: Missing ) {
1123
1294
// We got the special `Missing` constructor that stands for the constructors not present
1124
1295
// in the match.
1125
- if !report_individual_missing_ctors {
1296
+ if missing_ctors. is_empty ( ) {
1297
+ // Nothing to report.
1298
+ * self = Self :: empty ( ) ;
1299
+ } else if !report_individual_missing_ctors {
1126
1300
// Report `_` as missing.
1127
1301
let pat = WitnessPat :: wild_from_ctor ( pcx, Constructor :: Wildcard ) ;
1128
1302
self . push_pattern ( pat) ;
@@ -1181,6 +1355,13 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1181
1355
) -> WitnessMatrix < Cx > {
1182
1356
debug_assert ! ( matrix. rows( ) . all( |r| r. len( ) == matrix. column_count( ) ) ) ;
1183
1357
1358
+ if !matrix. wildcard_row . relevant && matrix. rows ( ) . all ( |r| !r. pats . relevant ) {
1359
+ // Here we know that nothing will contribute further to exhaustiveness or usefulness. This
1360
+ // is purely an optimization: skipping this check doesn't affect correctness. See the top of
1361
+ // the file for details.
1362
+ return WitnessMatrix :: empty ( ) ;
1363
+ }
1364
+
1184
1365
let Some ( ty) = matrix. head_ty ( ) else {
1185
1366
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
1186
1367
// A row is useful iff it has no (unguarded) rows above it.
@@ -1193,8 +1374,14 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1193
1374
return WitnessMatrix :: empty ( ) ;
1194
1375
}
1195
1376
}
1196
- // No (unguarded) rows, so the match is not exhaustive. We return a new witness.
1197
- return WitnessMatrix :: unit_witness ( ) ;
1377
+ // No (unguarded) rows, so the match is not exhaustive. We return a new witness unless
1378
+ // irrelevant.
1379
+ return if matrix. wildcard_row . relevant {
1380
+ WitnessMatrix :: unit_witness ( )
1381
+ } else {
1382
+ // We choose to not report anything here; see at the top for details.
1383
+ WitnessMatrix :: empty ( )
1384
+ } ;
1198
1385
} ;
1199
1386
1200
1387
debug ! ( "ty: {ty:?}" ) ;
@@ -1237,32 +1424,21 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1237
1424
1238
1425
let mut ret = WitnessMatrix :: empty ( ) ;
1239
1426
for ctor in split_ctors {
1240
- debug ! ( "specialize({:?})" , ctor) ;
1241
1427
// Dig into rows that match `ctor`.
1242
- let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor) ;
1428
+ debug ! ( "specialize({:?})" , ctor) ;
1429
+ // `ctor` is *irrelevant* if there's another constructor in `split_ctors` that matches
1430
+ // strictly fewer rows. In that case we can sometimes skip it. See the top of the file for
1431
+ // details.
1432
+ let ctor_is_relevant = matches ! ( ctor, Constructor :: Missing ) || missing_ctors. is_empty ( ) ;
1433
+ let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor, ctor_is_relevant) ;
1243
1434
let mut witnesses = ensure_sufficient_stack ( || {
1244
1435
compute_exhaustiveness_and_usefulness ( mcx, & mut spec_matrix, false )
1245
1436
} ) ;
1246
1437
1247
- let counts_for_exhaustiveness = match ctor {
1248
- Constructor :: Missing => !missing_ctors. is_empty ( ) ,
1249
- // If there are missing constructors we'll report those instead. Since `Missing` matches
1250
- // only the wildcard rows, it matches fewer rows than this constructor, and is therefore
1251
- // guaranteed to result in the same or more witnesses. So skipping this does not
1252
- // jeopardize correctness.
1253
- _ => missing_ctors. is_empty ( ) ,
1254
- } ;
1255
- if counts_for_exhaustiveness {
1256
- // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
1257
- witnesses. apply_constructor (
1258
- pcx,
1259
- & missing_ctors,
1260
- & ctor,
1261
- report_individual_missing_ctors,
1262
- ) ;
1263
- // Accumulate the found witnesses.
1264
- ret. extend ( witnesses) ;
1265
- }
1438
+ // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
1439
+ witnesses. apply_constructor ( pcx, & missing_ctors, & ctor, report_individual_missing_ctors) ;
1440
+ // Accumulate the found witnesses.
1441
+ ret. extend ( witnesses) ;
1266
1442
1267
1443
// A parent row is useful if any of its children is.
1268
1444
for child_row in spec_matrix. rows ( ) {
0 commit comments