300300//!
301301//!
302302//!
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+ //!
303463//! # Or-patterns
304464//!
305465//! 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 {
674834struct PatStack < ' a , ' p , Cx : TypeCx > {
675835 // Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well.
676836 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 ,
677841}
678842
679843impl < ' a , ' p , Cx : TypeCx > PatStack < ' a , ' p , Cx > {
680844 fn from_pattern ( pat : & ' a DeconstructedPat < ' p , Cx > ) -> Self {
681- PatStack { pats : smallvec ! [ pat] }
845+ PatStack { pats : smallvec ! [ pat] , relevant : true }
682846 }
683847
684848 fn is_empty ( & self ) -> bool {
@@ -713,12 +877,17 @@ impl<'a, 'p, Cx: TypeCx> PatStack<'a, 'p, Cx> {
713877 & self ,
714878 pcx : & PlaceCtxt < ' a , ' p , Cx > ,
715879 ctor : & Constructor < Cx > ,
880+ ctor_is_relevant : bool ,
716881 ) -> PatStack < ' a , ' p , Cx > {
717882 // We pop the head pattern and push the new fields extracted from the arguments of
718883 // `self.head()`.
719884 let mut new_pats = self . head ( ) . specialize ( pcx, ctor) ;
720885 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 }
722891 }
723892}
724893
@@ -784,10 +953,11 @@ impl<'a, 'p, Cx: TypeCx> MatrixRow<'a, 'p, Cx> {
784953 & self ,
785954 pcx : & PlaceCtxt < ' a , ' p , Cx > ,
786955 ctor : & Constructor < Cx > ,
956+ ctor_is_relevant : bool ,
787957 parent_row : usize ,
788958 ) -> MatrixRow < ' a , ' p , Cx > {
789959 MatrixRow {
790- pats : self . pats . pop_head_constructor ( pcx, ctor) ,
960+ pats : self . pats . pop_head_constructor ( pcx, ctor, ctor_is_relevant ) ,
791961 parent_row,
792962 is_under_guard : self . is_under_guard ,
793963 useful : false ,
@@ -913,8 +1083,9 @@ impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> {
9131083 & self ,
9141084 pcx : & PlaceCtxt < ' a , ' p , Cx > ,
9151085 ctor : & Constructor < Cx > ,
1086+ ctor_is_relevant : bool ,
9161087 ) -> 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 ) ;
9181089 let new_validity = self . place_validity [ 0 ] . specialize ( ctor) ;
9191090 let new_place_validity = std:: iter:: repeat ( new_validity)
9201091 . take ( ctor. arity ( pcx) )
@@ -924,7 +1095,7 @@ impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> {
9241095 Matrix { rows : Vec :: new ( ) , wildcard_row, place_validity : new_place_validity } ;
9251096 for ( i, row) in self . rows ( ) . enumerate ( ) {
9261097 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) ;
9281099 matrix. expand_and_push ( new_row) ;
9291100 }
9301101 }
@@ -1122,7 +1293,10 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
11221293 if matches ! ( ctor, Constructor :: Missing ) {
11231294 // We got the special `Missing` constructor that stands for the constructors not present
11241295 // 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 {
11261300 // Report `_` as missing.
11271301 let pat = WitnessPat :: wild_from_ctor ( pcx, Constructor :: Wildcard ) ;
11281302 self . push_pattern ( pat) ;
@@ -1181,6 +1355,13 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
11811355) -> WitnessMatrix < Cx > {
11821356 debug_assert ! ( matrix. rows( ) . all( |r| r. len( ) == matrix. column_count( ) ) ) ;
11831357
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+
11841365 let Some ( ty) = matrix. head_ty ( ) else {
11851366 // The base case: there are no columns in the matrix. We are morally pattern-matching on ().
11861367 // 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>(
11931374 return WitnessMatrix :: empty ( ) ;
11941375 }
11951376 }
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+ } ;
11981385 } ;
11991386
12001387 debug ! ( "ty: {ty:?}" ) ;
@@ -1237,32 +1424,21 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
12371424
12381425 let mut ret = WitnessMatrix :: empty ( ) ;
12391426 for ctor in split_ctors {
1240- debug ! ( "specialize({:?})" , ctor) ;
12411427 // 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) ;
12431434 let mut witnesses = ensure_sufficient_stack ( || {
12441435 compute_exhaustiveness_and_usefulness ( mcx, & mut spec_matrix, false )
12451436 } ) ;
12461437
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) ;
12661442
12671443 // A parent row is useful if any of its children is.
12681444 for child_row in spec_matrix. rows ( ) {
0 commit comments