@@ -1598,6 +1598,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
15981598 for subcandidate in candidate. subcandidates . iter_mut ( ) {
15991599 expanded_candidates. push ( subcandidate) ;
16001600 }
1601+ // Note that the subcandidates have been added to `expanded_candidates`,
1602+ // but `candidate` itself has not. If the last candidate has more match pairs,
1603+ // they are handled separately by `test_remaining_match_pairs_after_or`.
16011604 } else {
16021605 // A candidate that doesn't start with an or-pattern has nothing to
16031606 // expand, so it is included in the post-expansion list as-is.
@@ -1613,12 +1616,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16131616 expanded_candidates. as_mut_slice ( ) ,
16141617 ) ;
16151618
1616- // Simplify subcandidates and process any leftover match pairs.
1617- for candidate in candidates_to_expand {
1619+ // Postprocess subcandidates, and process any leftover match pairs.
1620+ // (Only the last candidate can possibly have more match pairs.)
1621+ debug_assert ! ( {
1622+ let mut all_except_last = candidates_to_expand. iter( ) . rev( ) . skip( 1 ) ;
1623+ all_except_last. all( |candidate| candidate. match_pairs. is_empty( ) )
1624+ } ) ;
1625+ for candidate in candidates_to_expand. iter_mut ( ) {
16181626 if !candidate. subcandidates . is_empty ( ) {
1619- self . finalize_or_candidate ( span , scrutinee_span , candidate) ;
1627+ self . finalize_or_candidate ( candidate) ;
16201628 }
16211629 }
1630+ if let Some ( last_candidate) = candidates_to_expand. last_mut ( ) {
1631+ self . test_remaining_match_pairs_after_or ( span, scrutinee_span, last_candidate) ;
1632+ }
16221633
16231634 remainder_start. and ( remaining_candidates)
16241635 }
@@ -1642,8 +1653,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16421653 candidate. subcandidates [ 0 ] . false_edge_start_block = candidate. false_edge_start_block ;
16431654 }
16441655
1645- /// Simplify subcandidates and process any leftover match pairs. The candidate should have been
1646- /// expanded with `create_or_subcandidates`.
1656+ /// Simplify subcandidates and remove `is_never` subcandidates.
1657+ /// The candidate should have been expanded with `create_or_subcandidates`.
16471658 ///
16481659 /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like
16491660 /// so:
@@ -1695,50 +1706,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
16951706 /// |
16961707 /// ...
16971708 /// ```
1698- fn finalize_or_candidate (
1699- & mut self ,
1700- span : Span ,
1701- scrutinee_span : Span ,
1702- candidate : & mut Candidate < ' _ , ' tcx > ,
1703- ) {
1709+ fn finalize_or_candidate ( & mut self , candidate : & mut Candidate < ' _ , ' tcx > ) {
17041710 if candidate. subcandidates . is_empty ( ) {
17051711 return ;
17061712 }
17071713
17081714 self . merge_trivial_subcandidates ( candidate) ;
17091715 self . remove_never_subcandidates ( candidate) ;
1710-
1711- if !candidate. match_pairs . is_empty ( ) {
1712- let or_span = candidate. or_span . unwrap_or ( candidate. extra_data . span ) ;
1713- let source_info = self . source_info ( or_span) ;
1714- // If more match pairs remain, test them after each subcandidate.
1715- // We could add them to the or-candidates before the call to `test_or_pattern` but this
1716- // would make it impossible to detect simplifiable or-patterns. That would guarantee
1717- // exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
1718- let mut last_otherwise = None ;
1719- candidate. visit_leaves ( |leaf_candidate| {
1720- last_otherwise = leaf_candidate. otherwise_block ;
1721- } ) ;
1722- let remaining_match_pairs = mem:: take ( & mut candidate. match_pairs ) ;
1723- candidate. visit_leaves ( |leaf_candidate| {
1724- assert ! ( leaf_candidate. match_pairs. is_empty( ) ) ;
1725- leaf_candidate. match_pairs . extend ( remaining_match_pairs. iter ( ) . cloned ( ) ) ;
1726- let or_start = leaf_candidate. pre_binding_block . unwrap ( ) ;
1727- let otherwise =
1728- self . match_candidates ( span, scrutinee_span, or_start, & mut [ leaf_candidate] ) ;
1729- // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q,
1730- // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching
1731- // directly to `last_otherwise`. If there is a guard,
1732- // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we
1733- // can't skip `Q`.
1734- let or_otherwise = if leaf_candidate. has_guard {
1735- leaf_candidate. otherwise_block . unwrap ( )
1736- } else {
1737- last_otherwise. unwrap ( )
1738- } ;
1739- self . cfg . goto ( otherwise, source_info, or_otherwise) ;
1740- } ) ;
1741- }
17421716 }
17431717
17441718 /// Try to merge all of the subcandidates of the given candidate into one. This avoids
@@ -1814,6 +1788,47 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
18141788 }
18151789 }
18161790
1791+ /// If more match pairs remain, test them after each subcandidate.
1792+ /// We could have added them to the or-candidates during or-pattern expansion, but that
1793+ /// would make it impossible to detect simplifiable or-patterns. That would guarantee
1794+ /// exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
1795+ fn test_remaining_match_pairs_after_or (
1796+ & mut self ,
1797+ span : Span ,
1798+ scrutinee_span : Span ,
1799+ candidate : & mut Candidate < ' _ , ' tcx > ,
1800+ ) {
1801+ if candidate. match_pairs . is_empty ( ) {
1802+ return ;
1803+ }
1804+
1805+ let or_span = candidate. or_span . unwrap_or ( candidate. extra_data . span ) ;
1806+ let source_info = self . source_info ( or_span) ;
1807+ let mut last_otherwise = None ;
1808+ candidate. visit_leaves ( |leaf_candidate| {
1809+ last_otherwise = leaf_candidate. otherwise_block ;
1810+ } ) ;
1811+ let remaining_match_pairs = mem:: take ( & mut candidate. match_pairs ) ;
1812+ candidate. visit_leaves ( |leaf_candidate| {
1813+ assert ! ( leaf_candidate. match_pairs. is_empty( ) ) ;
1814+ leaf_candidate. match_pairs . extend ( remaining_match_pairs. iter ( ) . cloned ( ) ) ;
1815+ let or_start = leaf_candidate. pre_binding_block . unwrap ( ) ;
1816+ let otherwise =
1817+ self . match_candidates ( span, scrutinee_span, or_start, & mut [ leaf_candidate] ) ;
1818+ // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q,
1819+ // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching
1820+ // directly to `last_otherwise`. If there is a guard,
1821+ // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we
1822+ // can't skip `Q`.
1823+ let or_otherwise = if leaf_candidate. has_guard {
1824+ leaf_candidate. otherwise_block . unwrap ( )
1825+ } else {
1826+ last_otherwise. unwrap ( )
1827+ } ;
1828+ self . cfg . goto ( otherwise, source_info, or_otherwise) ;
1829+ } ) ;
1830+ }
1831+
18171832 /// Pick a test to run. Which test doesn't matter as long as it is guaranteed to fully match at
18181833 /// least one match pair. We currently simply pick the test corresponding to the first match
18191834 /// pair of the first candidate in the list.
0 commit comments