@@ -1398,8 +1398,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1398
1398
/// Tests a candidate where there are only or-patterns left to test, or
1399
1399
/// forwards to [Builder::test_candidates].
1400
1400
///
1401
- /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like
1402
- /// so:
1401
+ /// Given a pattern `(P | Q, R | S)` we would like to generate a CFG like so:
1402
+ ///
1403
+ /// ```text
1404
+ /// ...
1405
+ /// +---------------+------------+
1406
+ /// | | |
1407
+ /// [ P matches ] [ Q matches ] [ otherwise ]
1408
+ /// | | |
1409
+ /// +---------------+ |
1410
+ /// | ...
1411
+ /// [ match R, S ]
1412
+ /// |
1413
+ /// ...
1414
+ /// ```
1415
+ ///
1416
+ /// In practice there are some complications:
1417
+ ///
1418
+ /// * If `P` or `Q` has bindings or type ascriptions, we must generate separate branches for
1419
+ /// each case.
1420
+ /// * If there's a guard, we must also keep branches separate as this changes how many times
1421
+ /// the guard is run.
1422
+ /// * If `P` succeeds and `R | S` fails, we know `(Q, R | S)` will fail too. So we could skip
1423
+ /// testing of `Q` in that case. Because we can't distinguish pattern failure from guard
1424
+ /// failure, we only do this optimization when there is no guard. We then get this:
1403
1425
///
1404
1426
/// ```text
1405
1427
/// [ start ]
@@ -1427,27 +1449,36 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1427
1449
/// [ Success ] [ Failure ]
1428
1450
/// ```
1429
1451
///
1430
- /// In practice there are some complications:
1431
- ///
1432
- /// * If there's a guard, then the otherwise branch of the first match on
1433
- /// `R | S` goes to a test for whether `Q` matches, and the control flow
1434
- /// doesn't merge into a single success block until after the guard is
1435
- /// tested.
1436
- /// * If neither `P` or `Q` has any bindings or type ascriptions and there
1437
- /// isn't a match guard, then we create a smaller CFG like:
1452
+ /// * If there's a guard, then the otherwise branch of the first match on `R | S` goes to a test
1453
+ /// for whether `Q` matches, and the control flow doesn't merge into a single success block
1454
+ /// until after the guard is tested. In other words, the branches are kept entirely separate:
1438
1455
///
1439
1456
/// ```text
1440
- /// ...
1441
- /// +---------------+------------+
1442
- /// | | |
1443
- /// [ P matches ] [ Q matches ] [ otherwise ]
1444
- /// | | |
1445
- /// +---------------+ |
1446
- /// | ...
1447
- /// [ match R, S ]
1457
+ /// [ start ]
1448
1458
/// |
1449
- /// ...
1459
+ /// [ match P, Q ]
1460
+ /// |
1461
+ /// +---------------------------+-------------+------------------------------------+
1462
+ /// | | | |
1463
+ /// V | V V
1464
+ /// [ P matches ] | [ Q matches ] [ otherwise ]
1465
+ /// | | | |
1466
+ /// V [ otherwise ] V |
1467
+ /// [ match R, S ] ^ [ match R, S ] |
1468
+ /// | | | |
1469
+ /// +--------------+------------+ +--------------+------------+ |
1470
+ /// | | | | | |
1471
+ /// V V V V V |
1472
+ /// [ R matches ] [ S matches ] [ R matches ] [ S matches ] [otherwise ] |
1473
+ /// | | | | | |
1474
+ /// +--------------+--------------------------+--------------+ | |
1475
+ /// | | |
1476
+ /// | +--------+
1477
+ /// | |
1478
+ /// V V
1479
+ /// [ Success ] [ Failure ]
1450
1480
/// ```
1481
+ ///
1451
1482
fn test_candidates_with_or (
1452
1483
& mut self ,
1453
1484
span : Span ,
@@ -1465,25 +1496,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1465
1496
1466
1497
let first_match_pair = first_candidate. match_pairs . remove ( 0 ) ;
1467
1498
let or_span = first_match_pair. pattern . span ;
1468
- let TestCase :: Or { pats, .. } = first_match_pair. test_case else { unreachable ! ( ) } ;
1499
+ let TestCase :: Or { pats, simple } = first_match_pair. test_case else { unreachable ! ( ) } ;
1500
+ debug ! ( "candidate={:#?}\n pats={:#?}" , first_candidate, pats) ;
1469
1501
1470
- let remainder_start = self . cfg . start_new_block ( ) ;
1471
1502
// Test the alternatives of this or-pattern.
1472
- self . test_or_pattern ( first_candidate, start_block, remainder_start, pats, or_span) ;
1503
+ let remainder_start = self . cfg . start_new_block ( ) ;
1504
+ first_candidate. subcandidates = pats
1505
+ . into_vec ( )
1506
+ . into_iter ( )
1507
+ . map ( |flat_pat| Candidate :: from_flat_pat ( flat_pat, first_candidate. has_guard ) )
1508
+ . collect ( ) ;
1509
+ let mut or_candidate_refs: Vec < _ > = first_candidate. subcandidates . iter_mut ( ) . collect ( ) ;
1510
+ self . match_candidates (
1511
+ or_span,
1512
+ or_span,
1513
+ start_block,
1514
+ remainder_start,
1515
+ & mut or_candidate_refs,
1516
+ ) ;
1517
+
1518
+ // Whether we need to keep the or-pattern branches separate.
1519
+ let keep_branches_separate = first_candidate. has_guard || !simple;
1520
+ if !keep_branches_separate {
1521
+ self . merge_subcandidates ( first_candidate, self . source_info ( or_span) ) ;
1522
+ }
1473
1523
1474
1524
if !first_candidate. match_pairs . is_empty ( ) {
1475
- // If more match pairs remain, test them after each subcandidate.
1476
- // We could add them to the or-candidates before the call to `test_or_pattern` but this
1477
- // would make it impossible to detect simplifiable or-patterns. That would guarantee
1478
- // exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
1525
+ // If more match pairs remain, test them after each subcandidate. If we merged them,
1526
+ // then this will only test `first_candidate`.
1479
1527
let remaining_match_pairs = mem:: take ( & mut first_candidate. match_pairs ) ;
1480
1528
first_candidate. visit_leaves ( |leaf_candidate| {
1481
1529
assert ! ( leaf_candidate. match_pairs. is_empty( ) ) ;
1482
1530
leaf_candidate. match_pairs . extend ( remaining_match_pairs. iter ( ) . cloned ( ) ) ;
1483
1531
let or_start = leaf_candidate. pre_binding_block . unwrap ( ) ;
1484
- // In a case like `(a | b, c | d)`, if `a` succeeds and `c | d` fails, we know `(b,
1485
- // c | d)` will fail too. If there is no guard, we skip testing of `b` by branching
1486
- // directly to `remainder_start`. If there is a guard, we have to try `(b, c | d)`.
1532
+ // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q,
1533
+ // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching
1534
+ // directly to `remainder_start`. If there is a guard, `or_otherwise` can be reached
1535
+ // by guard failure as well, so we can't skip `Q`.
1487
1536
let or_otherwise = leaf_candidate. otherwise_block . unwrap_or ( remainder_start) ;
1488
1537
self . test_candidates_with_or (
1489
1538
span,
@@ -1505,68 +1554,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
1505
1554
) ;
1506
1555
}
1507
1556
1508
- #[ instrument(
1509
- skip( self , start_block, otherwise_block, or_span, candidate, pats) ,
1510
- level = "debug"
1511
- ) ]
1512
- fn test_or_pattern < ' pat > (
1513
- & mut self ,
1514
- candidate : & mut Candidate < ' pat , ' tcx > ,
1515
- start_block : BasicBlock ,
1516
- otherwise_block : BasicBlock ,
1517
- pats : Box < [ FlatPat < ' pat , ' tcx > ] > ,
1518
- or_span : Span ,
1519
- ) {
1520
- debug ! ( "candidate={:#?}\n pats={:#?}" , candidate, pats) ;
1521
- let mut or_candidates: Vec < _ > = pats
1522
- . into_vec ( )
1523
- . into_iter ( )
1524
- . map ( |flat_pat| Candidate :: from_flat_pat ( flat_pat, candidate. has_guard ) )
1525
- . collect ( ) ;
1526
- let mut or_candidate_refs: Vec < _ > = or_candidates. iter_mut ( ) . collect ( ) ;
1527
- self . match_candidates (
1528
- or_span,
1529
- or_span,
1530
- start_block,
1531
- otherwise_block,
1532
- & mut or_candidate_refs,
1533
- ) ;
1534
- candidate. subcandidates = or_candidates;
1535
- self . merge_trivial_subcandidates ( candidate, self . source_info ( or_span) ) ;
1536
- }
1537
-
1538
- /// Try to merge all of the subcandidates of the given candidate into one.
1557
+ /// Merge all of the subcandidates of the given candidate into one.
1539
1558
/// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`.
1540
- fn merge_trivial_subcandidates (
1559
+ fn merge_subcandidates (
1541
1560
& mut self ,
1542
1561
candidate : & mut Candidate < ' _ , ' tcx > ,
1543
1562
source_info : SourceInfo ,
1544
1563
) {
1545
- if candidate. subcandidates . is_empty ( ) || candidate. has_guard {
1546
- // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard.
1547
- return ;
1548
- }
1549
-
1550
- let mut can_merge = true ;
1551
-
1552
- // Not `Iterator::all` because we don't want to short-circuit.
1553
- for subcandidate in & mut candidate. subcandidates {
1554
- self . merge_trivial_subcandidates ( subcandidate, source_info) ;
1555
-
1556
- // FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
1557
- can_merge &= subcandidate. subcandidates . is_empty ( )
1558
- && subcandidate. bindings . is_empty ( )
1559
- && subcandidate. ascriptions . is_empty ( ) ;
1560
- }
1561
-
1562
- if can_merge {
1563
- let any_matches = self . cfg . start_new_block ( ) ;
1564
- for subcandidate in mem:: take ( & mut candidate. subcandidates ) {
1565
- let or_block = subcandidate. pre_binding_block . unwrap ( ) ;
1566
- self . cfg . goto ( or_block, source_info, any_matches) ;
1567
- }
1568
- candidate. pre_binding_block = Some ( any_matches) ;
1569
- }
1564
+ let any_matches = self . cfg . start_new_block ( ) ;
1565
+ candidate. visit_leaves ( |leaf_candidate| {
1566
+ let or_block = leaf_candidate. pre_binding_block . unwrap ( ) ;
1567
+ self . cfg . goto ( or_block, source_info, any_matches) ;
1568
+ } ) ;
1569
+ candidate. subcandidates . clear ( ) ;
1570
+ candidate. pre_binding_block = Some ( any_matches) ;
1570
1571
}
1571
1572
1572
1573
/// Pick a test to run. Which test doesn't matter as long as it is guaranteed to fully match at
0 commit comments