@@ -1328,6 +1328,83 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13281328 }
13291329}
13301330
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+
13311408/// The core of the algorithm.
13321409///
13331410/// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks
@@ -1346,6 +1423,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13461423fn compute_exhaustiveness_and_usefulness < ' a , ' p , Cx : TypeCx > (
13471424 mcx : MatchCtxt < ' a , ' p , Cx > ,
13481425 matrix : & mut Matrix < ' p , Cx > ,
1426+ overlapping_range_endpoints : & mut Vec < OverlappingRanges < ' p , Cx > > ,
13491427 is_top_level : bool ,
13501428) -> Result < WitnessMatrix < Cx > , Cx :: Error > {
13511429 debug_assert ! ( matrix. rows( ) . all( |r| r. len( ) == matrix. column_count( ) ) ) ;
@@ -1425,7 +1503,12 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14251503 let ctor_is_relevant = matches ! ( ctor, Constructor :: Missing ) || missing_ctors. is_empty ( ) ;
14261504 let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor, ctor_is_relevant) ;
14271505 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+ )
14291512 } ) ?;
14301513
14311514 // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
@@ -1447,6 +1530,21 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14471530 }
14481531 }
14491532 }
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+ }
14501548 }
14511549
14521550 // Record usefulness in the patterns.
@@ -1487,6 +1585,7 @@ pub struct UsefulnessReport<'p, Cx: TypeCx> {
14871585 /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
14881586 /// exhaustiveness.
14891587 pub non_exhaustiveness_witnesses : Vec < WitnessPat < Cx > > ,
1588+ pub overlapping_range_endpoints : Vec < OverlappingRanges < ' p , Cx > > ,
14901589}
14911590
14921591/// 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>(
14971596 scrut_ty : Cx :: Ty ,
14981597 scrut_validity : ValidityConstraint ,
14991598) -> Result < UsefulnessReport < ' p , Cx > , Cx :: Error > {
1599+ let mut overlapping_range_endpoints = Vec :: new ( ) ;
15001600 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+ ) ?;
15031607
15041608 let non_exhaustiveness_witnesses: Vec < _ > = non_exhaustiveness_witnesses. single_column ( ) ;
15051609 let arm_usefulness: Vec < _ > = arms
@@ -1516,5 +1620,10 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
15161620 ( arm, usefulness)
15171621 } )
15181622 . 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+ } )
15201629}
0 commit comments