@@ -63,6 +63,7 @@ use rustc_span::{Span, DUMMY_SP};
63
63
use rustc_target:: abi:: { FieldIdx , Integer , VariantIdx , FIRST_VARIANT } ;
64
64
65
65
use self :: Constructor :: * ;
66
+ use self :: MaybeInfiniteInt :: * ;
66
67
use self :: SliceKind :: * ;
67
68
68
69
use super :: usefulness:: { MatchCheckCtxt , PatCtxt } ;
@@ -91,20 +92,99 @@ enum Presence {
91
92
Seen ,
92
93
}
93
94
95
+ /// A possibly infinite integer. Values are encoded such that the ordering on `u128` matches the
96
+ /// natural order on the original type. For example, `-128i8` is encoded as `0` and `127i8` as
97
+ /// `255`. See `signed_bias` for details.
98
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
99
+ pub ( crate ) enum MaybeInfiniteInt {
100
+ NegInfinity ,
101
+ /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`.
102
+ Finite ( u128 ) ,
103
+ /// The integer after `u128::MAX`. Used when we switch to exclusive ranges in `IntRange::split`.
104
+ JustAfterMax ,
105
+ PosInfinity ,
106
+ }
107
+
108
+ impl MaybeInfiniteInt {
109
+ // The return value of `signed_bias` should be XORed with a value to encode/decode it.
110
+ fn signed_bias ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > ) -> u128 {
111
+ match * ty. kind ( ) {
112
+ ty:: Int ( ity) => {
113
+ let bits = Integer :: from_int_ty ( & tcx, ity) . size ( ) . bits ( ) as u128 ;
114
+ 1u128 << ( bits - 1 )
115
+ }
116
+ _ => 0 ,
117
+ }
118
+ }
119
+
120
+ fn new_finite ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > , bits : u128 ) -> Self {
121
+ let bias = Self :: signed_bias ( tcx, ty) ;
122
+ // Perform a shift if the underlying types are signed, which makes the interval arithmetic
123
+ // type-independent.
124
+ let x = bits ^ bias;
125
+ Finite ( x)
126
+ }
127
+ fn from_pat_range_bdy < ' tcx > (
128
+ bdy : PatRangeBoundary < ' tcx > ,
129
+ ty : Ty < ' tcx > ,
130
+ tcx : TyCtxt < ' tcx > ,
131
+ param_env : ty:: ParamEnv < ' tcx > ,
132
+ ) -> Self {
133
+ match bdy {
134
+ PatRangeBoundary :: NegInfinity => NegInfinity ,
135
+ PatRangeBoundary :: Finite ( value) => {
136
+ let bits = value. eval_bits ( tcx, param_env) ;
137
+ Self :: new_finite ( tcx, ty, bits)
138
+ }
139
+ PatRangeBoundary :: PosInfinity => PosInfinity ,
140
+ }
141
+ }
142
+ fn to_pat_range_bdy < ' tcx > ( self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> PatRangeBoundary < ' tcx > {
143
+ match self {
144
+ NegInfinity => PatRangeBoundary :: NegInfinity ,
145
+ Finite ( x) => {
146
+ let bias = Self :: signed_bias ( tcx, ty) ;
147
+ let bits = x ^ bias;
148
+ let env = ty:: ParamEnv :: empty ( ) . and ( ty) ;
149
+ let value = mir:: Const :: from_bits ( tcx, bits, env) ;
150
+ PatRangeBoundary :: Finite ( value)
151
+ }
152
+ JustAfterMax | PosInfinity => PatRangeBoundary :: PosInfinity ,
153
+ }
154
+ }
155
+
156
+ fn minus_one ( self ) -> Self {
157
+ match self {
158
+ Finite ( n) => match n. checked_sub ( 1 ) {
159
+ Some ( m) => Finite ( m) ,
160
+ None => NegInfinity ,
161
+ } ,
162
+ JustAfterMax => Finite ( u128:: MAX ) ,
163
+ x => x,
164
+ }
165
+ }
166
+ fn plus_one ( self ) -> Self {
167
+ match self {
168
+ Finite ( n) => match n. checked_add ( 1 ) {
169
+ Some ( m) => Finite ( m) ,
170
+ None => JustAfterMax ,
171
+ } ,
172
+ x => x,
173
+ }
174
+ }
175
+ }
176
+
94
177
/// An inclusive interval, used for precise integer exhaustiveness checking.
95
- /// `IntRange`s always store a contiguous range. This means that values are
96
- /// encoded such that `0` encodes the minimum value for the integer,
97
- /// regardless of the signedness.
98
- /// For example, the pattern `-128..=127i8` is encoded as `0..=255`.
99
- /// This makes comparisons and arithmetic on interval endpoints much more
100
- /// straightforward. See `signed_bias` for details.
178
+ /// `IntRange`s always store a contiguous range.
101
179
///
102
180
/// `IntRange` is never used to encode an empty range or a "range" that wraps
103
181
/// around the (offset) space: i.e., `range.lo <= range.hi`.
182
+ ///
183
+ /// The range can have open ends.
104
184
#[ derive( Clone , Copy , PartialEq , Eq ) ]
105
185
pub ( crate ) struct IntRange {
106
- pub ( crate ) lo : u128 ,
107
- pub ( crate ) hi : u128 ,
186
+ pub ( crate ) lo : MaybeInfiniteInt , // Must not be `PosInfinity`.
187
+ pub ( crate ) hi : MaybeInfiniteInt , // Must not be `NegInfinity`.
108
188
}
109
189
110
190
impl IntRange {
@@ -113,51 +193,31 @@ impl IntRange {
113
193
matches ! ( ty. kind( ) , ty:: Char | ty:: Int ( _) | ty:: Uint ( _) )
114
194
}
115
195
196
+ /// Best effort; will not know that e.g. `255u8..` is a singleton.
116
197
pub ( super ) fn is_singleton ( & self ) -> bool {
198
+ // Since `lo` and `hi` can't be the same `Infinity`, this correctly only detects a
199
+ // `Finite(x)` singleton.
117
200
self . lo == self . hi
118
201
}
119
202
120
203
#[ inline]
121
204
fn from_bits < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , bits : u128 ) -> IntRange {
122
- let bias = IntRange :: signed_bias ( tcx, ty) ;
123
- // Perform a shift if the underlying types are signed, which makes the interval arithmetic
124
- // type-independent.
125
- let val = bits ^ bias;
126
- IntRange { lo : val, hi : val }
205
+ let x = MaybeInfiniteInt :: new_finite ( tcx, ty, bits) ;
206
+ IntRange { lo : x, hi : x }
127
207
}
128
208
129
209
#[ inline]
130
- fn from_range < ' tcx > (
131
- tcx : TyCtxt < ' tcx > ,
132
- lo : u128 ,
133
- hi : u128 ,
134
- ty : Ty < ' tcx > ,
135
- end : RangeEnd ,
136
- ) -> IntRange {
137
- // Perform a shift if the underlying types are signed, which makes the interval arithmetic
138
- // type-independent.
139
- let bias = IntRange :: signed_bias ( tcx, ty) ;
140
- let ( lo, hi) = ( lo ^ bias, hi ^ bias) ;
141
- let offset = ( end == RangeEnd :: Excluded ) as u128 ;
142
- 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
+ }
143
214
if lo > hi {
144
215
// This should have been caught earlier by E0030.
145
- bug ! ( "malformed range pattern: {lo}..={hi}" ) ;
216
+ bug ! ( "malformed range pattern: {lo:? }..={hi:? }" ) ;
146
217
}
147
218
IntRange { lo, hi }
148
219
}
149
220
150
- // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
151
- fn signed_bias ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > ) -> u128 {
152
- match * ty. kind ( ) {
153
- ty:: Int ( ity) => {
154
- let bits = Integer :: from_int_ty ( & tcx, ity) . size ( ) . bits ( ) as u128 ;
155
- 1u128 << ( bits - 1 )
156
- }
157
- _ => 0 ,
158
- }
159
- }
160
-
161
221
fn is_subrange ( & self , other : & Self ) -> bool {
162
222
other. lo <= self . lo && self . hi <= other. hi
163
223
}
@@ -201,29 +261,16 @@ impl IntRange {
201
261
& self ,
202
262
column_ranges : impl Iterator < Item = IntRange > ,
203
263
) -> impl Iterator < Item = ( Presence , IntRange ) > {
204
- /// Represents a boundary between 2 integers. Because the intervals spanning boundaries must be
205
- /// able to cover every integer, we need to be able to represent 2^128 + 1 such boundaries.
206
- #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
207
- enum IntBoundary {
208
- JustBefore ( u128 ) ,
209
- AfterMax ,
210
- }
211
-
212
- fn unpack_intrange ( range : IntRange ) -> [ IntBoundary ; 2 ] {
213
- use IntBoundary :: * ;
214
- let lo = JustBefore ( range. lo ) ;
215
- let hi = match range. hi . checked_add ( 1 ) {
216
- Some ( m) => JustBefore ( m) ,
217
- None => AfterMax ,
218
- } ;
219
- [ lo, hi]
264
+ // Make the range into an exclusive range.
265
+ fn unpack_intrange ( range : IntRange ) -> [ MaybeInfiniteInt ; 2 ] {
266
+ [ range. lo , range. hi . plus_one ( ) ]
220
267
}
221
268
222
269
// The boundaries of ranges in `column_ranges` intersected with `self`.
223
270
// We do parenthesis matching for input ranges. A boundary counts as +1 if it starts
224
271
// a range and -1 if it ends it. When the count is > 0 between two boundaries, we
225
272
// are within an input range.
226
- let mut boundaries: Vec < ( IntBoundary , isize ) > = column_ranges
273
+ let mut boundaries: Vec < ( MaybeInfiniteInt , isize ) > = column_ranges
227
274
. filter_map ( |r| self . intersection ( & r) )
228
275
. map ( unpack_intrange)
229
276
. flat_map ( |[ lo, hi] | [ ( lo, 1 ) , ( hi, -1 ) ] )
@@ -233,7 +280,7 @@ impl IntRange {
233
280
// the accumulated count between distinct boundary values.
234
281
boundaries. sort_unstable ( ) ;
235
282
236
- let [ self_start, self_end] = unpack_intrange ( self . clone ( ) ) ;
283
+ let [ self_start, self_end] = unpack_intrange ( * self ) ;
237
284
// Accumulate parenthesis counts.
238
285
let mut paren_counter = 0isize ;
239
286
// Gather pairs of adjacent boundaries.
@@ -255,36 +302,26 @@ impl IntRange {
255
302
. filter ( |& ( prev_bdy, _, bdy) | prev_bdy != bdy)
256
303
// Convert back to ranges.
257
304
. map ( move |( prev_bdy, paren_count, bdy) | {
258
- use IntBoundary :: * ;
259
305
use Presence :: * ;
260
306
let presence = if paren_count > 0 { Seen } else { Unseen } ;
261
- let ( lo, hi) = match ( prev_bdy, bdy) {
262
- ( JustBefore ( n) , JustBefore ( m) ) if n < m => ( n, m - 1 ) ,
263
- ( JustBefore ( n) , AfterMax ) => ( n, u128:: MAX ) ,
264
- _ => unreachable ! ( ) , // Ruled out by the sorting and filtering we did
265
- } ;
266
- ( presence, IntRange { lo, hi } )
307
+ // Turn back into an inclusive range.
308
+ let range = IntRange :: from_range ( prev_bdy, bdy, RangeEnd :: Excluded ) ;
309
+ ( presence, range)
267
310
} )
268
311
}
269
312
270
313
/// Only used for displaying the range.
271
- pub ( super ) fn to_pat < ' tcx > ( & self , tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Pat < ' tcx > {
272
- let bias = IntRange :: signed_bias ( tcx, ty) ;
273
- let ( lo_bits, hi_bits) = ( self . lo ^ bias, self . hi ^ bias) ;
274
-
275
- let env = ty:: ParamEnv :: empty ( ) . and ( ty) ;
276
- let lo_const = mir:: Const :: from_bits ( tcx, lo_bits, env) ;
277
- let hi_const = mir:: Const :: from_bits ( tcx, hi_bits, env) ;
278
-
279
- let kind = if lo_bits == hi_bits {
280
- PatKind :: Constant { value : lo_const }
314
+ pub ( super ) fn to_pat < ' tcx > ( & self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Pat < ' tcx > {
315
+ let lo = self . lo . to_pat_range_bdy ( ty, tcx) ;
316
+ let hi = self . hi . to_pat_range_bdy ( ty, tcx) ;
317
+
318
+ let kind = if self . is_singleton ( ) {
319
+ let value = lo. as_finite ( ) . unwrap ( ) ;
320
+ PatKind :: Constant { value }
321
+ } else if matches ! ( ( self . lo, self . hi) , ( NegInfinity , PosInfinity ) ) {
322
+ PatKind :: Wild
281
323
} else {
282
- PatKind :: Range ( Box :: new ( PatRange {
283
- lo : PatRangeBoundary :: Finite ( lo_const) ,
284
- hi : PatRangeBoundary :: Finite ( hi_const) ,
285
- end : RangeEnd :: Included ,
286
- ty,
287
- } ) )
324
+ PatKind :: Range ( Box :: new ( PatRange { lo, hi, end : RangeEnd :: Included , ty } ) )
288
325
} ;
289
326
290
327
Pat { ty, span : DUMMY_SP , kind }
@@ -295,10 +332,14 @@ impl IntRange {
295
332
/// first.
296
333
impl fmt:: Debug for IntRange {
297
334
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
298
- let ( lo, hi) = ( self . lo , self . hi ) ;
299
- write ! ( f, "{lo}" ) ?;
335
+ if let Finite ( lo) = self . lo {
336
+ write ! ( f, "{lo}" ) ?;
337
+ }
300
338
write ! ( f, "{}" , RangeEnd :: Included ) ?;
301
- write ! ( f, "{hi}" )
339
+ if let Finite ( hi) = self . hi {
340
+ write ! ( f, "{hi}" ) ?;
341
+ }
342
+ Ok ( ( ) )
302
343
}
303
344
}
304
345
@@ -840,8 +881,13 @@ pub(super) struct SplitConstructorSet<'tcx> {
840
881
impl ConstructorSet {
841
882
#[ instrument( level = "debug" , skip( cx) , ret) ]
842
883
pub ( super ) fn for_ty < ' p , ' tcx > ( cx : & MatchCheckCtxt < ' p , ' tcx > , ty : Ty < ' tcx > ) -> Self {
843
- let make_range =
844
- |start, end| IntRange :: from_range ( cx. tcx , start, end, ty, RangeEnd :: Included ) ;
884
+ let make_range = |start, end| {
885
+ IntRange :: from_range (
886
+ MaybeInfiniteInt :: new_finite ( cx. tcx , ty, start) ,
887
+ MaybeInfiniteInt :: new_finite ( cx. tcx , ty, end) ,
888
+ RangeEnd :: Included ,
889
+ )
890
+ } ;
845
891
// This determines the set of all possible constructors for the type `ty`. For numbers,
846
892
// arrays and slices we use ranges and variable-length slices when appropriate.
847
893
//
@@ -1419,24 +1465,33 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
1419
1465
}
1420
1466
}
1421
1467
PatKind :: Range ( box PatRange { lo, hi, end, .. } ) => {
1422
- use rustc_apfloat:: Float ;
1423
1468
let ty = pat. ty ;
1424
- // FIXME: handle half-open ranges
1425
- let lo = lo. eval_bits ( ty, cx. tcx , cx. param_env ) ;
1426
- let hi = hi. eval_bits ( ty, cx. tcx , cx. param_env ) ;
1427
1469
ctor = match ty. kind ( ) {
1428
1470
ty:: Char | ty:: Int ( _) | ty:: Uint ( _) => {
1429
- IntRange ( IntRange :: from_range ( cx. tcx , lo, hi, ty, * end) )
1430
- }
1431
- ty:: Float ( ty:: FloatTy :: F32 ) => {
1432
- let lo = rustc_apfloat:: ieee:: Single :: from_bits ( lo) ;
1433
- let hi = rustc_apfloat:: ieee:: Single :: from_bits ( hi) ;
1434
- F32Range ( lo, hi, * end)
1471
+ let lo =
1472
+ MaybeInfiniteInt :: from_pat_range_bdy ( * lo, ty, cx. tcx , cx. param_env ) ;
1473
+ let hi =
1474
+ MaybeInfiniteInt :: from_pat_range_bdy ( * hi, ty, cx. tcx , cx. param_env ) ;
1475
+ IntRange ( IntRange :: from_range ( lo, hi, * end) )
1435
1476
}
1436
- ty:: Float ( ty:: FloatTy :: F64 ) => {
1437
- let lo = rustc_apfloat:: ieee:: Double :: from_bits ( lo) ;
1438
- let hi = rustc_apfloat:: ieee:: Double :: from_bits ( hi) ;
1439
- F64Range ( lo, hi, * end)
1477
+ ty:: Float ( fty) => {
1478
+ use rustc_apfloat:: Float ;
1479
+ let lo = lo. as_finite ( ) . map ( |c| c. eval_bits ( cx. tcx , cx. param_env ) ) ;
1480
+ let hi = hi. as_finite ( ) . map ( |c| c. eval_bits ( cx. tcx , cx. param_env ) ) ;
1481
+ match fty {
1482
+ ty:: FloatTy :: F32 => {
1483
+ use rustc_apfloat:: ieee:: Single ;
1484
+ let lo = lo. map ( Single :: from_bits) . unwrap_or ( -Single :: INFINITY ) ;
1485
+ let hi = hi. map ( Single :: from_bits) . unwrap_or ( Single :: INFINITY ) ;
1486
+ F32Range ( lo, hi, * end)
1487
+ }
1488
+ ty:: FloatTy :: F64 => {
1489
+ use rustc_apfloat:: ieee:: Double ;
1490
+ let lo = lo. map ( Double :: from_bits) . unwrap_or ( -Double :: INFINITY ) ;
1491
+ let hi = hi. map ( Double :: from_bits) . unwrap_or ( Double :: INFINITY ) ;
1492
+ F64Range ( lo, hi, * end)
1493
+ }
1494
+ }
1440
1495
}
1441
1496
_ => bug ! ( "invalid type for range pattern: {}" , ty) ,
1442
1497
} ;
@@ -1706,7 +1761,7 @@ impl<'tcx> WitnessPat<'tcx> {
1706
1761
let mut subpatterns = self . iter_fields ( ) . map ( |p| Box :: new ( p. to_pat ( cx) ) ) ;
1707
1762
let kind = match & self . ctor {
1708
1763
Bool ( b) => PatKind :: Constant { value : mir:: Const :: from_bool ( cx. tcx , * b) } ,
1709
- IntRange ( range) => return range. to_pat ( cx . tcx , self . ty ) ,
1764
+ IntRange ( range) => return range. to_pat ( self . ty , cx . tcx ) ,
1710
1765
Single | Variant ( _) => match self . ty . kind ( ) {
1711
1766
ty:: Tuple ( ..) => PatKind :: Leaf {
1712
1767
subpatterns : subpatterns
0 commit comments