@@ -103,7 +103,7 @@ enum MaybeInfiniteInt {
103103 NegInfinity ,
104104 /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`.
105105 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 .
107107 JustAfterMax ,
108108 PosInfinity ,
109109}
@@ -142,8 +142,11 @@ impl MaybeInfiniteInt {
142142 PatRangeBoundary :: PosInfinity => PosInfinity ,
143143 }
144144 }
145+
145146 /// 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`.
147150 fn to_diagnostic_pat_range_bdy < ' tcx > (
148151 self ,
149152 ty : Ty < ' tcx > ,
@@ -170,19 +173,18 @@ impl MaybeInfiniteInt {
170173 }
171174 }
172175
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.
176177 fn minus_one ( self ) -> Self {
177178 match self {
178179 Finite ( n) => match n. checked_sub ( 1 ) {
179180 Some ( m) => Finite ( m) ,
180- None => NegInfinity ,
181+ None => bug ! ( ) ,
181182 } ,
182183 JustAfterMax => Finite ( u128:: MAX ) ,
183184 x => x,
184185 }
185186 }
187+ /// Note: this will not turn a finite value into an infinite one or vice-versa.
186188 fn plus_one ( self ) -> Self {
187189 match self {
188190 Finite ( n) => match n. checked_add ( 1 ) {
@@ -195,18 +197,15 @@ impl MaybeInfiniteInt {
195197 }
196198}
197199
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
199201/// store a contiguous range.
200202///
201203/// `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`.
206205#[ derive( Clone , Copy , PartialEq , Eq ) ]
207206pub ( crate ) struct IntRange {
208- lo : MaybeInfiniteInt ,
209- hi : MaybeInfiniteInt ,
207+ lo : MaybeInfiniteInt , // Must not be `PosInfinity`.
208+ hi : MaybeInfiniteInt , // Must not be `NegInfinity`.
210209}
211210
212211impl IntRange {
@@ -217,23 +216,23 @@ impl IntRange {
217216
218217 /// Best effort; will not know that e.g. `255u8..` is a singleton.
219218 fn is_singleton ( & self ) -> bool {
220- self . lo == self . hi && self . lo . is_finite ( )
219+ self . lo . plus_one ( ) == self . hi
221220 }
222221
223222 #[ inline]
224223 fn from_bits < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , bits : u128 ) -> IntRange {
225224 let x = MaybeInfiniteInt :: new_finite ( tcx, ty, bits) ;
226- IntRange { lo : x, hi : x }
225+ IntRange { lo : x, hi : x. plus_one ( ) }
227226 }
228227
229228 #[ inline]
230229 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 ( ) ;
233232 }
234- if lo > hi {
233+ if lo >= hi {
235234 // This should have been caught earlier by E0030.
236- bug ! ( "malformed range pattern: {lo:?}..= {hi:?}" ) ;
235+ bug ! ( "malformed range pattern: {lo:?}..{hi:?}" ) ;
237236 }
238237 IntRange { lo, hi }
239238 }
@@ -243,7 +242,7 @@ impl IntRange {
243242 }
244243
245244 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 {
247246 Some ( IntRange { lo : max ( self . lo , other. lo ) , hi : min ( self . hi , other. hi ) } )
248247 } else {
249248 None
@@ -262,8 +261,7 @@ impl IntRange {
262261 // `true` in the following cases:
263262 // 1 ------- // 1 -------
264263 // 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 ) )
267265 && !self . is_singleton ( )
268266 && !other. is_singleton ( )
269267 }
@@ -295,38 +293,45 @@ impl IntRange {
295293 /// ```
296294 /// where each sequence of dashes is an output range, and dashes outside parentheses are marked
297295 /// 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.
298310 fn split (
299311 & self ,
300312 column_ranges : impl Iterator < Item = IntRange > ,
301313 ) -> 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-
307314 // The boundaries of ranges in `column_ranges` intersected with `self`.
308315 // We do parenthesis matching for input ranges. A boundary counts as +1 if it starts
309316 // a range and -1 if it ends it. When the count is > 0 between two boundaries, we
310317 // are within an input range.
311318 let mut boundaries: Vec < ( MaybeInfiniteInt , isize ) > = column_ranges
312319 . 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 ) ] )
315321 . collect ( ) ;
316322 // We sort by boundary, and for each boundary we sort the "closing parentheses" first. The
317323 // order of +1/-1 for a same boundary value is actually irrelevant, because we only look at
318324 // the accumulated count between distinct boundary values.
319325 boundaries. sort_unstable ( ) ;
320326
321- let [ self_start, self_end] = unpack_intrange ( * self ) ;
322327 // Accumulate parenthesis counts.
323328 let mut paren_counter = 0isize ;
324329 // Gather pairs of adjacent boundaries.
325- let mut prev_bdy = self_start ;
330+ let mut prev_bdy = self . lo ;
326331 boundaries
327332 . into_iter ( )
328333 // End with the end of the range. The count is ignored.
329- . chain ( once ( ( self_end , 0 ) ) )
334+ . chain ( once ( ( self . hi , 0 ) ) )
330335 // List pairs of adjacent boundaries and the count between them.
331336 . map ( move |( bdy, delta) | {
332337 // `delta` affects the count as we cross `bdy`, so the relevant count between
@@ -342,21 +347,22 @@ impl IntRange {
342347 . map ( move |( prev_bdy, paren_count, bdy) | {
343348 use Presence :: * ;
344349 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 } ;
347351 ( presence, range)
348352 } )
349353 }
350354
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) .
353357 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`.
355358 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.
356363 let lo = self . lo . to_diagnostic_pat_range_bdy ( ty, tcx) ;
357- let hi = self . hi . to_diagnostic_pat_range_bdy ( ty, tcx) ;
358364 matches ! ( lo, PatRangeBoundary :: PosInfinity )
359- || matches ! ( hi, PatRangeBoundary :: NegInfinity )
365+ || matches ! ( self . hi, MaybeInfiniteInt :: Finite ( 0 ) )
360366 }
361367 }
362368 /// Only used for displaying the range.
@@ -368,28 +374,27 @@ impl IntRange {
368374 let value = lo. as_finite ( ) . unwrap ( ) ;
369375 PatKind :: Constant { value }
370376 } else {
377+ // We convert to an inclusive range for diagnostics.
378+ let mut end = RangeEnd :: Included ;
371379 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- }
385380 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.
389386 let c = ty. numeric_max_val ( tcx) . unwrap ( ) ;
390387 let value = mir:: Const :: from_ty_const ( c, tcx) ;
391388 lo = PatRangeBoundary :: Finite ( value) ;
392389 }
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) ;
393398 PatKind :: Range ( Box :: new ( PatRange { lo, hi, end, ty } ) )
394399 } ;
395400
@@ -449,7 +454,7 @@ impl fmt::Debug for IntRange {
449454 if let Finite ( lo) = self . lo {
450455 write ! ( f, "{lo}" ) ?;
451456 }
452- write ! ( f, "{}" , RangeEnd :: Included ) ?;
457+ write ! ( f, "{}" , RangeEnd :: Excluded ) ?;
453458 if let Finite ( hi) = self . hi {
454459 write ! ( f, "{hi}" ) ?;
455460 }
0 commit comments