@@ -64,6 +64,7 @@ use rustc_span::{Span, DUMMY_SP};
6464use rustc_target:: abi:: { FieldIdx , Integer , VariantIdx , FIRST_VARIANT } ;
6565
6666use self :: Constructor :: * ;
67+ use self :: MaybeInfiniteInt :: * ;
6768use self :: SliceKind :: * ;
6869
6970use super :: usefulness:: { MatchCheckCtxt , PatCtxt } ;
@@ -93,20 +94,99 @@ enum Presence {
9394 Seen ,
9495}
9596
97+ /// A possibly infinite integer. Values are encoded such that the ordering on `u128` matches the
98+ /// natural order on the original type. For example, `-128i8` is encoded as `0` and `127i8` as
99+ /// `255`. See `signed_bias` for details.
100+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
101+ enum MaybeInfiniteInt {
102+ NegInfinity ,
103+ /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`.
104+ Finite ( u128 ) ,
105+ /// The integer after `u128::MAX`. Used when we switch to exclusive ranges in `IntRange::split`.
106+ JustAfterMax ,
107+ PosInfinity ,
108+ }
109+
110+ impl MaybeInfiniteInt {
111+ // The return value of `signed_bias` should be XORed with a value to encode/decode it.
112+ fn signed_bias ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > ) -> u128 {
113+ match * ty. kind ( ) {
114+ ty:: Int ( ity) => {
115+ let bits = Integer :: from_int_ty ( & tcx, ity) . size ( ) . bits ( ) as u128 ;
116+ 1u128 << ( bits - 1 )
117+ }
118+ _ => 0 ,
119+ }
120+ }
121+
122+ fn new_finite ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > , bits : u128 ) -> Self {
123+ let bias = Self :: signed_bias ( tcx, ty) ;
124+ // Perform a shift if the underlying types are signed, which makes the interval arithmetic
125+ // type-independent.
126+ let x = bits ^ bias;
127+ Finite ( x)
128+ }
129+ fn from_pat_range_bdy < ' tcx > (
130+ bdy : PatRangeBoundary < ' tcx > ,
131+ ty : Ty < ' tcx > ,
132+ tcx : TyCtxt < ' tcx > ,
133+ param_env : ty:: ParamEnv < ' tcx > ,
134+ ) -> Self {
135+ match bdy {
136+ PatRangeBoundary :: NegInfinity => NegInfinity ,
137+ PatRangeBoundary :: Finite ( value) => {
138+ let bits = value. eval_bits ( tcx, param_env) ;
139+ Self :: new_finite ( tcx, ty, bits)
140+ }
141+ PatRangeBoundary :: PosInfinity => PosInfinity ,
142+ }
143+ }
144+ fn to_pat_range_bdy < ' tcx > ( self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> PatRangeBoundary < ' tcx > {
145+ match self {
146+ NegInfinity => PatRangeBoundary :: NegInfinity ,
147+ Finite ( x) => {
148+ let bias = Self :: signed_bias ( tcx, ty) ;
149+ let bits = x ^ bias;
150+ let env = ty:: ParamEnv :: empty ( ) . and ( ty) ;
151+ let value = mir:: Const :: from_bits ( tcx, bits, env) ;
152+ PatRangeBoundary :: Finite ( value)
153+ }
154+ JustAfterMax | PosInfinity => PatRangeBoundary :: PosInfinity ,
155+ }
156+ }
157+
158+ fn minus_one ( self ) -> Self {
159+ match self {
160+ Finite ( n) => match n. checked_sub ( 1 ) {
161+ Some ( m) => Finite ( m) ,
162+ None => NegInfinity ,
163+ } ,
164+ JustAfterMax => Finite ( u128:: MAX ) ,
165+ x => x,
166+ }
167+ }
168+ fn plus_one ( self ) -> Self {
169+ match self {
170+ Finite ( n) => match n. checked_add ( 1 ) {
171+ Some ( m) => Finite ( m) ,
172+ None => JustAfterMax ,
173+ } ,
174+ x => x,
175+ }
176+ }
177+ }
178+
96179/// An inclusive interval, used for precise integer exhaustiveness checking.
97- /// `IntRange`s always store a contiguous range. This means that values are
98- /// encoded such that `0` encodes the minimum value for the integer,
99- /// regardless of the signedness.
100- /// For example, the pattern `-128..=127i8` is encoded as `0..=255`.
101- /// This makes comparisons and arithmetic on interval endpoints much more
102- /// straightforward. See `signed_bias` for details.
180+ /// `IntRange`s always store a contiguous range.
103181///
104182/// `IntRange` is never used to encode an empty range or a "range" that wraps
105183/// around the (offset) space: i.e., `range.lo <= range.hi`.
184+ ///
185+ /// The range can have open ends.
106186#[ derive( Clone , Copy , PartialEq , Eq ) ]
107187pub ( crate ) struct IntRange {
108- lo : u128 ,
109- hi : u128 ,
188+ lo : MaybeInfiniteInt , // Must not be `PosInfinity`.
189+ hi : MaybeInfiniteInt , // Must not be `NegInfinity`.
110190}
111191
112192impl IntRange {
@@ -115,51 +195,29 @@ impl IntRange {
115195 matches ! ( ty. kind( ) , ty:: Char | ty:: Int ( _) | ty:: Uint ( _) )
116196 }
117197
198+ /// Best effort; will not know that e.g. `255u8..` is a singleton.
118199 fn is_singleton ( & self ) -> bool {
119200 self . lo == self . hi
120201 }
121202
122203 #[ inline]
123204 fn from_bits < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , bits : u128 ) -> IntRange {
124- let bias = IntRange :: signed_bias ( tcx, ty) ;
125- // Perform a shift if the underlying types are signed, which makes the interval arithmetic
126- // type-independent.
127- let val = bits ^ bias;
128- IntRange { lo : val, hi : val }
205+ let x = MaybeInfiniteInt :: new_finite ( tcx, ty, bits) ;
206+ IntRange { lo : x, hi : x }
129207 }
130208
131209 #[ inline]
132- fn from_range < ' tcx > (
133- tcx : TyCtxt < ' tcx > ,
134- lo : u128 ,
135- hi : u128 ,
136- ty : Ty < ' tcx > ,
137- end : RangeEnd ,
138- ) -> IntRange {
139- // Perform a shift if the underlying types are signed, which makes the interval arithmetic
140- // type-independent.
141- let bias = IntRange :: signed_bias ( tcx, ty) ;
142- let ( lo, hi) = ( lo ^ bias, hi ^ bias) ;
143- let offset = ( end == RangeEnd :: Excluded ) as u128 ;
144- 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+ }
145214 if lo > hi {
146215 // This should have been caught earlier by E0030.
147- bug ! ( "malformed range pattern: {lo}..={hi}" ) ;
216+ bug ! ( "malformed range pattern: {lo:? }..={hi:? }" ) ;
148217 }
149218 IntRange { lo, hi }
150219 }
151220
152- // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
153- fn signed_bias ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > ) -> u128 {
154- match * ty. kind ( ) {
155- ty:: Int ( ity) => {
156- let bits = Integer :: from_int_ty ( & tcx, ity) . size ( ) . bits ( ) as u128 ;
157- 1u128 << ( bits - 1 )
158- }
159- _ => 0 ,
160- }
161- }
162-
163221 fn is_subrange ( & self , other : & Self ) -> bool {
164222 other. lo <= self . lo && self . hi <= other. hi
165223 }
@@ -220,29 +278,16 @@ impl IntRange {
220278 & self ,
221279 column_ranges : impl Iterator < Item = IntRange > ,
222280 ) -> impl Iterator < Item = ( Presence , IntRange ) > {
223- /// Represents a boundary between 2 integers. Because the intervals spanning boundaries must be
224- /// able to cover every integer, we need to be able to represent 2^128 + 1 such boundaries.
225- #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
226- enum IntBoundary {
227- JustBefore ( u128 ) ,
228- AfterMax ,
229- }
230-
231- fn unpack_intrange ( range : IntRange ) -> [ IntBoundary ; 2 ] {
232- use IntBoundary :: * ;
233- let lo = JustBefore ( range. lo ) ;
234- let hi = match range. hi . checked_add ( 1 ) {
235- Some ( m) => JustBefore ( m) ,
236- None => AfterMax ,
237- } ;
238- [ lo, hi]
281+ // Make the range into an exclusive range.
282+ fn unpack_intrange ( range : IntRange ) -> [ MaybeInfiniteInt ; 2 ] {
283+ [ range. lo , range. hi . plus_one ( ) ]
239284 }
240285
241286 // The boundaries of ranges in `column_ranges` intersected with `self`.
242287 // We do parenthesis matching for input ranges. A boundary counts as +1 if it starts
243288 // a range and -1 if it ends it. When the count is > 0 between two boundaries, we
244289 // are within an input range.
245- let mut boundaries: Vec < ( IntBoundary , isize ) > = column_ranges
290+ let mut boundaries: Vec < ( MaybeInfiniteInt , isize ) > = column_ranges
246291 . filter_map ( |r| self . intersection ( & r) )
247292 . map ( unpack_intrange)
248293 . flat_map ( |[ lo, hi] | [ ( lo, 1 ) , ( hi, -1 ) ] )
@@ -252,7 +297,7 @@ impl IntRange {
252297 // the accumulated count between distinct boundary values.
253298 boundaries. sort_unstable ( ) ;
254299
255- let [ self_start, self_end] = unpack_intrange ( self . clone ( ) ) ;
300+ let [ self_start, self_end] = unpack_intrange ( * self ) ;
256301 // Accumulate parenthesis counts.
257302 let mut paren_counter = 0isize ;
258303 // Gather pairs of adjacent boundaries.
@@ -274,36 +319,26 @@ impl IntRange {
274319 . filter ( |& ( prev_bdy, _, bdy) | prev_bdy != bdy)
275320 // Convert back to ranges.
276321 . map ( move |( prev_bdy, paren_count, bdy) | {
277- use IntBoundary :: * ;
278322 use Presence :: * ;
279323 let presence = if paren_count > 0 { Seen } else { Unseen } ;
280- let ( lo, hi) = match ( prev_bdy, bdy) {
281- ( JustBefore ( n) , JustBefore ( m) ) if n < m => ( n, m - 1 ) ,
282- ( JustBefore ( n) , AfterMax ) => ( n, u128:: MAX ) ,
283- _ => unreachable ! ( ) , // Ruled out by the sorting and filtering we did
284- } ;
285- ( presence, IntRange { lo, hi } )
324+ // Turn back into an inclusive range.
325+ let range = IntRange :: from_range ( prev_bdy, bdy, RangeEnd :: Excluded ) ;
326+ ( presence, range)
286327 } )
287328 }
288329
289330 /// Only used for displaying the range.
290- fn to_pat < ' tcx > ( & self , tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Pat < ' tcx > {
291- let bias = IntRange :: signed_bias ( tcx, ty) ;
292- let ( lo_bits, hi_bits) = ( self . lo ^ bias, self . hi ^ bias) ;
293-
294- let env = ty:: ParamEnv :: empty ( ) . and ( ty) ;
295- let lo_const = mir:: Const :: from_bits ( tcx, lo_bits, env) ;
296- let hi_const = mir:: Const :: from_bits ( tcx, hi_bits, env) ;
297-
298- let kind = if lo_bits == hi_bits {
299- PatKind :: Constant { value : lo_const }
331+ fn to_pat < ' tcx > ( & self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Pat < ' tcx > {
332+ let lo = self . lo . to_pat_range_bdy ( ty, tcx) ;
333+ let hi = self . hi . to_pat_range_bdy ( ty, tcx) ;
334+
335+ let kind = if self . is_singleton ( ) {
336+ let value = lo. as_finite ( ) . unwrap ( ) ;
337+ PatKind :: Constant { value }
338+ } else if matches ! ( ( self . lo, self . hi) , ( NegInfinity , PosInfinity ) ) {
339+ PatKind :: Wild
300340 } else {
301- PatKind :: Range ( Box :: new ( PatRange {
302- lo : PatRangeBoundary :: Finite ( lo_const) ,
303- hi : PatRangeBoundary :: Finite ( hi_const) ,
304- end : RangeEnd :: Included ,
305- ty,
306- } ) )
341+ PatKind :: Range ( Box :: new ( PatRange { lo, hi, end : RangeEnd :: Included , ty } ) )
307342 } ;
308343
309344 Pat { ty, span : DUMMY_SP , kind }
@@ -339,7 +374,7 @@ impl IntRange {
339374 . filter_map ( |pat| Some ( ( pat. ctor ( ) . as_int_range ( ) ?, pat. span ( ) ) ) )
340375 . filter ( |( range, _) | self . suspicious_intersection ( range) )
341376 . map ( |( range, span) | Overlap {
342- range : self . intersection ( & range) . unwrap ( ) . to_pat ( pcx. cx . tcx , pcx. ty ) ,
377+ range : self . intersection ( & range) . unwrap ( ) . to_pat ( pcx. ty , pcx. cx . tcx ) ,
343378 span,
344379 } )
345380 . collect ( ) ;
@@ -359,10 +394,14 @@ impl IntRange {
359394/// first.
360395impl fmt:: Debug for IntRange {
361396 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
362- let ( lo, hi) = ( self . lo , self . hi ) ;
363- write ! ( f, "{lo}" ) ?;
397+ if let Finite ( lo) = self . lo {
398+ write ! ( f, "{lo}" ) ?;
399+ }
364400 write ! ( f, "{}" , RangeEnd :: Included ) ?;
365- write ! ( f, "{hi}" )
401+ if let Finite ( hi) = self . hi {
402+ write ! ( f, "{hi}" ) ?;
403+ }
404+ Ok ( ( ) )
366405 }
367406}
368407
@@ -919,8 +958,13 @@ struct SplitConstructorSet<'tcx> {
919958impl ConstructorSet {
920959 #[ instrument( level = "debug" , skip( cx) , ret) ]
921960 pub ( super ) fn for_ty < ' p , ' tcx > ( cx : & MatchCheckCtxt < ' p , ' tcx > , ty : Ty < ' tcx > ) -> Self {
922- let make_range =
923- |start, end| IntRange :: from_range ( cx. tcx , start, end, ty, RangeEnd :: Included ) ;
961+ let make_range = |start, end| {
962+ IntRange :: from_range (
963+ MaybeInfiniteInt :: new_finite ( cx. tcx , ty, start) ,
964+ MaybeInfiniteInt :: new_finite ( cx. tcx , ty, end) ,
965+ RangeEnd :: Included ,
966+ )
967+ } ;
924968 // This determines the set of all possible constructors for the type `ty`. For numbers,
925969 // arrays and slices we use ranges and variable-length slices when appropriate.
926970 //
@@ -1504,24 +1548,33 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
15041548 }
15051549 }
15061550 PatKind :: Range ( box PatRange { lo, hi, end, .. } ) => {
1507- use rustc_apfloat:: Float ;
15081551 let ty = pat. ty ;
1509- // FIXME: handle half-open ranges
1510- let lo = lo. eval_bits ( ty, cx. tcx , cx. param_env ) ;
1511- let hi = hi. eval_bits ( ty, cx. tcx , cx. param_env ) ;
15121552 ctor = match ty. kind ( ) {
15131553 ty:: Char | ty:: Int ( _) | ty:: Uint ( _) => {
1514- IntRange ( IntRange :: from_range ( cx. tcx , lo, hi, ty, * end) )
1515- }
1516- ty:: Float ( ty:: FloatTy :: F32 ) => {
1517- let lo = rustc_apfloat:: ieee:: Single :: from_bits ( lo) ;
1518- let hi = rustc_apfloat:: ieee:: Single :: from_bits ( hi) ;
1519- F32Range ( lo, hi, * end)
1554+ let lo =
1555+ MaybeInfiniteInt :: from_pat_range_bdy ( * lo, ty, cx. tcx , cx. param_env ) ;
1556+ let hi =
1557+ MaybeInfiniteInt :: from_pat_range_bdy ( * hi, ty, cx. tcx , cx. param_env ) ;
1558+ IntRange ( IntRange :: from_range ( lo, hi, * end) )
15201559 }
1521- ty:: Float ( ty:: FloatTy :: F64 ) => {
1522- let lo = rustc_apfloat:: ieee:: Double :: from_bits ( lo) ;
1523- let hi = rustc_apfloat:: ieee:: Double :: from_bits ( hi) ;
1524- F64Range ( lo, hi, * end)
1560+ ty:: Float ( fty) => {
1561+ use rustc_apfloat:: Float ;
1562+ let lo = lo. as_finite ( ) . map ( |c| c. eval_bits ( cx. tcx , cx. param_env ) ) ;
1563+ let hi = hi. as_finite ( ) . map ( |c| c. eval_bits ( cx. tcx , cx. param_env ) ) ;
1564+ match fty {
1565+ ty:: FloatTy :: F32 => {
1566+ use rustc_apfloat:: ieee:: Single ;
1567+ let lo = lo. map ( Single :: from_bits) . unwrap_or ( -Single :: INFINITY ) ;
1568+ let hi = hi. map ( Single :: from_bits) . unwrap_or ( Single :: INFINITY ) ;
1569+ F32Range ( lo, hi, * end)
1570+ }
1571+ ty:: FloatTy :: F64 => {
1572+ use rustc_apfloat:: ieee:: Double ;
1573+ let lo = lo. map ( Double :: from_bits) . unwrap_or ( -Double :: INFINITY ) ;
1574+ let hi = hi. map ( Double :: from_bits) . unwrap_or ( Double :: INFINITY ) ;
1575+ F64Range ( lo, hi, * end)
1576+ }
1577+ }
15251578 }
15261579 _ => bug ! ( "invalid type for range pattern: {}" , ty) ,
15271580 } ;
@@ -1628,7 +1681,7 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
16281681 }
16291682 }
16301683 Bool ( b) => PatKind :: Constant { value : mir:: Const :: from_bool ( cx. tcx , * b) } ,
1631- IntRange ( range) => return range. to_pat ( cx . tcx , self . ty ) ,
1684+ IntRange ( range) => return range. to_pat ( self . ty , cx . tcx ) ,
16321685 & Str ( value) => PatKind :: Constant { value } ,
16331686 Wildcard | NonExhaustive | Hidden => PatKind :: Wild ,
16341687 Missing { .. } => bug ! (
0 commit comments