@@ -1333,6 +1333,83 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13331333 }
13341334}
13351335
1336+ /// Collect ranges that overlap like `lo..=overlap`/`overlap..=hi`. Must be called during
1337+ /// exhaustiveness checking, if we find a singleton range after constructor splitting. This reuses
1338+ /// row intersection information to only detect ranges that truly overlap.
1339+ ///
1340+ /// If two ranges overlapped, the split set will contain their intersection as a singleton.
1341+ /// Specialization will then select rows that match the overlap, and exhaustiveness will compute
1342+ /// which rows have an intersection that includes the overlap. That gives us all the info we need to
1343+ /// compute overlapping ranges without false positives.
1344+ ///
1345+ /// We can however get false negatives because exhaustiveness does not explore all cases. See the
1346+ /// section on relevancy at the top of the file.
1347+ fn collect_overlapping_range_endpoints < ' p , Cx : TypeCx > (
1348+ overlap_range : IntRange ,
1349+ matrix : & Matrix < ' p , Cx > ,
1350+ specialized_matrix : & Matrix < ' p , Cx > ,
1351+ overlapping_range_endpoints : & mut Vec < OverlappingRanges < ' p , Cx > > ,
1352+ ) {
1353+ let overlap = overlap_range. lo ;
1354+ // Ranges that look like `lo..=overlap`.
1355+ let mut prefixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1356+ // Ranges that look like `overlap..=hi`.
1357+ let mut suffixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1358+ // Iterate on patterns that contained `overlap`. We iterate on `specialized_matrix` which
1359+ // contains only rows that matched the current `ctor` as well as accurate intersection
1360+ // information. It doesn't contain the column that contains the range; that can be found in
1361+ // `matrix`.
1362+ for ( child_row_id, child_row) in specialized_matrix. rows ( ) . enumerate ( ) {
1363+ let pat = matrix. rows [ child_row. parent_row ] . head ( ) ;
1364+ let Constructor :: IntRange ( this_range) = pat. ctor ( ) else { continue } ;
1365+ // Don't lint when one of the ranges is a singleton.
1366+ if this_range. is_singleton ( ) {
1367+ continue ;
1368+ }
1369+ if this_range. lo == overlap {
1370+ // `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
1371+ // ranges that look like `lo..=overlap`.
1372+ if !prefixes. is_empty ( ) {
1373+ let overlaps_with: Vec < _ > = prefixes
1374+ . iter ( )
1375+ . filter ( |& & ( other_child_row_id, _) | {
1376+ child_row. intersects . contains ( other_child_row_id)
1377+ } )
1378+ . map ( |& ( _, pat) | pat)
1379+ . collect ( ) ;
1380+ if !overlaps_with. is_empty ( ) {
1381+ overlapping_range_endpoints. push ( OverlappingRanges {
1382+ pat,
1383+ overlaps_on : overlap_range,
1384+ overlaps_with,
1385+ } ) ;
1386+ }
1387+ }
1388+ suffixes. push ( ( child_row_id, pat) )
1389+ } else if this_range. hi == overlap. plus_one ( ) {
1390+ // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
1391+ // ranges that look like `overlap..=hi`.
1392+ if !suffixes. is_empty ( ) {
1393+ let overlaps_with: Vec < _ > = suffixes
1394+ . iter ( )
1395+ . filter ( |& & ( other_child_row_id, _) | {
1396+ child_row. intersects . contains ( other_child_row_id)
1397+ } )
1398+ . map ( |& ( _, pat) | pat)
1399+ . collect ( ) ;
1400+ if !overlaps_with. is_empty ( ) {
1401+ overlapping_range_endpoints. push ( OverlappingRanges {
1402+ pat,
1403+ overlaps_on : overlap_range,
1404+ overlaps_with,
1405+ } ) ;
1406+ }
1407+ }
1408+ prefixes. push ( ( child_row_id, pat) )
1409+ }
1410+ }
1411+ }
1412+
13361413/// The core of the algorithm.
13371414///
13381415/// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks
@@ -1351,6 +1428,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13511428fn compute_exhaustiveness_and_usefulness < ' a , ' p , Cx : TypeCx > (
13521429 mcx : MatchCtxt < ' a , ' p , Cx > ,
13531430 matrix : & mut Matrix < ' p , Cx > ,
1431+ overlapping_range_endpoints : & mut Vec < OverlappingRanges < ' p , Cx > > ,
13541432 is_top_level : bool ,
13551433) -> WitnessMatrix < Cx > {
13561434 debug_assert ! ( matrix. rows( ) . all( |r| r. len( ) == matrix. column_count( ) ) ) ;
@@ -1430,7 +1508,12 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14301508 let ctor_is_relevant = matches ! ( ctor, Constructor :: Missing ) || missing_ctors. is_empty ( ) ;
14311509 let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor, ctor_is_relevant) ;
14321510 let mut witnesses = ensure_sufficient_stack ( || {
1433- compute_exhaustiveness_and_usefulness ( mcx, & mut spec_matrix, false )
1511+ compute_exhaustiveness_and_usefulness (
1512+ mcx,
1513+ & mut spec_matrix,
1514+ overlapping_range_endpoints,
1515+ false ,
1516+ )
14341517 } ) ;
14351518
14361519 // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
@@ -1452,6 +1535,21 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14521535 }
14531536 }
14541537 }
1538+
1539+ // Detect ranges that overlap on their endpoints.
1540+ if let Constructor :: IntRange ( overlap_range) = ctor {
1541+ if overlap_range. is_singleton ( )
1542+ && spec_matrix. rows . len ( ) >= 2
1543+ && spec_matrix. rows . iter ( ) . any ( |row| !row. intersects . is_empty ( ) )
1544+ {
1545+ collect_overlapping_range_endpoints (
1546+ overlap_range,
1547+ matrix,
1548+ & spec_matrix,
1549+ overlapping_range_endpoints,
1550+ ) ;
1551+ }
1552+ }
14551553 }
14561554
14571555 // Record usefulness in the patterns.
@@ -1492,6 +1590,7 @@ pub struct UsefulnessReport<'p, Cx: TypeCx> {
14921590 /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
14931591 /// exhaustiveness.
14941592 pub non_exhaustiveness_witnesses : Vec < WitnessPat < Cx > > ,
1593+ pub overlapping_range_endpoints : Vec < OverlappingRanges < ' p , Cx > > ,
14951594}
14961595
14971596/// Computes whether a match is exhaustive and which of its arms are useful.
@@ -1502,8 +1601,14 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
15021601 scrut_ty : Cx :: Ty ,
15031602 scrut_validity : ValidityConstraint ,
15041603) -> UsefulnessReport < ' p , Cx > {
1604+ let mut overlapping_range_endpoints = Vec :: new ( ) ;
15051605 let mut matrix = Matrix :: new ( arms, scrut_ty, scrut_validity) ;
1506- let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness ( cx, & mut matrix, true ) ;
1606+ let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness (
1607+ cx,
1608+ & mut matrix,
1609+ & mut overlapping_range_endpoints,
1610+ true ,
1611+ ) ;
15071612
15081613 let non_exhaustiveness_witnesses: Vec < _ > = non_exhaustiveness_witnesses. single_column ( ) ;
15091614 let arm_usefulness: Vec < _ > = arms
@@ -1520,5 +1625,5 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
15201625 ( arm, usefulness)
15211626 } )
15221627 . collect ( ) ;
1523- UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
1628+ UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses, overlapping_range_endpoints }
15241629}
0 commit comments