Skip to content

Commit 1dbc781

Browse files
committed
Handle equivalence classes of length-1 ranges
1 parent e9c8361 commit 1dbc781

File tree

2 files changed

+64
-31
lines changed

2 files changed

+64
-31
lines changed

src/librustc_mir/hair/pattern/_match.rs

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ use self::Constructor::*;
166166
use self::Usefulness::*;
167167
use self::WitnessPreference::*;
168168

169-
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
169+
use rustc_data_structures::fx::FxHashMap;
170170
use rustc_data_structures::indexed_vec::Idx;
171171

172172
use super::{FieldPattern, Pattern, PatternKind};
@@ -321,7 +321,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
321321
tcx,
322322
module,
323323
pattern_arena: &pattern_arena,
324-
byte_array_map: FxHashMap(),
324+
byte_array_map: FxHashMap::default(),
325325
})
326326
}
327327

@@ -1422,50 +1422,77 @@ fn split_grouped_constructors<'p, 'a: 'p, 'tcx: 'a>(
14221422
}
14231423
// We're going to collect all the endpoints in the new pattern so we can create
14241424
// 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+
};
14261450
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);
14291452
// We're going to iterate through every row pattern, adding endpoints in.
14301453
for row in m.iter() {
14311454
if let Some(r) = IntRange::from_pat(tcx, row[0]) {
14321455
// We're only interested in endpoints that lie (at least partially)
14331456
// within the subrange domain.
14341457
if let Some(r) = ctor_range.intersection(&r) {
14351458
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);
14551460
}
14561461
}
14571462
}
14581463

14591464
// The patterns were iterated in an arbitrary order (i.e. in the order the user
14601465
// 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);
14631468
let mut points = points.into_iter();
1464-
let mut start = points.next().unwrap();
1469+
let mut a = points.next().unwrap();
1470+
14651471
// 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;
14691496
}
14701497
}
14711498
// Any other constructor can be used unchanged.

src/test/ui/exhaustive_integer_patterns.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,16 @@ fn main() {
145145
(0..=255, true) => {}
146146
}
147147

148-
match (0u8, true) {
148+
match (0u8, true) { // ok
149149
(0..=125, false) => {}
150150
(128..=255, false) => {}
151151
(0..=255, true) => {}
152152
(125..128, false) => {}
153153
}
154+
155+
match 0u8 { // ok
156+
0..2 => {}
157+
1..=2 => {}
158+
_ => {}
159+
}
154160
}

0 commit comments

Comments
 (0)