@@ -166,7 +166,7 @@ use self::Constructor::*;
166
166
use self :: Usefulness :: * ;
167
167
use self :: WitnessPreference :: * ;
168
168
169
- use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
169
+ use rustc_data_structures:: fx:: FxHashMap ;
170
170
use rustc_data_structures:: indexed_vec:: Idx ;
171
171
172
172
use super :: { FieldPattern , Pattern , PatternKind } ;
@@ -321,7 +321,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
321
321
tcx,
322
322
module,
323
323
pattern_arena : & pattern_arena,
324
- byte_array_map : FxHashMap ( ) ,
324
+ byte_array_map : FxHashMap :: default ( ) ,
325
325
} )
326
326
}
327
327
@@ -1422,50 +1422,77 @@ fn split_grouped_constructors<'p, 'a: 'p, 'tcx: 'a>(
1422
1422
}
1423
1423
// We're going to collect all the endpoints in the new pattern so we can create
1424
1424
// subranges between them.
1425
- let mut points = FxHashSet :: default ( ) ;
1425
+ // If there's a single point, we need to identify it as belonging
1426
+ // to a length-1 range, so it can be treated as an individual
1427
+ // constructor, rather than as an endpoint. To do this, we keep track of which
1428
+ // endpoint a point corresponds to. Whenever a point corresponds to both a start
1429
+ // and an end, then we create a unit range for it.
1430
+ #[ derive( PartialEq , Clone , Copy , Debug ) ]
1431
+ enum Endpoint {
1432
+ Start ,
1433
+ End ,
1434
+ Both ,
1435
+ } ;
1436
+ let mut points = FxHashMap :: default ( ) ;
1437
+ let add_endpoint = |points : & mut FxHashMap < _ , _ > , x, endpoint| {
1438
+ points. entry ( x) . and_modify ( |ex_x| {
1439
+ if * ex_x != endpoint {
1440
+ * ex_x = Endpoint :: Both
1441
+ }
1442
+ } ) . or_insert ( endpoint) ;
1443
+ } ;
1444
+ let add_endpoints = |points : & mut FxHashMap < _ , _ > , lo, hi| {
1445
+ // Insert the endpoints, taking care to keep track of to
1446
+ // which endpoints a point corresponds.
1447
+ add_endpoint ( points, lo, Endpoint :: Start ) ;
1448
+ add_endpoint ( points, hi, Endpoint :: End ) ;
1449
+ } ;
1426
1450
let ( lo, hi) = ( * ctor_range. range . start ( ) , * ctor_range. range . end ( ) ) ;
1427
- points. insert ( lo) ;
1428
- points. insert ( hi) ;
1451
+ add_endpoints ( & mut points, lo, hi) ;
1429
1452
// We're going to iterate through every row pattern, adding endpoints in.
1430
1453
for row in m. iter ( ) {
1431
1454
if let Some ( r) = IntRange :: from_pat ( tcx, row[ 0 ] ) {
1432
1455
// We're only interested in endpoints that lie (at least partially)
1433
1456
// within the subrange domain.
1434
1457
if let Some ( r) = ctor_range. intersection ( & r) {
1435
1458
let ( r_lo, r_hi) = r. range . into_inner ( ) ;
1436
- // Insert the endpoints.
1437
- points. insert ( r_lo) ;
1438
- points. insert ( r_hi) ;
1439
- // There's a slight subtlety here, which involves the fact we're using
1440
- // inclusive ranges everywhere. When we subdivide the range into
1441
- // subranges, they can't overlap, or the subranges effectively
1442
- // coalesce. We need hard boundaries between subranges. The simplest
1443
- // way to do this is by adding extra "boundary points" to prevent this
1444
- // intersection. Technically this means we occasionally check a few more
1445
- // cases for usefulness than we need to (because they're part of another
1446
- // equivalence class), but it's still linear and very simple to verify,
1447
- // which is handy when it comes to matching, which can often be quite
1448
- // fiddly.
1449
- if r_lo > lo {
1450
- points. insert ( r_lo - 1 ) ;
1451
- }
1452
- if r_hi < hi {
1453
- points. insert ( r_hi + 1 ) ;
1454
- }
1459
+ add_endpoints ( & mut points, r_lo, r_hi) ;
1455
1460
}
1456
1461
}
1457
1462
}
1458
1463
1459
1464
// The patterns were iterated in an arbitrary order (i.e. in the order the user
1460
1465
// wrote them), so we need to make sure our endpoints are sorted.
1461
- let mut points: Vec < _ > = points. into_iter ( ) . collect ( ) ;
1462
- points. sort ( ) ;
1466
+ let mut points: Vec < ( u128 , Endpoint ) > = points. into_iter ( ) . collect ( ) ;
1467
+ points. sort_unstable_by_key ( | ( x , _ ) | * x ) ;
1463
1468
let mut points = points. into_iter ( ) ;
1464
- let mut start = points. next ( ) . unwrap ( ) ;
1469
+ let mut a = points. next ( ) . unwrap ( ) ;
1470
+
1465
1471
// Iterate through pairs of points, adding the subranges to `split_ctors`.
1466
- while let Some ( end) = points. next ( ) {
1467
- split_ctors. push ( IntRange :: range_to_ctor ( tcx, ty, start..=end) ) ;
1468
- start = end;
1472
+ // We have to be careful about the orientation of the points as endpoints, to make
1473
+ // sure we're enumerating precisely the correct ranges. Too few and the matching is
1474
+ // actually incorrect. Too many and our diagnostics are poorer. This involves some
1475
+ // case analysis.
1476
+ while let Some ( b) = points. next ( ) {
1477
+ // a < b (strictly)
1478
+ if let Endpoint :: Both = a. 1 {
1479
+ split_ctors. push ( IntRange :: range_to_ctor ( tcx, ty, a. 0 ..=a. 0 ) ) ;
1480
+ }
1481
+ let c = match a. 1 {
1482
+ Endpoint :: Start => a. 0 ,
1483
+ Endpoint :: End | Endpoint :: Both => a. 0 + 1 ,
1484
+ } ;
1485
+ let d = match b. 1 {
1486
+ Endpoint :: Start | Endpoint :: Both => b. 0 - 1 ,
1487
+ Endpoint :: End => b. 0 ,
1488
+ } ;
1489
+ // In some cases, we won't need an intermediate range between two ranges
1490
+ // lie immediately adjacent to one another.
1491
+ if c <= d {
1492
+ split_ctors. push ( IntRange :: range_to_ctor ( tcx, ty, c..=d) ) ;
1493
+ }
1494
+
1495
+ a = b;
1469
1496
}
1470
1497
}
1471
1498
// Any other constructor can be used unchanged.
0 commit comments