@@ -103,7 +103,7 @@ enum MaybeInfiniteInt {
103
103
NegInfinity ,
104
104
/// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`.
105
105
Finite ( u128 ) ,
106
- /// The integer after `u128::MAX`. Used when we switch to exclusive ranges in `IntRange::split` .
106
+ /// The integer after `u128::MAX`. We need it to represent `x..=u128::MAX` as an exclusive range .
107
107
JustAfterMax ,
108
108
PosInfinity ,
109
109
}
@@ -142,8 +142,11 @@ impl MaybeInfiniteInt {
142
142
PatRangeBoundary :: PosInfinity => PosInfinity ,
143
143
}
144
144
}
145
+
145
146
/// Used only for diagnostics.
146
- /// This could change from finite to infinite if we got `usize::MAX+1` after range splitting.
147
+ /// Note: it is possible to get `isize/usize::MAX+1` here, as explained in the doc for
148
+ /// [`IntRange::split`]. This cannot be represented as a `Const`, so we represent it with
149
+ /// `PosInfinity`.
147
150
fn to_diagnostic_pat_range_bdy < ' tcx > (
148
151
self ,
149
152
ty : Ty < ' tcx > ,
@@ -170,19 +173,18 @@ impl MaybeInfiniteInt {
170
173
}
171
174
}
172
175
173
- fn is_finite ( self ) -> bool {
174
- matches ! ( self , Finite ( _) )
175
- }
176
+ /// Note: this will not turn a finite value into an infinite one or vice-versa.
176
177
fn minus_one ( self ) -> Self {
177
178
match self {
178
179
Finite ( n) => match n. checked_sub ( 1 ) {
179
180
Some ( m) => Finite ( m) ,
180
- None => NegInfinity ,
181
+ None => bug ! ( ) ,
181
182
} ,
182
183
JustAfterMax => Finite ( u128:: MAX ) ,
183
184
x => x,
184
185
}
185
186
}
187
+ /// Note: this will not turn a finite value into an infinite one or vice-versa.
186
188
fn plus_one ( self ) -> Self {
187
189
match self {
188
190
Finite ( n) => match n. checked_add ( 1 ) {
@@ -195,18 +197,15 @@ impl MaybeInfiniteInt {
195
197
}
196
198
}
197
199
198
- /// An inclusive interval, used for precise integer exhaustiveness checking. `IntRange`s always
200
+ /// An exclusive interval, used for precise integer exhaustiveness checking. `IntRange`s always
199
201
/// store a contiguous range.
200
202
///
201
203
/// `IntRange` is never used to encode an empty range or a "range" that wraps around the (offset)
202
- /// space: i.e., `range.lo <= range.hi`.
203
- ///
204
- /// Note: the range can be `NegInfinity..=NegInfinity` or `PosInfinity..=PosInfinity` to represent
205
- /// the values before `isize::MIN` and after `isize::MAX`/`usize::MAX`.
204
+ /// space: i.e., `range.lo < range.hi`.
206
205
#[ derive( Clone , Copy , PartialEq , Eq ) ]
207
206
pub ( crate ) struct IntRange {
208
- lo : MaybeInfiniteInt ,
209
- hi : MaybeInfiniteInt ,
207
+ lo : MaybeInfiniteInt , // Must not be `PosInfinity`.
208
+ hi : MaybeInfiniteInt , // Must not be `NegInfinity`.
210
209
}
211
210
212
211
impl IntRange {
@@ -217,23 +216,23 @@ impl IntRange {
217
216
218
217
/// Best effort; will not know that e.g. `255u8..` is a singleton.
219
218
fn is_singleton ( & self ) -> bool {
220
- self . lo == self . hi && self . lo . is_finite ( )
219
+ self . lo . plus_one ( ) == self . hi
221
220
}
222
221
223
222
#[ inline]
224
223
fn from_bits < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , bits : u128 ) -> IntRange {
225
224
let x = MaybeInfiniteInt :: new_finite ( tcx, ty, bits) ;
226
- IntRange { lo : x, hi : x }
225
+ IntRange { lo : x, hi : x. plus_one ( ) }
227
226
}
228
227
229
228
#[ inline]
230
229
fn from_range ( lo : MaybeInfiniteInt , mut hi : MaybeInfiniteInt , end : RangeEnd ) -> IntRange {
231
- if end == RangeEnd :: Excluded {
232
- hi = hi. minus_one ( ) ;
230
+ if end == RangeEnd :: Included {
231
+ hi = hi. plus_one ( ) ;
233
232
}
234
- if lo > hi {
233
+ if lo >= hi {
235
234
// This should have been caught earlier by E0030.
236
- bug ! ( "malformed range pattern: {lo:?}..= {hi:?}" ) ;
235
+ bug ! ( "malformed range pattern: {lo:?}..{hi:?}" ) ;
237
236
}
238
237
IntRange { lo, hi }
239
238
}
@@ -243,7 +242,7 @@ impl IntRange {
243
242
}
244
243
245
244
fn intersection ( & self , other : & Self ) -> Option < Self > {
246
- if self . lo <= other. hi && other. lo <= self . hi {
245
+ if self . lo < other. hi && other. lo < self . hi {
247
246
Some ( IntRange { lo : max ( self . lo , other. lo ) , hi : min ( self . hi , other. hi ) } )
248
247
} else {
249
248
None
@@ -262,8 +261,7 @@ impl IntRange {
262
261
// `true` in the following cases:
263
262
// 1 ------- // 1 -------
264
263
// 2 -------- // 2 -------
265
- ( ( self . lo == other. hi && self . lo . is_finite ( ) )
266
- || ( self . hi == other. lo && self . hi . is_finite ( ) ) )
264
+ ( ( self . lo . plus_one ( ) == other. hi ) || ( other. lo . plus_one ( ) == self . hi ) )
267
265
&& !self . is_singleton ( )
268
266
&& !other. is_singleton ( )
269
267
}
@@ -295,38 +293,45 @@ impl IntRange {
295
293
/// ```
296
294
/// where each sequence of dashes is an output range, and dashes outside parentheses are marked
297
295
/// as `Presence::Missing`.
296
+ ///
297
+ /// ## `isize`/`usize`
298
+ ///
299
+ /// Whereas a wildcard of type `i32` stands for the range `i32::MIN..=i32::MAX`, a `usize`
300
+ /// wildcard stands for `0..PosInfinity` and a `isize` wildcard stands for
301
+ /// `NegInfinity..PosInfinity`. In other words, as far as `IntRange` is concerned, there are
302
+ /// values before `isize::MIN` and after `usize::MAX`/`isize::MAX`.
303
+ /// This is to avoid e.g. `0..(u32::MAX as usize)` from being exhaustive on one architecture and
304
+ /// not others. See discussions around the `precise_pointer_size_matching` feature for more
305
+ /// details.
306
+ ///
307
+ /// These infinities affect splitting subtly: it is possible to get `NegInfinity..0` and
308
+ /// `usize::MAX+1..PosInfinity` in the output. Diagnostics must be careful to handle these
309
+ /// fictitious ranges sensibly.
298
310
fn split (
299
311
& self ,
300
312
column_ranges : impl Iterator < Item = IntRange > ,
301
313
) -> impl Iterator < Item = ( Presence , IntRange ) > {
302
- // Make the range into an exclusive range.
303
- fn unpack_intrange ( range : IntRange ) -> [ MaybeInfiniteInt ; 2 ] {
304
- [ range. lo , range. hi . plus_one ( ) ]
305
- }
306
-
307
314
// The boundaries of ranges in `column_ranges` intersected with `self`.
308
315
// We do parenthesis matching for input ranges. A boundary counts as +1 if it starts
309
316
// a range and -1 if it ends it. When the count is > 0 between two boundaries, we
310
317
// are within an input range.
311
318
let mut boundaries: Vec < ( MaybeInfiniteInt , isize ) > = column_ranges
312
319
. filter_map ( |r| self . intersection ( & r) )
313
- . map ( unpack_intrange)
314
- . flat_map ( |[ lo, hi] | [ ( lo, 1 ) , ( hi, -1 ) ] )
320
+ . flat_map ( |r| [ ( r. lo , 1 ) , ( r. hi , -1 ) ] )
315
321
. collect ( ) ;
316
322
// We sort by boundary, and for each boundary we sort the "closing parentheses" first. The
317
323
// order of +1/-1 for a same boundary value is actually irrelevant, because we only look at
318
324
// the accumulated count between distinct boundary values.
319
325
boundaries. sort_unstable ( ) ;
320
326
321
- let [ self_start, self_end] = unpack_intrange ( * self ) ;
322
327
// Accumulate parenthesis counts.
323
328
let mut paren_counter = 0isize ;
324
329
// Gather pairs of adjacent boundaries.
325
- let mut prev_bdy = self_start ;
330
+ let mut prev_bdy = self . lo ;
326
331
boundaries
327
332
. into_iter ( )
328
333
// End with the end of the range. The count is ignored.
329
- . chain ( once ( ( self_end , 0 ) ) )
334
+ . chain ( once ( ( self . hi , 0 ) ) )
330
335
// List pairs of adjacent boundaries and the count between them.
331
336
. map ( move |( bdy, delta) | {
332
337
// `delta` affects the count as we cross `bdy`, so the relevant count between
@@ -342,21 +347,22 @@ impl IntRange {
342
347
. map ( move |( prev_bdy, paren_count, bdy) | {
343
348
use Presence :: * ;
344
349
let presence = if paren_count > 0 { Seen } else { Unseen } ;
345
- // Turn back into an inclusive range.
346
- let range = IntRange :: from_range ( prev_bdy, bdy, RangeEnd :: Excluded ) ;
350
+ let range = IntRange { lo : prev_bdy, hi : bdy } ;
347
351
( presence, range)
348
352
} )
349
353
}
350
354
351
- /// Whether the range denotes the values before `isize::MIN` or the values after
352
- /// `usize::MAX`/`isize::MAX`.
355
+ /// Whether the range denotes the fictitious values before `isize::MIN` or after
356
+ /// `usize::MAX`/`isize::MAX` (see doc of [`IntRange::split`] for why these exist) .
353
357
pub ( crate ) fn is_beyond_boundaries < ' tcx > ( & self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> bool {
354
- // First check if we are usize/isize to avoid unnecessary `to_diagnostic_pat_range_bdy`.
355
358
ty. is_ptr_sized_integral ( ) && !tcx. features ( ) . precise_pointer_size_matching && {
359
+ // The two invalid ranges are `NegInfinity..isize::MIN` (represented as
360
+ // `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `to_diagnostic_pat_range_bdy`
361
+ // converts `MAX+1` to `PosInfinity`, and we couldn't have `PosInfinity` in `self.lo`
362
+ // otherwise.
356
363
let lo = self . lo . to_diagnostic_pat_range_bdy ( ty, tcx) ;
357
- let hi = self . hi . to_diagnostic_pat_range_bdy ( ty, tcx) ;
358
364
matches ! ( lo, PatRangeBoundary :: PosInfinity )
359
- || matches ! ( hi, PatRangeBoundary :: NegInfinity )
365
+ || matches ! ( self . hi, MaybeInfiniteInt :: Finite ( 0 ) )
360
366
}
361
367
}
362
368
/// Only used for displaying the range.
@@ -368,28 +374,27 @@ impl IntRange {
368
374
let value = lo. as_finite ( ) . unwrap ( ) ;
369
375
PatKind :: Constant { value }
370
376
} else {
377
+ // We convert to an inclusive range for diagnostics.
378
+ let mut end = RangeEnd :: Included ;
371
379
let mut lo = self . lo . to_diagnostic_pat_range_bdy ( ty, tcx) ;
372
- let mut hi = self . hi . to_diagnostic_pat_range_bdy ( ty, tcx) ;
373
- let end = if hi. is_finite ( ) {
374
- RangeEnd :: Included
375
- } else {
376
- // `0..=` isn't a valid pattern.
377
- RangeEnd :: Excluded
378
- } ;
379
- if matches ! ( hi, PatRangeBoundary :: NegInfinity ) {
380
- // The range denotes the values before `isize::MIN`.
381
- let c = ty. numeric_min_val ( tcx) . unwrap ( ) ;
382
- let value = mir:: Const :: from_ty_const ( c, tcx) ;
383
- hi = PatRangeBoundary :: Finite ( value) ;
384
- }
385
380
if matches ! ( lo, PatRangeBoundary :: PosInfinity ) {
386
- // The range denotes the values after `usize::MAX`/`isize::MAX`.
387
- // We represent this as `usize::MAX..` which is slightly incorrect but probably
388
- // clear enough.
381
+ // The only reason to get `PosInfinity` here is the special case where
382
+ // `to_diagnostic_pat_range_bdy` found `{u,i}size::MAX+1`. So the range denotes the
383
+ // fictitious values after `{u,i}size::MAX` (see [`IntRange::split`] for why we do
384
+ // this). We show this to the user as `usize::MAX..` which is slightly incorrect but
385
+ // probably clear enough.
389
386
let c = ty. numeric_max_val ( tcx) . unwrap ( ) ;
390
387
let value = mir:: Const :: from_ty_const ( c, tcx) ;
391
388
lo = PatRangeBoundary :: Finite ( value) ;
392
389
}
390
+ let hi = if matches ! ( self . hi, MaybeInfiniteInt :: Finite ( 0 ) ) {
391
+ // The range encodes `..ty::MIN`, so we can't convert it to an inclusive range.
392
+ end = RangeEnd :: Excluded ;
393
+ self . hi
394
+ } else {
395
+ self . hi . minus_one ( )
396
+ } ;
397
+ let hi = hi. to_diagnostic_pat_range_bdy ( ty, tcx) ;
393
398
PatKind :: Range ( Box :: new ( PatRange { lo, hi, end, ty } ) )
394
399
} ;
395
400
@@ -449,7 +454,7 @@ impl fmt::Debug for IntRange {
449
454
if let Finite ( lo) = self . lo {
450
455
write ! ( f, "{lo}" ) ?;
451
456
}
452
- write ! ( f, "{}" , RangeEnd :: Included ) ?;
457
+ write ! ( f, "{}" , RangeEnd :: Excluded ) ?;
453
458
if let Finite ( hi) = self . hi {
454
459
write ! ( f, "{hi}" ) ?;
455
460
}
0 commit comments