@@ -101,7 +101,7 @@ pub(crate) enum MaybeInfiniteInt {
101101 NegInfinity ,
102102 /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`.
103103 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 .
105105 JustAfterMax ,
106106 PosInfinity ,
107107}
@@ -140,8 +140,11 @@ impl MaybeInfiniteInt {
140140 PatRangeBoundary :: PosInfinity => PosInfinity ,
141141 }
142142 }
143+
143144 /// 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`.
145148 fn to_diagnostic_pat_range_bdy < ' tcx > (
146149 self ,
147150 ty : Ty < ' tcx > ,
@@ -168,20 +171,19 @@ impl MaybeInfiniteInt {
168171 }
169172 }
170173
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 {
175176 match self {
176177 Finite ( n) => match n. checked_sub ( 1 ) {
177178 Some ( m) => Finite ( m) ,
178- None => NegInfinity ,
179+ None => bug ! ( ) ,
179180 } ,
180181 JustAfterMax => Finite ( u128:: MAX ) ,
181182 x => x,
182183 }
183184 }
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 {
185187 match self {
186188 Finite ( n) => match n. checked_add ( 1 ) {
187189 Some ( m) => Finite ( m) ,
@@ -193,18 +195,15 @@ impl MaybeInfiniteInt {
193195 }
194196}
195197
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
197199/// store a contiguous range.
198200///
199201/// `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`.
204203#[ derive( Clone , Copy , PartialEq , Eq ) ]
205204pub ( 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`.
208207}
209208
210209impl IntRange {
@@ -215,23 +214,25 @@ impl IntRange {
215214
216215 /// Best effort; will not know that e.g. `255u8..` is a singleton.
217216 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
219220 }
220221
221222 #[ inline]
222223 fn from_bits < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , bits : u128 ) -> IntRange {
223224 let x = MaybeInfiniteInt :: new_finite ( tcx, ty, bits) ;
224- IntRange { lo : x, hi : x }
225+ IntRange { lo : x, hi : x. plus_one ( ) }
225226 }
226227
227228 #[ inline]
228229 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 ( ) ;
231232 }
232- if lo > hi {
233+ if lo >= hi {
233234 // This should have been caught earlier by E0030.
234- bug ! ( "malformed range pattern: {lo:?}..= {hi:?}" ) ;
235+ bug ! ( "malformed range pattern: {lo:?}..{hi:?}" ) ;
235236 }
236237 IntRange { lo, hi }
237238 }
@@ -241,7 +242,7 @@ impl IntRange {
241242 }
242243
243244 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 {
245246 Some ( IntRange { lo : max ( self . lo , other. lo ) , hi : min ( self . hi , other. hi ) } )
246247 } else {
247248 None
@@ -275,38 +276,45 @@ impl IntRange {
275276 /// ```
276277 /// where each sequence of dashes is an output range, and dashes outside parentheses are marked
277278 /// 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.
278293 fn split (
279294 & self ,
280295 column_ranges : impl Iterator < Item = IntRange > ,
281296 ) -> 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-
287297 // The boundaries of ranges in `column_ranges` intersected with `self`.
288298 // We do parenthesis matching for input ranges. A boundary counts as +1 if it starts
289299 // a range and -1 if it ends it. When the count is > 0 between two boundaries, we
290300 // are within an input range.
291301 let mut boundaries: Vec < ( MaybeInfiniteInt , isize ) > = column_ranges
292302 . 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 ) ] )
295304 . collect ( ) ;
296305 // We sort by boundary, and for each boundary we sort the "closing parentheses" first. The
297306 // order of +1/-1 for a same boundary value is actually irrelevant, because we only look at
298307 // the accumulated count between distinct boundary values.
299308 boundaries. sort_unstable ( ) ;
300309
301- let [ self_start, self_end] = unpack_intrange ( * self ) ;
302310 // Accumulate parenthesis counts.
303311 let mut paren_counter = 0isize ;
304312 // Gather pairs of adjacent boundaries.
305- let mut prev_bdy = self_start ;
313+ let mut prev_bdy = self . lo ;
306314 boundaries
307315 . into_iter ( )
308316 // End with the end of the range. The count is ignored.
309- . chain ( once ( ( self_end , 0 ) ) )
317+ . chain ( once ( ( self . hi , 0 ) ) )
310318 // List pairs of adjacent boundaries and the count between them.
311319 . map ( move |( bdy, delta) | {
312320 // `delta` affects the count as we cross `bdy`, so the relevant count between
@@ -322,21 +330,22 @@ impl IntRange {
322330 . map ( move |( prev_bdy, paren_count, bdy) | {
323331 use Presence :: * ;
324332 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 } ;
327334 ( presence, range)
328335 } )
329336 }
330337
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) .
333340 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`.
335341 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.
336346 let lo = self . lo . to_diagnostic_pat_range_bdy ( ty, tcx) ;
337- let hi = self . hi . to_diagnostic_pat_range_bdy ( ty, tcx) ;
338347 matches ! ( lo, PatRangeBoundary :: PosInfinity )
339- || matches ! ( hi, PatRangeBoundary :: NegInfinity )
348+ || matches ! ( self . hi, MaybeInfiniteInt :: Finite ( 0 ) )
340349 }
341350 }
342351 /// Only used for displaying the range.
@@ -348,28 +357,27 @@ impl IntRange {
348357 let value = lo. as_finite ( ) . unwrap ( ) ;
349358 PatKind :: Constant { value }
350359 } else {
360+ // We convert to an inclusive range for diagnostics.
361+ let mut end = RangeEnd :: Included ;
351362 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- }
365363 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.
369369 let c = ty. numeric_max_val ( tcx) . unwrap ( ) ;
370370 let value = mir:: Const :: from_ty_const ( c, tcx) ;
371371 lo = PatRangeBoundary :: Finite ( value) ;
372372 }
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) ;
373381 PatKind :: Range ( Box :: new ( PatRange { lo, hi, end, ty } ) )
374382 } ;
375383
@@ -384,7 +392,7 @@ impl fmt::Debug for IntRange {
384392 if let Finite ( lo) = self . lo {
385393 write ! ( f, "{lo}" ) ?;
386394 }
387- write ! ( f, "{}" , RangeEnd :: Included ) ?;
395+ write ! ( f, "{}" , RangeEnd :: Excluded ) ?;
388396 if let Finite ( hi) = self . hi {
389397 write ! ( f, "{hi}" ) ?;
390398 }
0 commit comments