@@ -64,6 +64,7 @@ use rustc_span::{Span, DUMMY_SP};
64
64
use rustc_target:: abi:: { FieldIdx , Integer , VariantIdx , FIRST_VARIANT } ;
65
65
66
66
use self :: Constructor :: * ;
67
+ use self :: MaybeInfiniteInt :: * ;
67
68
use self :: SliceKind :: * ;
68
69
69
70
use super :: usefulness:: { MatchCheckCtxt , PatCtxt } ;
@@ -93,20 +94,99 @@ enum Presence {
93
94
Seen ,
94
95
}
95
96
97
+ /// A possibly infinite integer. Values are encoded such that the ordering on `u128` matches the
98
+ /// natural order on the original type. For example, `-128i8` is encoded as `0` and `127i8` as
99
+ /// `255`. See `signed_bias` for details.
100
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
101
+ enum MaybeInfiniteInt {
102
+ NegInfinity ,
103
+ /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`.
104
+ Finite ( u128 ) ,
105
+ /// The integer after `u128::MAX`. Used when we switch to exclusive ranges in `IntRange::split`.
106
+ JustAfterMax ,
107
+ PosInfinity ,
108
+ }
109
+
110
+ impl MaybeInfiniteInt {
111
+ // The return value of `signed_bias` should be XORed with a value to encode/decode it.
112
+ fn signed_bias ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > ) -> u128 {
113
+ match * ty. kind ( ) {
114
+ ty:: Int ( ity) => {
115
+ let bits = Integer :: from_int_ty ( & tcx, ity) . size ( ) . bits ( ) as u128 ;
116
+ 1u128 << ( bits - 1 )
117
+ }
118
+ _ => 0 ,
119
+ }
120
+ }
121
+
122
+ fn new_finite ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > , bits : u128 ) -> Self {
123
+ let bias = Self :: signed_bias ( tcx, ty) ;
124
+ // Perform a shift if the underlying types are signed, which makes the interval arithmetic
125
+ // type-independent.
126
+ let x = bits ^ bias;
127
+ Finite ( x)
128
+ }
129
+ fn from_pat_range_bdy < ' tcx > (
130
+ bdy : PatRangeBoundary < ' tcx > ,
131
+ ty : Ty < ' tcx > ,
132
+ tcx : TyCtxt < ' tcx > ,
133
+ param_env : ty:: ParamEnv < ' tcx > ,
134
+ ) -> Self {
135
+ match bdy {
136
+ PatRangeBoundary :: NegInfinity => NegInfinity ,
137
+ PatRangeBoundary :: Finite ( value) => {
138
+ let bits = value. eval_bits ( tcx, param_env) ;
139
+ Self :: new_finite ( tcx, ty, bits)
140
+ }
141
+ PatRangeBoundary :: PosInfinity => PosInfinity ,
142
+ }
143
+ }
144
+ fn to_pat_range_bdy < ' tcx > ( self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> PatRangeBoundary < ' tcx > {
145
+ match self {
146
+ NegInfinity => PatRangeBoundary :: NegInfinity ,
147
+ Finite ( x) => {
148
+ let bias = Self :: signed_bias ( tcx, ty) ;
149
+ let bits = x ^ bias;
150
+ let env = ty:: ParamEnv :: empty ( ) . and ( ty) ;
151
+ let value = mir:: Const :: from_bits ( tcx, bits, env) ;
152
+ PatRangeBoundary :: Finite ( value)
153
+ }
154
+ JustAfterMax | PosInfinity => PatRangeBoundary :: PosInfinity ,
155
+ }
156
+ }
157
+
158
+ fn minus_one ( self ) -> Self {
159
+ match self {
160
+ Finite ( n) => match n. checked_sub ( 1 ) {
161
+ Some ( m) => Finite ( m) ,
162
+ None => NegInfinity ,
163
+ } ,
164
+ JustAfterMax => Finite ( u128:: MAX ) ,
165
+ x => x,
166
+ }
167
+ }
168
+ fn plus_one ( self ) -> Self {
169
+ match self {
170
+ Finite ( n) => match n. checked_add ( 1 ) {
171
+ Some ( m) => Finite ( m) ,
172
+ None => JustAfterMax ,
173
+ } ,
174
+ x => x,
175
+ }
176
+ }
177
+ }
178
+
96
179
/// An inclusive interval, used for precise integer exhaustiveness checking.
97
- /// `IntRange`s always store a contiguous range. This means that values are
98
- /// encoded such that `0` encodes the minimum value for the integer,
99
- /// regardless of the signedness.
100
- /// For example, the pattern `-128..=127i8` is encoded as `0..=255`.
101
- /// This makes comparisons and arithmetic on interval endpoints much more
102
- /// straightforward. See `signed_bias` for details.
180
+ /// `IntRange`s always store a contiguous range.
103
181
///
104
182
/// `IntRange` is never used to encode an empty range or a "range" that wraps
105
183
/// around the (offset) space: i.e., `range.lo <= range.hi`.
184
+ ///
185
+ /// The range can have open ends.
106
186
#[ derive( Clone , Copy , PartialEq , Eq ) ]
107
187
pub ( crate ) struct IntRange {
108
- lo : u128 ,
109
- hi : u128 ,
188
+ lo : MaybeInfiniteInt , // Must not be `PosInfinity`.
189
+ hi : MaybeInfiniteInt , // Must not be `NegInfinity`.
110
190
}
111
191
112
192
impl IntRange {
@@ -115,51 +195,29 @@ impl IntRange {
115
195
matches ! ( ty. kind( ) , ty:: Char | ty:: Int ( _) | ty:: Uint ( _) )
116
196
}
117
197
198
+ /// Best effort; will not know that e.g. `255u8..` is a singleton.
118
199
fn is_singleton ( & self ) -> bool {
119
200
self . lo == self . hi
120
201
}
121
202
122
203
#[ inline]
123
204
fn from_bits < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , bits : u128 ) -> IntRange {
124
- let bias = IntRange :: signed_bias ( tcx, ty) ;
125
- // Perform a shift if the underlying types are signed, which makes the interval arithmetic
126
- // type-independent.
127
- let val = bits ^ bias;
128
- IntRange { lo : val, hi : val }
205
+ let x = MaybeInfiniteInt :: new_finite ( tcx, ty, bits) ;
206
+ IntRange { lo : x, hi : x }
129
207
}
130
208
131
209
#[ inline]
132
- fn from_range < ' tcx > (
133
- tcx : TyCtxt < ' tcx > ,
134
- lo : u128 ,
135
- hi : u128 ,
136
- ty : Ty < ' tcx > ,
137
- end : RangeEnd ,
138
- ) -> IntRange {
139
- // Perform a shift if the underlying types are signed, which makes the interval arithmetic
140
- // type-independent.
141
- let bias = IntRange :: signed_bias ( tcx, ty) ;
142
- let ( lo, hi) = ( lo ^ bias, hi ^ bias) ;
143
- let offset = ( end == RangeEnd :: Excluded ) as u128 ;
144
- let hi = hi - offset;
210
+ fn from_range ( lo : MaybeInfiniteInt , mut hi : MaybeInfiniteInt , end : RangeEnd ) -> IntRange {
211
+ if end == RangeEnd :: Excluded {
212
+ hi = hi. minus_one ( ) ;
213
+ }
145
214
if lo > hi {
146
215
// This should have been caught earlier by E0030.
147
- bug ! ( "malformed range pattern: {lo}..={hi}" ) ;
216
+ bug ! ( "malformed range pattern: {lo:? }..={hi:? }" ) ;
148
217
}
149
218
IntRange { lo, hi }
150
219
}
151
220
152
- // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
153
- fn signed_bias ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > ) -> u128 {
154
- match * ty. kind ( ) {
155
- ty:: Int ( ity) => {
156
- let bits = Integer :: from_int_ty ( & tcx, ity) . size ( ) . bits ( ) as u128 ;
157
- 1u128 << ( bits - 1 )
158
- }
159
- _ => 0 ,
160
- }
161
- }
162
-
163
221
fn is_subrange ( & self , other : & Self ) -> bool {
164
222
other. lo <= self . lo && self . hi <= other. hi
165
223
}
@@ -220,29 +278,16 @@ impl IntRange {
220
278
& self ,
221
279
column_ranges : impl Iterator < Item = IntRange > ,
222
280
) -> impl Iterator < Item = ( Presence , IntRange ) > {
223
- /// Represents a boundary between 2 integers. Because the intervals spanning boundaries must be
224
- /// able to cover every integer, we need to be able to represent 2^128 + 1 such boundaries.
225
- #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
226
- enum IntBoundary {
227
- JustBefore ( u128 ) ,
228
- AfterMax ,
229
- }
230
-
231
- fn unpack_intrange ( range : IntRange ) -> [ IntBoundary ; 2 ] {
232
- use IntBoundary :: * ;
233
- let lo = JustBefore ( range. lo ) ;
234
- let hi = match range. hi . checked_add ( 1 ) {
235
- Some ( m) => JustBefore ( m) ,
236
- None => AfterMax ,
237
- } ;
238
- [ lo, hi]
281
+ // Make the range into an exclusive range.
282
+ fn unpack_intrange ( range : IntRange ) -> [ MaybeInfiniteInt ; 2 ] {
283
+ [ range. lo , range. hi . plus_one ( ) ]
239
284
}
240
285
241
286
// The boundaries of ranges in `column_ranges` intersected with `self`.
242
287
// We do parenthesis matching for input ranges. A boundary counts as +1 if it starts
243
288
// a range and -1 if it ends it. When the count is > 0 between two boundaries, we
244
289
// are within an input range.
245
- let mut boundaries: Vec < ( IntBoundary , isize ) > = column_ranges
290
+ let mut boundaries: Vec < ( MaybeInfiniteInt , isize ) > = column_ranges
246
291
. filter_map ( |r| self . intersection ( & r) )
247
292
. map ( unpack_intrange)
248
293
. flat_map ( |[ lo, hi] | [ ( lo, 1 ) , ( hi, -1 ) ] )
@@ -252,7 +297,7 @@ impl IntRange {
252
297
// the accumulated count between distinct boundary values.
253
298
boundaries. sort_unstable ( ) ;
254
299
255
- let [ self_start, self_end] = unpack_intrange ( self . clone ( ) ) ;
300
+ let [ self_start, self_end] = unpack_intrange ( * self ) ;
256
301
// Accumulate parenthesis counts.
257
302
let mut paren_counter = 0isize ;
258
303
// Gather pairs of adjacent boundaries.
@@ -274,36 +319,26 @@ impl IntRange {
274
319
. filter ( |& ( prev_bdy, _, bdy) | prev_bdy != bdy)
275
320
// Convert back to ranges.
276
321
. map ( move |( prev_bdy, paren_count, bdy) | {
277
- use IntBoundary :: * ;
278
322
use Presence :: * ;
279
323
let presence = if paren_count > 0 { Seen } else { Unseen } ;
280
- let ( lo, hi) = match ( prev_bdy, bdy) {
281
- ( JustBefore ( n) , JustBefore ( m) ) if n < m => ( n, m - 1 ) ,
282
- ( JustBefore ( n) , AfterMax ) => ( n, u128:: MAX ) ,
283
- _ => unreachable ! ( ) , // Ruled out by the sorting and filtering we did
284
- } ;
285
- ( presence, IntRange { lo, hi } )
324
+ // Turn back into an inclusive range.
325
+ let range = IntRange :: from_range ( prev_bdy, bdy, RangeEnd :: Excluded ) ;
326
+ ( presence, range)
286
327
} )
287
328
}
288
329
289
330
/// Only used for displaying the range.
290
- fn to_pat < ' tcx > ( & self , tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Pat < ' tcx > {
291
- let bias = IntRange :: signed_bias ( tcx, ty) ;
292
- let ( lo_bits, hi_bits) = ( self . lo ^ bias, self . hi ^ bias) ;
293
-
294
- let env = ty:: ParamEnv :: empty ( ) . and ( ty) ;
295
- let lo_const = mir:: Const :: from_bits ( tcx, lo_bits, env) ;
296
- let hi_const = mir:: Const :: from_bits ( tcx, hi_bits, env) ;
297
-
298
- let kind = if lo_bits == hi_bits {
299
- PatKind :: Constant { value : lo_const }
331
+ fn to_pat < ' tcx > ( & self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Pat < ' tcx > {
332
+ let lo = self . lo . to_pat_range_bdy ( ty, tcx) ;
333
+ let hi = self . hi . to_pat_range_bdy ( ty, tcx) ;
334
+
335
+ let kind = if self . is_singleton ( ) {
336
+ let value = lo. as_finite ( ) . unwrap ( ) ;
337
+ PatKind :: Constant { value }
338
+ } else if matches ! ( ( self . lo, self . hi) , ( NegInfinity , PosInfinity ) ) {
339
+ PatKind :: Wild
300
340
} else {
301
- PatKind :: Range ( Box :: new ( PatRange {
302
- lo : PatRangeBoundary :: Finite ( lo_const) ,
303
- hi : PatRangeBoundary :: Finite ( hi_const) ,
304
- end : RangeEnd :: Included ,
305
- ty,
306
- } ) )
341
+ PatKind :: Range ( Box :: new ( PatRange { lo, hi, end : RangeEnd :: Included , ty } ) )
307
342
} ;
308
343
309
344
Pat { ty, span : DUMMY_SP , kind }
@@ -339,7 +374,7 @@ impl IntRange {
339
374
. filter_map ( |pat| Some ( ( pat. ctor ( ) . as_int_range ( ) ?, pat. span ( ) ) ) )
340
375
. filter ( |( range, _) | self . suspicious_intersection ( range) )
341
376
. map ( |( range, span) | Overlap {
342
- range : self . intersection ( & range) . unwrap ( ) . to_pat ( pcx. cx . tcx , pcx. ty ) ,
377
+ range : self . intersection ( & range) . unwrap ( ) . to_pat ( pcx. ty , pcx. cx . tcx ) ,
343
378
span,
344
379
} )
345
380
. collect ( ) ;
@@ -359,10 +394,14 @@ impl IntRange {
359
394
/// first.
360
395
impl fmt:: Debug for IntRange {
361
396
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
362
- let ( lo, hi) = ( self . lo , self . hi ) ;
363
- write ! ( f, "{lo}" ) ?;
397
+ if let Finite ( lo) = self . lo {
398
+ write ! ( f, "{lo}" ) ?;
399
+ }
364
400
write ! ( f, "{}" , RangeEnd :: Included ) ?;
365
- write ! ( f, "{hi}" )
401
+ if let Finite ( hi) = self . hi {
402
+ write ! ( f, "{hi}" ) ?;
403
+ }
404
+ Ok ( ( ) )
366
405
}
367
406
}
368
407
@@ -919,8 +958,13 @@ struct SplitConstructorSet<'tcx> {
919
958
impl ConstructorSet {
920
959
#[ instrument( level = "debug" , skip( cx) , ret) ]
921
960
pub ( super ) fn for_ty < ' p , ' tcx > ( cx : & MatchCheckCtxt < ' p , ' tcx > , ty : Ty < ' tcx > ) -> Self {
922
- let make_range =
923
- |start, end| IntRange :: from_range ( cx. tcx , start, end, ty, RangeEnd :: Included ) ;
961
+ let make_range = |start, end| {
962
+ IntRange :: from_range (
963
+ MaybeInfiniteInt :: new_finite ( cx. tcx , ty, start) ,
964
+ MaybeInfiniteInt :: new_finite ( cx. tcx , ty, end) ,
965
+ RangeEnd :: Included ,
966
+ )
967
+ } ;
924
968
// This determines the set of all possible constructors for the type `ty`. For numbers,
925
969
// arrays and slices we use ranges and variable-length slices when appropriate.
926
970
//
@@ -1504,24 +1548,33 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
1504
1548
}
1505
1549
}
1506
1550
PatKind :: Range ( box PatRange { lo, hi, end, .. } ) => {
1507
- use rustc_apfloat:: Float ;
1508
1551
let ty = pat. ty ;
1509
- // FIXME: handle half-open ranges
1510
- let lo = lo. eval_bits ( ty, cx. tcx , cx. param_env ) ;
1511
- let hi = hi. eval_bits ( ty, cx. tcx , cx. param_env ) ;
1512
1552
ctor = match ty. kind ( ) {
1513
1553
ty:: Char | ty:: Int ( _) | ty:: Uint ( _) => {
1514
- IntRange ( IntRange :: from_range ( cx. tcx , lo, hi, ty, * end) )
1515
- }
1516
- ty:: Float ( ty:: FloatTy :: F32 ) => {
1517
- let lo = rustc_apfloat:: ieee:: Single :: from_bits ( lo) ;
1518
- let hi = rustc_apfloat:: ieee:: Single :: from_bits ( hi) ;
1519
- F32Range ( lo, hi, * end)
1554
+ let lo =
1555
+ MaybeInfiniteInt :: from_pat_range_bdy ( * lo, ty, cx. tcx , cx. param_env ) ;
1556
+ let hi =
1557
+ MaybeInfiniteInt :: from_pat_range_bdy ( * hi, ty, cx. tcx , cx. param_env ) ;
1558
+ IntRange ( IntRange :: from_range ( lo, hi, * end) )
1520
1559
}
1521
- ty:: Float ( ty:: FloatTy :: F64 ) => {
1522
- let lo = rustc_apfloat:: ieee:: Double :: from_bits ( lo) ;
1523
- let hi = rustc_apfloat:: ieee:: Double :: from_bits ( hi) ;
1524
- F64Range ( lo, hi, * end)
1560
+ ty:: Float ( fty) => {
1561
+ use rustc_apfloat:: Float ;
1562
+ let lo = lo. as_finite ( ) . map ( |c| c. eval_bits ( cx. tcx , cx. param_env ) ) ;
1563
+ let hi = hi. as_finite ( ) . map ( |c| c. eval_bits ( cx. tcx , cx. param_env ) ) ;
1564
+ match fty {
1565
+ ty:: FloatTy :: F32 => {
1566
+ use rustc_apfloat:: ieee:: Single ;
1567
+ let lo = lo. map ( Single :: from_bits) . unwrap_or ( -Single :: INFINITY ) ;
1568
+ let hi = hi. map ( Single :: from_bits) . unwrap_or ( Single :: INFINITY ) ;
1569
+ F32Range ( lo, hi, * end)
1570
+ }
1571
+ ty:: FloatTy :: F64 => {
1572
+ use rustc_apfloat:: ieee:: Double ;
1573
+ let lo = lo. map ( Double :: from_bits) . unwrap_or ( -Double :: INFINITY ) ;
1574
+ let hi = hi. map ( Double :: from_bits) . unwrap_or ( Double :: INFINITY ) ;
1575
+ F64Range ( lo, hi, * end)
1576
+ }
1577
+ }
1525
1578
}
1526
1579
_ => bug ! ( "invalid type for range pattern: {}" , ty) ,
1527
1580
} ;
@@ -1628,7 +1681,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
1628
1681
}
1629
1682
}
1630
1683
Bool ( b) => PatKind :: Constant { value : mir:: Const :: from_bool ( cx. tcx , * b) } ,
1631
- IntRange ( range) => return range. to_pat ( cx . tcx , self . ty ) ,
1684
+ IntRange ( range) => return range. to_pat ( self . ty , cx . tcx ) ,
1632
1685
& Str ( value) => PatKind :: Constant { value } ,
1633
1686
Wildcard | NonExhaustive | Hidden => PatKind :: Wild ,
1634
1687
Missing { .. } => bug ! (
0 commit comments