@@ -1598,6 +1598,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1598
1598
for subcandidate in candidate. subcandidates . iter_mut ( ) {
1599
1599
expanded_candidates. push ( subcandidate) ;
1600
1600
}
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`.
1601
1604
} else {
1602
1605
// A candidate that doesn't start with an or-pattern has nothing to
1603
1606
// expand, so it is included in the post-expansion list as-is.
@@ -1613,12 +1616,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1613
1616
expanded_candidates. as_mut_slice ( ) ,
1614
1617
) ;
1615
1618
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 ( ) {
1618
1626
if !candidate. subcandidates . is_empty ( ) {
1619
- self . finalize_or_candidate ( span , scrutinee_span , candidate) ;
1627
+ self . finalize_or_candidate ( candidate) ;
1620
1628
}
1621
1629
}
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
+ }
1622
1633
1623
1634
remainder_start. and ( remaining_candidates)
1624
1635
}
@@ -1642,8 +1653,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1642
1653
candidate. subcandidates [ 0 ] . false_edge_start_block = candidate. false_edge_start_block ;
1643
1654
}
1644
1655
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`.
1647
1658
///
1648
1659
/// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like
1649
1660
/// so:
@@ -1695,50 +1706,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1695
1706
/// |
1696
1707
/// ...
1697
1708
/// ```
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 > ) {
1704
1710
if candidate. subcandidates . is_empty ( ) {
1705
1711
return ;
1706
1712
}
1707
1713
1708
1714
self . merge_trivial_subcandidates ( candidate) ;
1709
1715
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
- }
1742
1716
}
1743
1717
1744
1718
/// 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> {
1814
1788
}
1815
1789
}
1816
1790
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
+
1817
1832
/// Pick a test to run. Which test doesn't matter as long as it is guaranteed to fully match at
1818
1833
/// least one match pair. We currently simply pick the test corresponding to the first match
1819
1834
/// pair of the first candidate in the list.
0 commit comments