@@ -1327,6 +1327,83 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13271327 }
13281328}
13291329
1330+ /// Collect ranges that overlap like `lo..=overlap`/`overlap..=hi`. Must be called during
1331+ /// exhaustiveness checking, if we find a singleton range after constructor splitting. This reuses
1332+ /// row intersection information to only detect ranges that truly overlap.
1333+ ///
1334+ /// If two ranges overlapped, the split set will contain their intersection as a singleton.
1335+ /// Specialization will then select rows that match the overlap, and exhaustiveness will compute
1336+ /// which rows have an intersection that includes the overlap. That gives us all the info we need to
1337+ /// compute overlapping ranges without false positives.
1338+ ///
1339+ /// We can however get false negatives because exhaustiveness does not explore all cases. See the
1340+ /// section on relevancy at the top of the file.
1341+ fn collect_overlapping_range_endpoints < ' p , Cx : TypeCx > (
1342+ overlap_range : IntRange ,
1343+ matrix : & Matrix < ' p , Cx > ,
1344+ specialized_matrix : & Matrix < ' p , Cx > ,
1345+ overlapping_range_endpoints : & mut Vec < OverlappingRanges < ' p , Cx > > ,
1346+ ) {
1347+ let overlap = overlap_range. lo ;
1348+ // Ranges that look like `lo..=overlap`.
1349+ let mut prefixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1350+ // Ranges that look like `overlap..=hi`.
1351+ let mut suffixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1352+ // Iterate on patterns that contained `overlap`. We iterate on `specialized_matrix` which
1353+ // contains only rows that matched the current `ctor` as well as accurate intersection
1354+ // information. It doesn't contain the column that contains the range; that can be found in
1355+ // `matrix`.
1356+ for ( child_row_id, child_row) in specialized_matrix. rows ( ) . enumerate ( ) {
1357+ let pat = matrix. rows [ child_row. parent_row ] . head ( ) ;
1358+ let Constructor :: IntRange ( this_range) = pat. ctor ( ) else { continue } ;
1359+ // Don't lint when one of the ranges is a singleton.
1360+ if this_range. is_singleton ( ) {
1361+ continue ;
1362+ }
1363+ if this_range. lo == overlap {
1364+ // `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
1365+ // ranges that look like `lo..=overlap`.
1366+ if !prefixes. is_empty ( ) {
1367+ let overlaps_with: Vec < _ > = prefixes
1368+ . iter ( )
1369+ . filter ( |& & ( other_child_row_id, _) | {
1370+ child_row. intersects . contains ( other_child_row_id)
1371+ } )
1372+ . map ( |& ( _, pat) | pat)
1373+ . collect ( ) ;
1374+ if !overlaps_with. is_empty ( ) {
1375+ overlapping_range_endpoints. push ( OverlappingRanges {
1376+ pat,
1377+ overlaps_on : overlap_range,
1378+ overlaps_with,
1379+ } ) ;
1380+ }
1381+ }
1382+ suffixes. push ( ( child_row_id, pat) )
1383+ } else if this_range. hi == overlap. plus_one ( ) {
1384+ // `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
1385+ // ranges that look like `overlap..=hi`.
1386+ if !suffixes. is_empty ( ) {
1387+ let overlaps_with: Vec < _ > = suffixes
1388+ . iter ( )
1389+ . filter ( |& & ( other_child_row_id, _) | {
1390+ child_row. intersects . contains ( other_child_row_id)
1391+ } )
1392+ . map ( |& ( _, pat) | pat)
1393+ . collect ( ) ;
1394+ if !overlaps_with. is_empty ( ) {
1395+ overlapping_range_endpoints. push ( OverlappingRanges {
1396+ pat,
1397+ overlaps_on : overlap_range,
1398+ overlaps_with,
1399+ } ) ;
1400+ }
1401+ }
1402+ prefixes. push ( ( child_row_id, pat) )
1403+ }
1404+ }
1405+ }
1406+
13301407/// The core of the algorithm.
13311408///
13321409/// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks
@@ -1345,6 +1422,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13451422fn compute_exhaustiveness_and_usefulness < ' a , ' p , Cx : TypeCx > (
13461423 mcx : MatchCtxt < ' a , ' p , Cx > ,
13471424 matrix : & mut Matrix < ' p , Cx > ,
1425+ overlapping_range_endpoints : & mut Vec < OverlappingRanges < ' p , Cx > > ,
13481426 is_top_level : bool ,
13491427) -> WitnessMatrix < Cx > {
13501428 debug_assert ! ( matrix. rows( ) . all( |r| r. len( ) == matrix. column_count( ) ) ) ;
@@ -1424,7 +1502,12 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14241502 let ctor_is_relevant = matches ! ( ctor, Constructor :: Missing ) || missing_ctors. is_empty ( ) ;
14251503 let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor, ctor_is_relevant) ;
14261504 let mut witnesses = ensure_sufficient_stack ( || {
1427- compute_exhaustiveness_and_usefulness ( mcx, & mut spec_matrix, false )
1505+ compute_exhaustiveness_and_usefulness (
1506+ mcx,
1507+ & mut spec_matrix,
1508+ overlapping_range_endpoints,
1509+ false ,
1510+ )
14281511 } ) ;
14291512
14301513 // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
@@ -1446,6 +1529,21 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14461529 }
14471530 }
14481531 }
1532+
1533+ // Detect ranges that overlap on their endpoints.
1534+ if let Constructor :: IntRange ( overlap_range) = ctor {
1535+ if overlap_range. is_singleton ( )
1536+ && spec_matrix. rows . len ( ) >= 2
1537+ && spec_matrix. rows . iter ( ) . any ( |row| !row. intersects . is_empty ( ) )
1538+ {
1539+ collect_overlapping_range_endpoints (
1540+ overlap_range,
1541+ matrix,
1542+ & spec_matrix,
1543+ overlapping_range_endpoints,
1544+ ) ;
1545+ }
1546+ }
14491547 }
14501548
14511549 // Record usefulness in the patterns.
@@ -1486,6 +1584,7 @@ pub struct UsefulnessReport<'p, Cx: TypeCx> {
14861584 /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
14871585 /// exhaustiveness.
14881586 pub non_exhaustiveness_witnesses : Vec < WitnessPat < Cx > > ,
1587+ pub overlapping_range_endpoints : Vec < OverlappingRanges < ' p , Cx > > ,
14891588}
14901589
14911590/// Computes whether a match is exhaustive and which of its arms are useful.
@@ -1496,8 +1595,14 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
14961595 scrut_ty : Cx :: Ty ,
14971596 scrut_validity : ValidityConstraint ,
14981597) -> UsefulnessReport < ' p , Cx > {
1598+ let mut overlapping_range_endpoints = Vec :: new ( ) ;
14991599 let mut matrix = Matrix :: new ( cx. wildcard_arena , arms, scrut_ty, scrut_validity) ;
1500- let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness ( cx, & mut matrix, true ) ;
1600+ let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness (
1601+ cx,
1602+ & mut matrix,
1603+ & mut overlapping_range_endpoints,
1604+ true ,
1605+ ) ;
15011606
15021607 let non_exhaustiveness_witnesses: Vec < _ > = non_exhaustiveness_witnesses. single_column ( ) ;
15031608 let arm_usefulness: Vec < _ > = arms
@@ -1514,5 +1619,5 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
15141619 ( arm, usefulness)
15151620 } )
15161621 . collect ( ) ;
1517- UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
1622+ UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses, overlapping_range_endpoints }
15181623}
0 commit comments