@@ -1328,6 +1328,83 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
1328
1328
}
1329
1329
}
1330
1330
1331
+ /// Collect ranges that overlap like `lo..=overlap`/`overlap..=hi`. Must be called during
1332
+ /// exhaustiveness checking, if we find a singleton range after constructor splitting. This reuses
1333
+ /// row intersection information to only detect ranges that truly overlap.
1334
+ ///
1335
+ /// If two ranges overlapped, the split set will contain their intersection as a singleton.
1336
+ /// Specialization will then select rows that match the overlap, and exhaustiveness will compute
1337
+ /// which rows have an intersection that includes the overlap. That gives us all the info we need to
1338
+ /// compute overlapping ranges without false positives.
1339
+ ///
1340
+ /// We can however get false negatives because exhaustiveness does not explore all cases. See the
1341
+ /// section on relevancy at the top of the file.
1342
+ fn collect_overlapping_range_endpoints < ' p , Cx : TypeCx > (
1343
+ overlap_range : IntRange ,
1344
+ matrix : & Matrix < ' p , Cx > ,
1345
+ specialized_matrix : & Matrix < ' p , Cx > ,
1346
+ overlapping_range_endpoints : & mut Vec < OverlappingRanges < ' p , Cx > > ,
1347
+ ) {
1348
+ let overlap = overlap_range. lo ;
1349
+ // Ranges that look like `lo..=overlap`.
1350
+ let mut prefixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1351
+ // Ranges that look like `overlap..=hi`.
1352
+ let mut suffixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1353
+ // Iterate on patterns that contained `overlap`. We iterate on `specialized_matrix` which
1354
+ // contains only rows that matched the current `ctor` as well as accurate intersection
1355
+ // information. It doesn't contain the column that contains the range; that can be found in
1356
+ // `matrix`.
1357
+ for ( child_row_id, child_row) in specialized_matrix. rows ( ) . enumerate ( ) {
1358
+ let PatOrWild :: Pat ( pat) = matrix. rows [ child_row. parent_row ] . head ( ) else { continue } ;
1359
+ let Constructor :: IntRange ( this_range) = pat. ctor ( ) else { continue } ;
1360
+ // Don't lint when one of the ranges is a singleton.
1361
+ if this_range. is_singleton ( ) {
1362
+ continue ;
1363
+ }
1364
+ if this_range. lo == overlap {
1365
+ // `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
1366
+ // ranges that look like `lo..=overlap`.
1367
+ if !prefixes. is_empty ( ) {
1368
+ let overlaps_with: Vec < _ > = prefixes
1369
+ . iter ( )
1370
+ . filter ( |& & ( other_child_row_id, _) | {
1371
+ child_row. intersects . contains ( other_child_row_id)
1372
+ } )
1373
+ . map ( |& ( _, pat) | pat)
1374
+ . collect ( ) ;
1375
+ if !overlaps_with. is_empty ( ) {
1376
+ overlapping_range_endpoints. push ( OverlappingRanges {
1377
+ pat,
1378
+ overlaps_on : overlap_range,
1379
+ overlaps_with,
1380
+ } ) ;
1381
+ }
1382
+ }
1383
+ suffixes. push ( ( child_row_id, pat) )
1384
+ } else if this_range. hi == overlap. plus_one ( ) {
1385
+ // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
1386
+ // ranges that look like `overlap..=hi`.
1387
+ if !suffixes. is_empty ( ) {
1388
+ let overlaps_with: Vec < _ > = suffixes
1389
+ . iter ( )
1390
+ . filter ( |& & ( other_child_row_id, _) | {
1391
+ child_row. intersects . contains ( other_child_row_id)
1392
+ } )
1393
+ . map ( |& ( _, pat) | pat)
1394
+ . collect ( ) ;
1395
+ if !overlaps_with. is_empty ( ) {
1396
+ overlapping_range_endpoints. push ( OverlappingRanges {
1397
+ pat,
1398
+ overlaps_on : overlap_range,
1399
+ overlaps_with,
1400
+ } ) ;
1401
+ }
1402
+ }
1403
+ prefixes. push ( ( child_row_id, pat) )
1404
+ }
1405
+ }
1406
+ }
1407
+
1331
1408
/// The core of the algorithm.
1332
1409
///
1333
1410
/// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks
@@ -1346,6 +1423,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
1346
1423
fn compute_exhaustiveness_and_usefulness < ' a , ' p , Cx : TypeCx > (
1347
1424
mcx : MatchCtxt < ' a , ' p , Cx > ,
1348
1425
matrix : & mut Matrix < ' p , Cx > ,
1426
+ overlapping_range_endpoints : & mut Vec < OverlappingRanges < ' p , Cx > > ,
1349
1427
is_top_level : bool ,
1350
1428
) -> Result < WitnessMatrix < Cx > , Cx :: Error > {
1351
1429
debug_assert ! ( matrix. rows( ) . all( |r| r. len( ) == matrix. column_count( ) ) ) ;
@@ -1425,7 +1503,12 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1425
1503
let ctor_is_relevant = matches ! ( ctor, Constructor :: Missing ) || missing_ctors. is_empty ( ) ;
1426
1504
let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor, ctor_is_relevant) ;
1427
1505
let mut witnesses = ensure_sufficient_stack ( || {
1428
- compute_exhaustiveness_and_usefulness ( mcx, & mut spec_matrix, false )
1506
+ compute_exhaustiveness_and_usefulness (
1507
+ mcx,
1508
+ & mut spec_matrix,
1509
+ overlapping_range_endpoints,
1510
+ false ,
1511
+ )
1429
1512
} ) ?;
1430
1513
1431
1514
// Transform witnesses for `spec_matrix` into witnesses for `matrix`.
@@ -1447,6 +1530,21 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1447
1530
}
1448
1531
}
1449
1532
}
1533
+
1534
+ // Detect ranges that overlap on their endpoints.
1535
+ if let Constructor :: IntRange ( overlap_range) = ctor {
1536
+ if overlap_range. is_singleton ( )
1537
+ && spec_matrix. rows . len ( ) >= 2
1538
+ && spec_matrix. rows . iter ( ) . any ( |row| !row. intersects . is_empty ( ) )
1539
+ {
1540
+ collect_overlapping_range_endpoints (
1541
+ overlap_range,
1542
+ matrix,
1543
+ & spec_matrix,
1544
+ overlapping_range_endpoints,
1545
+ ) ;
1546
+ }
1547
+ }
1450
1548
}
1451
1549
1452
1550
// Record usefulness in the patterns.
@@ -1487,6 +1585,7 @@ pub struct UsefulnessReport<'p, Cx: TypeCx> {
1487
1585
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
1488
1586
/// exhaustiveness.
1489
1587
pub non_exhaustiveness_witnesses : Vec < WitnessPat < Cx > > ,
1588
+ pub overlapping_range_endpoints : Vec < OverlappingRanges < ' p , Cx > > ,
1490
1589
}
1491
1590
1492
1591
/// Computes whether a match is exhaustive and which of its arms are useful.
@@ -1497,9 +1596,14 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
1497
1596
scrut_ty : Cx :: Ty ,
1498
1597
scrut_validity : ValidityConstraint ,
1499
1598
) -> Result < UsefulnessReport < ' p , Cx > , Cx :: Error > {
1599
+ let mut overlapping_range_endpoints = Vec :: new ( ) ;
1500
1600
let mut matrix = Matrix :: new ( arms, scrut_ty, scrut_validity) ;
1501
- let non_exhaustiveness_witnesses =
1502
- compute_exhaustiveness_and_usefulness ( cx, & mut matrix, true ) ?;
1601
+ let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness (
1602
+ cx,
1603
+ & mut matrix,
1604
+ & mut overlapping_range_endpoints,
1605
+ true ,
1606
+ ) ?;
1503
1607
1504
1608
let non_exhaustiveness_witnesses: Vec < _ > = non_exhaustiveness_witnesses. single_column ( ) ;
1505
1609
let arm_usefulness: Vec < _ > = arms
@@ -1516,5 +1620,10 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
1516
1620
( arm, usefulness)
1517
1621
} )
1518
1622
. collect ( ) ;
1519
- Ok ( UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses } )
1623
+
1624
+ Ok ( UsefulnessReport {
1625
+ arm_usefulness,
1626
+ non_exhaustiveness_witnesses,
1627
+ overlapping_range_endpoints,
1628
+ } )
1520
1629
}
0 commit comments