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