712712//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
713713//! reason not to, for example if they crucially depend on a particular feature like `or_patterns`.
714714
715+ use rustc_index:: bit_set:: BitSet ;
715716use smallvec:: { smallvec, SmallVec } ;
716717use std:: fmt;
717718
718- use crate :: constructor:: { Constructor , ConstructorSet } ;
719+ use crate :: constructor:: { Constructor , ConstructorSet , IntRange } ;
719720use crate :: pat:: { DeconstructedPat , WitnessPat } ;
720721use crate :: { Captures , MatchArm , MatchCtxt , TypeCx , TypedArena } ;
721722
@@ -911,6 +912,10 @@ struct MatrixRow<'p, Cx: TypeCx> {
911912 /// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful.
912913 /// This is reset to `false` when specializing.
913914 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 > ,
914919}
915920
916921impl < ' p , Cx : TypeCx > MatrixRow < ' p , Cx > {
@@ -938,6 +943,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
938943 parent_row : self . parent_row ,
939944 is_under_guard : self . is_under_guard ,
940945 useful : false ,
946+ intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::expand_and_push`.
941947 } )
942948 }
943949
@@ -955,6 +961,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
955961 parent_row,
956962 is_under_guard : self . is_under_guard ,
957963 useful : false ,
964+ intersects : BitSet :: new_empty ( 0 ) , // Initialized in `Matrix::expand_and_push`.
958965 }
959966 }
960967}
@@ -991,13 +998,15 @@ struct Matrix<'p, Cx: TypeCx> {
991998impl < ' p , Cx : TypeCx > Matrix < ' p , Cx > {
992999 /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
9931000 /// 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 > ) {
9951002 if !row. is_empty ( ) && row. head ( ) . is_or_pat ( ) {
9961003 // 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 ( ) ) ;
9981006 self . rows . push ( new_row) ;
9991007 }
10001008 } else {
1009+ row. intersects = BitSet :: new_empty ( self . rows . len ( ) ) ;
10011010 self . rows . push ( row) ;
10021011 }
10031012 }
@@ -1022,6 +1031,7 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
10221031 parent_row : row_id, // dummy, we won't read it
10231032 is_under_guard : arm. has_guard ,
10241033 useful : false ,
1034+ intersects : BitSet :: new_empty ( row_id) ,
10251035 } ;
10261036 matrix. expand_and_push ( v) ;
10271037 }
@@ -1334,6 +1344,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13341344fn compute_exhaustiveness_and_usefulness < ' a , ' p , Cx : TypeCx > (
13351345 mcx : MatchCtxt < ' a , ' p , Cx > ,
13361346 matrix : & mut Matrix < ' p , Cx > ,
1347+ overlapping_range_endpoints : & mut Vec < OverlappingRanges < ' p , Cx > > ,
13371348 is_top_level : bool ,
13381349) -> WitnessMatrix < Cx > {
13391350 debug_assert ! ( matrix. rows( ) . all( |r| r. len( ) == matrix. column_count( ) ) ) ;
@@ -1348,21 +1359,19 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
13481359 let Some ( ty) = matrix. head_ty ( mcx) else {
13491360 // The base case: there are no columns in the matrix. We are morally pattern-matching on ().
13501361 // 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 ;
13591368 }
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.
13631371 WitnessMatrix :: unit_witness ( )
13641372 } 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.
13661375 WitnessMatrix :: empty ( )
13671376 } ;
13681377 } ;
@@ -1415,18 +1424,93 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14151424 let ctor_is_relevant = matches ! ( ctor, Constructor :: Missing ) || missing_ctors. is_empty ( ) ;
14161425 let mut spec_matrix = matrix. specialize_constructor ( pcx, & ctor, ctor_is_relevant) ;
14171426 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+ )
14191433 } ) ;
14201434
14211435 // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
14221436 witnesses. apply_constructor ( pcx, & missing_ctors, & ctor, report_individual_missing_ctors) ;
14231437 // Accumulate the found witnesses.
14241438 ret. extend ( witnesses) ;
14251439
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+ }
14301514 }
14311515 }
14321516
@@ -1452,13 +1536,22 @@ pub enum Usefulness<'p, Cx: TypeCx> {
14521536 Redundant ,
14531537}
14541538
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+
14551547/// The output of checking a match for exhaustiveness and arm usefulness.
14561548pub struct UsefulnessReport < ' p , Cx : TypeCx > {
14571549 /// For each arm of the input, whether that arm is useful after the arms above it.
14581550 pub arm_usefulness : Vec < ( MatchArm < ' p , Cx > , Usefulness < ' p , Cx > ) > ,
14591551 /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
14601552 /// exhaustiveness.
14611553 pub non_exhaustiveness_witnesses : Vec < WitnessPat < Cx > > ,
1554+ pub overlapping_range_endpoints : Vec < OverlappingRanges < ' p , Cx > > ,
14621555}
14631556
14641557/// 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>(
14691562 scrut_ty : Cx :: Ty ,
14701563 scrut_validity : ValidityConstraint ,
14711564) -> UsefulnessReport < ' p , Cx > {
1565+ let mut overlapping_range_endpoints = Vec :: new ( ) ;
14721566 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+ ) ;
14741573
14751574 let non_exhaustiveness_witnesses: Vec < _ > = non_exhaustiveness_witnesses. single_column ( ) ;
14761575 let arm_usefulness: Vec < _ > = arms
@@ -1487,5 +1586,5 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
14871586 ( arm, usefulness)
14881587 } )
14891588 . collect ( ) ;
1490- UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
1589+ UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses, overlapping_range_endpoints }
14911590}
0 commit comments