462462//! # Or-patterns
463463//!
464464//! 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".
467468//!
468469//! This makes usefulness tracking subtle, because we also want to compute whether an alternative of
469470//! 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> {
875876 return Ok ( ( smallvec ! [ Constructor :: PrivateUninhabited ] , vec ! [ ] ) ) ;
876877 }
877878
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+
878884 let ctors_for_ty = cx. ctors_for_ty ( & self . ty ) ?;
879885 debug ! ( ?ctors_for_ty) ;
880886
@@ -968,10 +974,6 @@ impl<'p, Cx: PatCx> PatStack<'p, Cx> {
968974 PatStack { pats : smallvec ! [ PatOrWild :: Pat ( pat) ] , relevant : true }
969975 }
970976
971- fn is_empty ( & self ) -> bool {
972- self . pats . is_empty ( )
973- }
974-
975977 fn len ( & self ) -> usize {
976978 self . pats . len ( )
977979 }
@@ -984,10 +986,10 @@ impl<'p, Cx: PatCx> PatStack<'p, Cx> {
984986 self . pats . iter ( ) . copied ( )
985987 }
986988
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.
989991 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| {
991993 let mut new = self . clone ( ) ;
992994 new. pats [ 0 ] = pat;
993995 new
@@ -1057,10 +1059,6 @@ struct MatrixRow<'p, Cx: PatCx> {
10571059}
10581060
10591061impl < ' p , Cx : PatCx > MatrixRow < ' p , Cx > {
1060- fn is_empty ( & self ) -> bool {
1061- self . pats . is_empty ( )
1062- }
1063-
10641062 fn len ( & self ) -> usize {
10651063 self . pats . len ( )
10661064 }
@@ -1073,12 +1071,14 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> {
10731071 self . pats . iter ( )
10741072 }
10751073
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 {
10801080 pats : patstack,
1081- parent_row : self . parent_row ,
1081+ parent_row,
10821082 is_under_guard : self . is_under_guard ,
10831083 useful : false ,
10841084 intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::expand_and_push`.
@@ -1100,7 +1100,7 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> {
11001100 parent_row,
11011101 is_under_guard : self . is_under_guard ,
11021102 useful : false ,
1103- intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::expand_and_push `.
1103+ intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::push `.
11041104 } )
11051105 }
11061106}
@@ -1116,7 +1116,7 @@ impl<'p, Cx: PatCx> fmt::Debug for MatrixRow<'p, Cx> {
11161116/// Invariant: each row must have the same length, and each column must have the same type.
11171117///
11181118/// Invariant: the first column must not contain or-patterns. This is handled by
1119- /// [`Matrix::expand_and_push `].
1119+ /// [`Matrix::push `].
11201120///
11211121/// In fact each column corresponds to a place inside the scrutinee of the match. E.g. after
11221122/// specializing `(,)` and `Some` on a pattern of type `(Option<u32>, bool)`, the first column of
@@ -1136,19 +1136,10 @@ struct Matrix<'p, Cx: PatCx> {
11361136}
11371137
11381138impl < ' 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) ;
11521143 }
11531144
11541145 /// Build a new matrix from an iterator of `MatchArm`s.
@@ -1170,9 +1161,9 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> {
11701161 parent_row : arm_id,
11711162 is_under_guard : arm. has_guard ,
11721163 useful : false ,
1173- intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::expand_and_push `.
1164+ intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::push `.
11741165 } ;
1175- matrix. expand_and_push ( v) ;
1166+ matrix. push ( v) ;
11761167 }
11771168 matrix
11781169 }
@@ -1209,22 +1200,38 @@ impl<'p, Cx: PatCx> Matrix<'p, Cx> {
12091200 ctor : & Constructor < Cx > ,
12101201 ctor_is_relevant : bool ,
12111202 ) -> 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+ }
12251214 }
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)
12261234 }
1227- Ok ( matrix)
12281235 }
12291236
12301237 /// Recover row usefulness and intersection information from a processed specialized matrix.
@@ -1465,7 +1472,9 @@ impl<Cx: PatCx> WitnessMatrix<Cx> {
14651472 missing_ctors : & [ Constructor < Cx > ] ,
14661473 ctor : & Constructor < Cx > ,
14671474 ) {
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 ) {
14691478 return ;
14701479 }
14711480 if matches ! ( ctor, Constructor :: Missing ) {
@@ -1715,48 +1724,6 @@ pub enum Usefulness<'p, Cx: PatCx> {
17151724 Redundant ,
17161725}
17171726
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-
17601727/// The output of checking a match for exhaustiveness and arm usefulness.
17611728pub struct UsefulnessReport < ' p , Cx : PatCx > {
17621729 /// 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>(
17931760 . copied ( )
17941761 . map ( |arm| {
17951762 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+ } ;
17971777 debug ! ( ?usefulness) ;
17981778 ( arm, usefulness)
17991779 } )
18001780 . collect ( ) ;
18011781
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 ( ) ;
18151783
18161784 Ok ( UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses, arm_intersections } )
18171785}
0 commit comments