@@ -194,6 +194,7 @@ use std::cmp::{self, Ordering, min, max};
194194use std:: fmt;
195195use std:: iter:: { FromIterator , IntoIterator } ;
196196use std:: ops:: RangeInclusive ;
197+ use std:: u128;
197198
198199pub fn expand_pattern < ' a , ' tcx > ( cx : & MatchCheckCtxt < ' a , ' tcx > , pat : Pattern < ' tcx > )
199200 -> & ' a Pattern < ' tcx >
@@ -799,6 +800,7 @@ fn max_slice_length<'p, 'a: 'p, 'tcx: 'a, I>(
799800///
800801/// `IntRange` is never used to encode an empty range or a "range" that wraps
801802/// around the (offset) space: i.e. `range.lo <= range.hi`.
803+ #[ derive( Clone ) ]
802804struct IntRange < ' tcx > {
803805 pub range : RangeInclusive < u128 > ,
804806 pub ty : Ty < ' tcx > ,
@@ -1400,9 +1402,7 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'_, 'tcx, 'tcx>, ctor: &Construct
14001402/// patterns that apply to that range (specifically: the patterns that *intersect* with that range)
14011403/// change.
14021404/// 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).
14061406/// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching
14071407/// on actual integers. The nice thing about this is that the number of subranges is linear in the
14081408/// 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
14141414/// |-------| |-------| |----| ||
14151415/// |---------|
14161416///
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:
14191418///
14201419/// |--|--|||-||||--||---|||-------| |-|||| ||
14211420///
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.)
14251425fn split_grouped_constructors < ' p , ' a : ' p , ' tcx : ' a > (
14261426 tcx : TyCtxt < ' a , ' tcx , ' tcx > ,
14271427 ctors : Vec < Constructor < ' tcx > > ,
@@ -1440,84 +1440,54 @@ fn split_grouped_constructors<'p, 'a: 'p, 'tcx: 'a>(
14401440 // `NotUseful`, which is the default case anyway, and can be ignored.
14411441 let ctor_range = IntRange :: from_ctor ( tcx, & ctor) . unwrap ( ) ;
14421442
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 ,
14821449 }
14831450
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 ,
15131458 } ;
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+ }
15191461
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) ) ;
15211491 }
15221492 }
15231493 // Any other constructor can be used unchanged.
0 commit comments