@@ -194,6 +194,7 @@ use std::cmp::{self, Ordering, min, max};
194
194
use std:: fmt;
195
195
use std:: iter:: { FromIterator , IntoIterator } ;
196
196
use std:: ops:: RangeInclusive ;
197
+ use std:: u128;
197
198
198
199
pub fn expand_pattern < ' a , ' tcx > ( cx : & MatchCheckCtxt < ' a , ' tcx > , pat : Pattern < ' tcx > )
199
200
-> & ' a Pattern < ' tcx >
@@ -799,6 +800,7 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
799
800
///
800
801
/// `IntRange` is never used to encode an empty range or a "range" that wraps
801
802
/// around the (offset) space: i.e. `range.lo <= range.hi`.
803
+ #[ derive( Clone ) ]
802
804
struct IntRange < ' tcx > {
803
805
pub range : RangeInclusive < u128 > ,
804
806
pub ty : Ty < ' tcx > ,
@@ -1400,9 +1402,7 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'_, 'tcx, 'tcx>, ctor: &Construct
1400
1402
/// patterns that apply to that range (specifically: the patterns that *intersect* with that range)
1401
1403
/// change.
1402
1404
/// Our solution, therefore, is to split the range constructor into subranges at every single point
1403
- /// the group of intersecting patterns changes, which we can compute by converting each pattern to
1404
- /// a range and recording its endpoints, then creating subranges between each consecutive pair of
1405
- /// endpoints.
1405
+ /// the group of intersecting patterns changes (using the method described below).
1406
1406
/// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching
1407
1407
/// on actual integers. The nice thing about this is that the number of subranges is linear in the
1408
1408
/// number of rows in the matrix (i.e. the number of cases in the `match` statement), so we don't
@@ -1414,14 +1414,14 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'_, 'tcx, 'tcx>, ctor: &Construct
1414
1414
/// |-------| |-------| |----| ||
1415
1415
/// |---------|
1416
1416
///
1417
- /// We truncate the ranges so that they lie inside each range constructor and then split them
1418
- /// up into equivalence classes so the ranges are no longer overlapping:
1417
+ /// We split the ranges up into equivalence classes so the ranges are no longer overlapping:
1419
1418
///
1420
1419
/// |--|--|||-||||--||---|||-------| |-|||| ||
1421
1420
///
1422
- /// The logic for determining how to split the ranges is a little involved: we need to make sure
1423
- /// that we have a new range for each subrange for which a different set of rows coïncides, but
1424
- /// essentially reduces to case analysis on the endpoints of the ranges.
1421
+ /// The logic for determining how to split the ranges is fairly straightforward: we calculate
1422
+ /// boundaries for each interval range, sort them, then create constructors for each new interval
1423
+ /// between every pair of boundary points. (This essentially sums up to performing the intuitive
1424
+ /// merging operation depicted above.)
1425
1425
fn split_grouped_constructors < ' p , ' a : ' p , ' tcx : ' a > (
1426
1426
tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
1427
1427
ctors : Vec < Constructor < ' tcx > > ,
@@ -1440,84 +1440,54 @@ fn split_grouped_constructors<'p, 'a: 'p, 'tcx: 'a>(
1440
1440
// `NotUseful`, which is the default case anyway, and can be ignored.
1441
1441
let ctor_range = IntRange :: from_ctor ( tcx, & ctor) . unwrap ( ) ;
1442
1442
1443
- // We're going to collect all the endpoints in the new pattern so we can create
1444
- // subranges between them.
1445
- // If there's a single point, we need to identify it as belonging
1446
- // to a length-1 range, so it can be treated as an individual
1447
- // constructor, rather than as an endpoint. To do this, we keep track of which
1448
- // endpoint a point corresponds to. Whenever a point corresponds to both a start
1449
- // and an end, then we create a unit range for it.
1450
- #[ derive( PartialEq , Clone , Copy , Debug ) ]
1451
- enum Endpoint {
1452
- Start ,
1453
- End ,
1454
- Both ,
1455
- } ;
1456
- let mut points = FxHashMap :: default ( ) ;
1457
- let add_endpoint = |points : & mut FxHashMap < _ , _ > , x, endpoint| {
1458
- points. entry ( x) . and_modify ( |ex_x| {
1459
- if * ex_x != endpoint {
1460
- * ex_x = Endpoint :: Both
1461
- }
1462
- } ) . or_insert ( endpoint) ;
1463
- } ;
1464
- let add_endpoints = |points : & mut FxHashMap < _ , _ > , lo, hi| {
1465
- // Insert the endpoints, taking care to keep track of to
1466
- // which endpoints a point corresponds.
1467
- add_endpoint ( points, lo, Endpoint :: Start ) ;
1468
- add_endpoint ( points, hi, Endpoint :: End ) ;
1469
- } ;
1470
- let ( lo, hi) = ( * ctor_range. range . start ( ) , * ctor_range. range . end ( ) ) ;
1471
- add_endpoints ( & mut points, lo, hi) ;
1472
- // We're going to iterate through every row pattern, adding endpoints in.
1473
- for row in m. iter ( ) {
1474
- if let Some ( r) = IntRange :: from_pat ( tcx, row[ 0 ] ) {
1475
- // We're only interested in endpoints that lie (at least partially)
1476
- // within the subrange domain.
1477
- if let Some ( r) = ctor_range. intersection ( & r) {
1478
- let ( r_lo, r_hi) = r. range . into_inner ( ) ;
1479
- add_endpoints ( & mut points, r_lo, r_hi) ;
1480
- }
1481
- }
1443
+ /// Represents a border between 2 integers. Because the intervals spanning borders
1444
+ /// must be able to cover every integer, we need 2^128 + 1 such borders.
1445
+ #[ derive( Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
1446
+ enum Border {
1447
+ JustBefore ( u128 ) ,
1448
+ AfterMax ,
1482
1449
}
1483
1450
1484
- // The patterns were iterated in an arbitrary order (i.e. in the order the user
1485
- // wrote them), so we need to make sure our endpoints are sorted.
1486
- let mut points: Vec < ( u128 , Endpoint ) > = points. into_iter ( ) . collect ( ) ;
1487
- points. sort_unstable_by_key ( |( x, _) | * x) ;
1488
- let mut points = points. into_iter ( ) ;
1489
- let mut a = points. next ( ) . unwrap ( ) ;
1490
-
1491
- // Iterate through pairs of points, adding the subranges to `split_ctors`.
1492
- // We have to be careful about the orientation of the points as endpoints, to make
1493
- // sure we're enumerating precisely the correct ranges. Too few and the matching is
1494
- // actually incorrect. Too many and our diagnostics are poorer. This involves some
1495
- // case analysis.
1496
- // In essence, we need to ensure that every time the set of row-ranges that are
1497
- // overlapping changes (as we go through the values covered by the ranges), we split
1498
- // into a new subrange.
1499
- while let Some ( b) = points. next ( ) {
1500
- // a < b (strictly)
1501
- if let Endpoint :: Both = a. 1 {
1502
- split_ctors. push ( IntRange :: range_to_ctor ( tcx, ty, a. 0 ..=a. 0 ) ) ;
1503
- }
1504
- // Integer overflow cannot occur here, because only the first point may be
1505
- // u128::MIN and only the last may be u128::MAX.
1506
- let c = match a. 1 {
1507
- Endpoint :: Start => a. 0 ,
1508
- Endpoint :: End | Endpoint :: Both => a. 0 + 1 ,
1509
- } ;
1510
- let d = match b. 1 {
1511
- Endpoint :: Start | Endpoint :: Both => b. 0 - 1 ,
1512
- Endpoint :: End => b. 0 ,
1451
+ // A function for extracting the borders of an integer interval.
1452
+ fn range_borders ( r : IntRange < ' _ > ) -> impl Iterator < Item = Border > {
1453
+ let ( lo, hi) = r. range . into_inner ( ) ;
1454
+ let from = Border :: JustBefore ( lo) ;
1455
+ let to = match hi. checked_add ( 1 ) {
1456
+ Some ( m) => Border :: JustBefore ( m) ,
1457
+ None => Border :: AfterMax ,
1513
1458
} ;
1514
- // In some cases, we won't need an intermediate range between two ranges
1515
- // lie immediately adjacent to one another.
1516
- if c <= d {
1517
- split_ctors. push ( IntRange :: range_to_ctor ( tcx, ty, c..=d) ) ;
1518
- }
1459
+ vec ! [ from, to] . into_iter ( )
1460
+ }
1519
1461
1520
- a = b;
1462
+ // `borders` is the set of borders between equivalence classes: each equivalence
1463
+ // class lies between 2 borders.
1464
+ let row_borders = m. iter ( )
1465
+ . flat_map ( |row| IntRange :: from_pat ( tcx, row[ 0 ] ) )
1466
+ . flat_map ( |range| ctor_range. intersection ( & range) )
1467
+ . flat_map ( |range| range_borders ( range) ) ;
1468
+ let ctor_borders = range_borders ( ctor_range. clone ( ) ) ;
1469
+ let mut borders: Vec < _ > = row_borders. chain ( ctor_borders) . collect ( ) ;
1470
+ borders. sort_unstable ( ) ;
1471
+
1472
+ // We're going to iterate through every pair of borders, making sure that each
1473
+ // represents an interval of nonnegative length, and convert each such interval
1474
+ // into a constructor.
1475
+ for IntRange { range, .. } in borders. windows ( 2 ) . filter_map ( |window| {
1476
+ match ( window[ 0 ] , window[ 1 ] ) {
1477
+ ( Border :: JustBefore ( n) , Border :: JustBefore ( m) ) => {
1478
+ if n < m {
1479
+ Some ( IntRange { range : n..=( m - 1 ) , ty } )
1480
+ } else {
1481
+ None
1482
+ }
1483
+ }
1484
+ ( Border :: JustBefore ( n) , Border :: AfterMax ) => {
1485
+ Some ( IntRange { range : n..=u128:: MAX , ty } )
1486
+ }
1487
+ ( Border :: AfterMax , _) => None ,
1488
+ }
1489
+ } ) {
1490
+ split_ctors. push ( IntRange :: range_to_ctor ( tcx, ty, range) ) ;
1521
1491
}
1522
1492
}
1523
1493
// Any other constructor can be used unchanged.
0 commit comments