712
712
//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
713
713
//! reason not to, for example if they crucially depend on a particular feature like `or_patterns`.
714
714
715
+ use rustc_index:: bit_set:: BitSet ;
715
716
use smallvec:: { smallvec, SmallVec } ;
716
717
use std:: fmt;
717
718
718
- use crate :: constructor:: { Constructor , ConstructorSet } ;
719
+ use crate :: constructor:: { Constructor , ConstructorSet , IntRange } ;
719
720
use crate :: pat:: { DeconstructedPat , WitnessPat } ;
720
721
use crate :: { Captures , MatchArm , MatchCtxt , TypeCx , TypedArena } ;
721
722
@@ -911,6 +912,10 @@ struct MatrixRow<'p, Cx: TypeCx> {
911
912
/// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful.
912
913
/// This is reset to `false` when specializing.
913
914
useful : bool ,
915
+ /// Tracks which rows above this one have an intersection with this one, i.e. such that there is
916
+ /// a value that matches both rows.
917
+ /// FIXME: Because of relevancy we may miss some intersections.
918
+ intersects : BitSet < usize > ,
914
919
}
915
920
916
921
impl < ' p , Cx : TypeCx > MatrixRow < ' p , Cx > {
@@ -938,6 +943,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
938
943
parent_row : self . parent_row ,
939
944
is_under_guard : self . is_under_guard ,
940
945
useful : false ,
946
+ intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::expand_and_push`.
941
947
} )
942
948
}
943
949
@@ -955,6 +961,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
955
961
parent_row,
956
962
is_under_guard : self . is_under_guard ,
957
963
useful : false ,
964
+ intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::expand_and_push`.
958
965
}
959
966
}
960
967
}
@@ -991,13 +998,15 @@ struct Matrix<'p, Cx: TypeCx> {
991
998
impl < ' p , Cx : TypeCx > Matrix < ' p , Cx > {
992
999
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
993
1000
/// expands it. Internal method, prefer [`Matrix::new`].
994
- fn expand_and_push ( & mut self , row : MatrixRow < ' p , Cx > ) {
1001
+ fn expand_and_push ( & mut self , mut row : MatrixRow < ' p , Cx > ) {
995
1002
if !row. is_empty ( ) && row. head ( ) . is_or_pat ( ) {
996
1003
// Expand nested or-patterns.
997
- for new_row in row. expand_or_pat ( ) {
1004
+ for mut new_row in row. expand_or_pat ( ) {
1005
+ new_row. intersects = BitSet :: new_empty ( self . rows . len ( ) ) ;
998
1006
self . rows . push ( new_row) ;
999
1007
}
1000
1008
} else {
1009
+ row. intersects = BitSet :: new_empty ( self . rows . len ( ) ) ;
1001
1010
self . rows . push ( row) ;
1002
1011
}
1003
1012
}
@@ -1022,6 +1031,7 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
1022
1031
parent_row : row_id, // dummy, we won't read it
1023
1032
is_under_guard : arm. has_guard ,
1024
1033
useful : false ,
1034
+ intersects : BitSet :: new_empty ( row_id) ,
1025
1035
} ;
1026
1036
matrix. expand_and_push ( v) ;
1027
1037
}
@@ -1334,6 +1344,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
1334
1344
fn compute_exhaustiveness_and_usefulness < ' a , ' p , Cx : TypeCx > (
1335
1345
mcx : MatchCtxt < ' a , ' p , Cx > ,
1336
1346
matrix : & mut Matrix < ' p , Cx > ,
1347
+ overlapping_range_endpoints : & mut Vec < OverlappingRanges < ' p , Cx > > ,
1337
1348
is_top_level : bool ,
1338
1349
) -> WitnessMatrix < Cx > {
1339
1350
debug_assert ! ( matrix. rows( ) . all( |r| r. len( ) == matrix. column_count( ) ) ) ;
@@ -1348,21 +1359,19 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1348
1359
let Some ( ty) = matrix. head_ty ( mcx) else {
1349
1360
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
1350
1361
// A row is useful iff it has no (unguarded) rows above it.
1351
- for row in matrix. rows_mut ( ) {
1352
- // All rows are useful until they're not.
1353
- row. useful = true ;
1354
- // When there's an unguarded row, the match is exhaustive and any subsequent row is not
1355
- // useful.
1356
- if !row. is_under_guard {
1357
- return WitnessMatrix :: empty ( ) ;
1358
- }
1362
+ let mut useful = true ; // Whether the next row is useful.
1363
+ for ( i, row) in matrix. rows_mut ( ) . enumerate ( ) {
1364
+ row. useful = useful;
1365
+ row. intersects . insert_range ( 0 ..i) ;
1366
+ // The next rows stays useful if this one is under a guard.
1367
+ useful &= row. is_under_guard ;
1359
1368
}
1360
- // No (unguarded) rows, so the match is not exhaustive. We return a new witness unless
1361
- // irrelevant.
1362
- return if matrix. wildcard_row . relevant {
1369
+ return if useful && matrix. wildcard_row . relevant {
1370
+ // The wildcard row is useful; the match is non-exhaustive.
1363
1371
WitnessMatrix :: unit_witness ( )
1364
1372
} else {
1365
- // We choose to not report anything here; see at the top for details.
1373
+ // Either the match is exhaustive, or we choose not to report anything because of
1374
+ // relevancy. See at the top for details.
1366
1375
WitnessMatrix :: empty ( )
1367
1376
} ;
1368
1377
} ;
@@ -1415,18 +1424,93 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
1415
1424
let ctor_is_relevant = matches ! ( ctor, Constructor :: Missing ) || missing_ctors. is_empty ( ) ;
1416
1425
let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor, ctor_is_relevant) ;
1417
1426
let mut witnesses = ensure_sufficient_stack ( || {
1418
- compute_exhaustiveness_and_usefulness ( mcx, & mut spec_matrix, false )
1427
+ compute_exhaustiveness_and_usefulness (
1428
+ mcx,
1429
+ & mut spec_matrix,
1430
+ overlapping_range_endpoints,
1431
+ false ,
1432
+ )
1419
1433
} ) ;
1420
1434
1421
1435
// Transform witnesses for `spec_matrix` into witnesses for `matrix`.
1422
1436
witnesses. apply_constructor ( pcx, & missing_ctors, & ctor, report_individual_missing_ctors) ;
1423
1437
// Accumulate the found witnesses.
1424
1438
ret. extend ( witnesses) ;
1425
1439
1426
- // A parent row is useful if any of its children is.
1427
- for child_row in spec_matrix. rows ( ) {
1428
- let parent_row = & mut matrix. rows [ child_row. parent_row ] ;
1429
- parent_row. useful = parent_row. useful || child_row. useful ;
1440
+ for ( child_row_id, child_row) in spec_matrix. rows ( ) . enumerate ( ) {
1441
+ let parent_row_id = child_row. parent_row ;
1442
+ let parent_row = & mut matrix. rows [ parent_row_id] ;
1443
+ // A parent row is useful if any of its children is.
1444
+ parent_row. useful |= child_row. useful ;
1445
+ for child_intersects in child_row. intersects . iter ( ) {
1446
+ // Convert the intersecting ids into ids for the parent matrix.
1447
+ let parent_intersects = spec_matrix. rows [ child_intersects] . parent_row ;
1448
+ debug ! ( "child row {child_row_id} intersects with child row {child_intersects}" ) ;
1449
+ debug ! ( "parent row {parent_row_id} intersects with parent row {parent_intersects}" ) ;
1450
+ if parent_intersects != parent_row_id {
1451
+ // self-intersect can happen with or-patterns
1452
+ parent_row. intersects . insert ( parent_intersects) ;
1453
+ }
1454
+ }
1455
+ }
1456
+
1457
+ // Detect overlapping ranges.
1458
+ if let Constructor :: IntRange ( overlap_range) = ctor {
1459
+ // If two ranges overlap on their endpoing, that endpoint will show up as a singleton in
1460
+ // the splitted set.
1461
+ if overlap_range. is_singleton ( ) {
1462
+ let overlap = overlap_range. lo ;
1463
+ // Ranges that look like `lo..=overlap`.
1464
+ let mut prefixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1465
+ // Ranges that look like `overlap..=hi`.
1466
+ let mut suffixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1467
+ // Iterate on patterns that contained `overlap`.
1468
+ for ( row_id, row) in matrix. rows ( ) . enumerate ( ) {
1469
+ let pat = row. head ( ) ;
1470
+ let Constructor :: IntRange ( this_range) = pat. ctor ( ) else { continue } ;
1471
+ // Don't lint when one of the ranges is a singleton.
1472
+ if this_range. is_singleton ( ) {
1473
+ continue ;
1474
+ }
1475
+ if this_range. lo == overlap {
1476
+ // `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
1477
+ // ranges that look like `lo..=overlap`.
1478
+ if !prefixes. is_empty ( ) {
1479
+ let overlaps_with: Vec < _ > = prefixes
1480
+ . iter ( )
1481
+ . filter ( |& & other_row_id| row. intersects . contains ( other_row_id) )
1482
+ . map ( |& other_row_id| matrix. rows [ other_row_id] . head ( ) )
1483
+ . collect ( ) ;
1484
+ if !overlaps_with. is_empty ( ) {
1485
+ overlapping_range_endpoints. push ( OverlappingRanges {
1486
+ pat,
1487
+ overlaps_on : overlap_range,
1488
+ overlaps_with,
1489
+ } ) ;
1490
+ }
1491
+ }
1492
+ suffixes. push ( row_id)
1493
+ } else if this_range. hi == overlap. plus_one ( ) {
1494
+ // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
1495
+ // ranges that look like `overlap..=hi`.
1496
+ if !suffixes. is_empty ( ) {
1497
+ let overlaps_with: Vec < _ > = suffixes
1498
+ . iter ( )
1499
+ . filter ( |& & other_row_id| row. intersects . contains ( other_row_id) )
1500
+ . map ( |& other_row_id| matrix. rows [ other_row_id] . head ( ) )
1501
+ . collect ( ) ;
1502
+ if !overlaps_with. is_empty ( ) {
1503
+ overlapping_range_endpoints. push ( OverlappingRanges {
1504
+ pat,
1505
+ overlaps_on : overlap_range,
1506
+ overlaps_with,
1507
+ } ) ;
1508
+ }
1509
+ }
1510
+ prefixes. push ( row_id)
1511
+ }
1512
+ }
1513
+ }
1430
1514
}
1431
1515
}
1432
1516
@@ -1452,13 +1536,22 @@ pub enum Usefulness<'p, Cx: TypeCx> {
1452
1536
Redundant ,
1453
1537
}
1454
1538
1539
+ /// Indicates whether or not a given arm is useful.
1540
+ #[ derive( Clone , Debug ) ]
1541
+ pub struct OverlappingRanges < ' p , Cx : TypeCx > {
1542
+ pub pat : & ' p DeconstructedPat < ' p , Cx > ,
1543
+ pub overlaps_on : IntRange ,
1544
+ pub overlaps_with : Vec < & ' p DeconstructedPat < ' p , Cx > > ,
1545
+ }
1546
+
1455
1547
/// The output of checking a match for exhaustiveness and arm usefulness.
1456
1548
pub struct UsefulnessReport < ' p , Cx : TypeCx > {
1457
1549
/// For each arm of the input, whether that arm is useful after the arms above it.
1458
1550
pub arm_usefulness : Vec < ( MatchArm < ' p , Cx > , Usefulness < ' p , Cx > ) > ,
1459
1551
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
1460
1552
/// exhaustiveness.
1461
1553
pub non_exhaustiveness_witnesses : Vec < WitnessPat < Cx > > ,
1554
+ pub overlapping_range_endpoints : Vec < OverlappingRanges < ' p , Cx > > ,
1462
1555
}
1463
1556
1464
1557
/// Computes whether a match is exhaustive and which of its arms are useful.
@@ -1469,8 +1562,14 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
1469
1562
scrut_ty : Cx :: Ty ,
1470
1563
scrut_validity : ValidityConstraint ,
1471
1564
) -> UsefulnessReport < ' p , Cx > {
1565
+ let mut overlapping_range_endpoints = Vec :: new ( ) ;
1472
1566
let mut matrix = Matrix :: new ( cx. wildcard_arena , arms, scrut_ty, scrut_validity) ;
1473
- let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness ( cx, & mut matrix, true ) ;
1567
+ let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness (
1568
+ cx,
1569
+ & mut matrix,
1570
+ & mut overlapping_range_endpoints,
1571
+ true ,
1572
+ ) ;
1474
1573
1475
1574
let non_exhaustiveness_witnesses: Vec < _ > = non_exhaustiveness_witnesses. single_column ( ) ;
1476
1575
let arm_usefulness: Vec < _ > = arms
@@ -1487,5 +1586,5 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
1487
1586
( arm, usefulness)
1488
1587
} )
1489
1588
. collect ( ) ;
1490
- UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
1589
+ UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses, overlapping_range_endpoints }
1491
1590
}
0 commit comments