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
@@ -669,11 +829,15 @@ impl fmt::Display for ValidityConstraint {
669
829
struct PatStack < ' a , ' p , Cx : TypeCx > {
670
830
// Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well.
671
831
pats : SmallVec < [ & ' a DeconstructedPat < ' p , Cx > ; 2 ] > ,
832
+ /// Sometimes we know that as far as this row is concerned, the current case is already handled
833
+ /// by a different, more general, case. When the case is irrelevant for all rows this allows us
834
+ /// to skip a case entirely. This is purely an optimization. See at the top for details.
835
+ relevant : bool ,
672
836
}
673
837
674
838
impl < ' a , ' p , Cx : TypeCx > PatStack < ' a , ' p , Cx > {
675
839
fn from_pattern ( pat : & ' a DeconstructedPat < ' p , Cx > ) -> Self {
676
- PatStack { pats : smallvec ! [ pat] }
840
+ PatStack { pats : smallvec ! [ pat] , relevant : true }
677
841
}
678
842
679
843
fn is_empty ( & self ) -> bool {
@@ -708,12 +872,17 @@ impl<'a, 'p, Cx: TypeCx> PatStack<'a, 'p, Cx> {
708
872
& self ,
709
873
pcx : & PlaceCtxt < ' a , ' p , Cx > ,
710
874
ctor : & Constructor < Cx > ,
875
+ ctor_is_relevant : bool ,
711
876
) -> PatStack < ' a , ' p , Cx > {
712
877
// We pop the head pattern and push the new fields extracted from the arguments of
713
878
// `self.head()`.
714
879
let mut new_pats = self . head ( ) . specialize ( pcx, ctor) ;
715
880
new_pats. extend_from_slice ( & self . pats [ 1 ..] ) ;
716
- PatStack { pats : new_pats }
881
+ // `ctor` is relevant for this row if it is the actual constructor of this row, or if the
882
+ // row has a wildcard and `ctor` is relevant for wildcards.
883
+ let ctor_is_relevant =
884
+ !matches ! ( self . head( ) . ctor( ) , Constructor :: Wildcard ) || ctor_is_relevant;
885
+ PatStack { pats : new_pats, relevant : self . relevant && ctor_is_relevant }
717
886
}
718
887
}
719
888
@@ -779,10 +948,11 @@ impl<'a, 'p, Cx: TypeCx> MatrixRow<'a, 'p, Cx> {
779
948
& self ,
780
949
pcx : & PlaceCtxt < ' a , ' p , Cx > ,
781
950
ctor : & Constructor < Cx > ,
951
+ ctor_is_relevant : bool ,
782
952
parent_row : usize ,
783
953
) -> MatrixRow < ' a , ' p , Cx > {
784
954
MatrixRow {
785
- pats : self . pats . pop_head_constructor ( pcx, ctor) ,
955
+ pats : self . pats . pop_head_constructor ( pcx, ctor, ctor_is_relevant ) ,
786
956
parent_row,
787
957
is_under_guard : self . is_under_guard ,
788
958
useful : false ,
@@ -897,8 +1067,9 @@ impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> {
897
1067
& self ,
898
1068
pcx : & PlaceCtxt < ' a , ' p , Cx > ,
899
1069
ctor : & Constructor < Cx > ,
1070
+ ctor_is_relevant : bool ,
900
1071
) -> Matrix < ' a , ' p , Cx > {
901
- let wildcard_row = self . wildcard_row . pop_head_constructor ( pcx, ctor) ;
1072
+ let wildcard_row = self . wildcard_row . pop_head_constructor ( pcx, ctor, ctor_is_relevant ) ;
902
1073
let new_validity = self . place_validity [ 0 ] . specialize ( ctor) ;
903
1074
let new_place_validity = std:: iter:: repeat ( new_validity)
904
1075
. take ( ctor. arity ( pcx) )
@@ -908,7 +1079,7 @@ impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> {
908
1079
Matrix { rows : Vec :: new ( ) , wildcard_row, place_validity : new_place_validity } ;
909
1080
for ( i, row) in self . rows ( ) . enumerate ( ) {
910
1081
if ctor. is_covered_by ( pcx, row. head ( ) . ctor ( ) ) {
911
- let new_row = row. pop_head_constructor ( pcx, ctor, i) ;
1082
+ let new_row = row. pop_head_constructor ( pcx, ctor, ctor_is_relevant , i) ;
912
1083
matrix. expand_and_push ( new_row) ;
913
1084
}
914
1085
}
@@ -1108,7 +1279,10 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
1108
1279
if matches ! ( ctor, Constructor :: Missing ) {
1109
1280
// We got the special `Missing` constructor that stands for the constructors not present
1110
1281
// in the match.
1111
- if !report_individual_missing_ctors {
1282
+ if missing_ctors. is_empty ( ) {
1283
+ // Nothing to report.
1284
+ * self = Self :: empty ( ) ;
1285
+ } else if !report_individual_missing_ctors {
1112
1286
// Report `_` as missing.
1113
1287
let pat = WitnessPat :: wild_from_ctor ( pcx, Constructor :: Wildcard ) ;
1114
1288
self . push_pattern ( pat) ;
@@ -1167,6 +1341,13 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1167
1341
) -> WitnessMatrix < Cx > {
1168
1342
debug_assert ! ( matrix. rows( ) . all( |r| r. len( ) == matrix. column_count( ) ) ) ;
1169
1343
1344
+ if !matrix. wildcard_row . relevant && matrix. rows ( ) . all ( |r| !r. pats . relevant ) {
1345
+ // Here we know that nothing will contribute further to exhaustiveness or usefulness. This
1346
+ // is purely an optimization: skipping this check doesn't affect correctness. See the top of
1347
+ // the file for details.
1348
+ return WitnessMatrix :: empty ( ) ;
1349
+ }
1350
+
1170
1351
let Some ( ty) = matrix. head_ty ( mcx) else {
1171
1352
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
1172
1353
// A row is useful iff it has no (unguarded) rows above it.
@@ -1179,8 +1360,14 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1179
1360
return WitnessMatrix :: empty ( ) ;
1180
1361
}
1181
1362
}
1182
- // No (unguarded) rows, so the match is not exhaustive. We return a new witness.
1183
- return WitnessMatrix :: unit_witness ( ) ;
1363
+ // No (unguarded) rows, so the match is not exhaustive. We return a new witness unless
1364
+ // irrelevant.
1365
+ return if matrix. wildcard_row . relevant {
1366
+ WitnessMatrix :: unit_witness ( )
1367
+ } else {
1368
+ // We choose to not report anything here; see at the top for details.
1369
+ WitnessMatrix :: empty ( )
1370
+ } ;
1184
1371
} ;
1185
1372
1186
1373
debug ! ( "ty: {ty:?}" ) ;
@@ -1223,32 +1410,21 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1223
1410
1224
1411
let mut ret = WitnessMatrix :: empty ( ) ;
1225
1412
for ctor in split_ctors {
1226
- debug ! ( "specialize({:?})" , ctor) ;
1227
1413
// Dig into rows that match `ctor`.
1228
- let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor) ;
1414
+ debug ! ( "specialize({:?})" , ctor) ;
1415
+ // `ctor` is *irrelevant* if there's another constructor in `split_ctors` that matches
1416
+ // strictly fewer rows. In that case we can sometimes skip it. See the top of the file for
1417
+ // details.
1418
+ let ctor_is_relevant = matches ! ( ctor, Constructor :: Missing ) || missing_ctors. is_empty ( ) ;
1419
+ let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor, ctor_is_relevant) ;
1229
1420
let mut witnesses = ensure_sufficient_stack ( || {
1230
1421
compute_exhaustiveness_and_usefulness ( mcx, & mut spec_matrix, false )
1231
1422
} ) ;
1232
1423
1233
- let counts_for_exhaustiveness = match ctor {
1234
- Constructor :: Missing => !missing_ctors. is_empty ( ) ,
1235
- // If there are missing constructors we'll report those instead. Since `Missing` matches
1236
- // only the wildcard rows, it matches fewer rows than this constructor, and is therefore
1237
- // guaranteed to result in the same or more witnesses. So skipping this does not
1238
- // jeopardize correctness.
1239
- _ => missing_ctors. is_empty ( ) ,
1240
- } ;
1241
- if counts_for_exhaustiveness {
1242
- // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
1243
- witnesses. apply_constructor (
1244
- pcx,
1245
- & missing_ctors,
1246
- & ctor,
1247
- report_individual_missing_ctors,
1248
- ) ;
1249
- // Accumulate the found witnesses.
1250
- ret. extend ( witnesses) ;
1251
- }
1424
+ // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
1425
+ witnesses. apply_constructor ( pcx, & missing_ctors, & ctor, report_individual_missing_ctors) ;
1426
+ // Accumulate the found witnesses.
1427
+ ret. extend ( witnesses) ;
1252
1428
1253
1429
// A parent row is useful if any of its children is.
1254
1430
for child_row in spec_matrix. rows ( ) {
0 commit comments