@@ -63,6 +63,7 @@ use rustc_span::{Span, DUMMY_SP};
6363use rustc_target:: abi:: { FieldIdx , Integer , VariantIdx , FIRST_VARIANT } ;
6464
6565use self :: Constructor :: * ;
66+ use self :: MaybeInfiniteInt :: * ;
6667use self :: SliceKind :: * ;
6768
6869use super :: usefulness:: { MatchCheckCtxt , PatCtxt } ;
@@ -91,20 +92,99 @@ enum Presence {
9192 Seen ,
9293}
9394
95+ /// A possibly infinite integer. Values are encoded such that the ordering on `u128` matches the
96+ /// natural order on the original type. For example, `-128i8` is encoded as `0` and `127i8` as
97+ /// `255`. See `signed_bias` for details.
98+ #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
99+ pub ( crate ) enum MaybeInfiniteInt {
100+ NegInfinity ,
101+ /// Encoded value. DO NOT CONSTRUCT BY HAND; use `new_finite`.
102+ Finite ( u128 ) ,
103+ /// The integer after `u128::MAX`. Used when we switch to exclusive ranges in `IntRange::split`.
104+ JustAfterMax ,
105+ PosInfinity ,
106+ }
107+
108+ impl MaybeInfiniteInt {
109+ // The return value of `signed_bias` should be XORed with a value to encode/decode it.
110+ fn signed_bias ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > ) -> u128 {
111+ match * ty. kind ( ) {
112+ ty:: Int ( ity) => {
113+ let bits = Integer :: from_int_ty ( & tcx, ity) . size ( ) . bits ( ) as u128 ;
114+ 1u128 << ( bits - 1 )
115+ }
116+ _ => 0 ,
117+ }
118+ }
119+
120+ fn new_finite ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > , bits : u128 ) -> Self {
121+ let bias = Self :: signed_bias ( tcx, ty) ;
122+ // Perform a shift if the underlying types are signed, which makes the interval arithmetic
123+ // type-independent.
124+ let x = bits ^ bias;
125+ Finite ( x)
126+ }
127+ fn from_pat_range_bdy < ' tcx > (
128+ bdy : PatRangeBoundary < ' tcx > ,
129+ ty : Ty < ' tcx > ,
130+ tcx : TyCtxt < ' tcx > ,
131+ param_env : ty:: ParamEnv < ' tcx > ,
132+ ) -> Self {
133+ match bdy {
134+ PatRangeBoundary :: NegInfinity => NegInfinity ,
135+ PatRangeBoundary :: Finite ( value) => {
136+ let bits = value. eval_bits ( tcx, param_env) ;
137+ Self :: new_finite ( tcx, ty, bits)
138+ }
139+ PatRangeBoundary :: PosInfinity => PosInfinity ,
140+ }
141+ }
142+ fn to_pat_range_bdy < ' tcx > ( self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> PatRangeBoundary < ' tcx > {
143+ match self {
144+ NegInfinity => PatRangeBoundary :: NegInfinity ,
145+ Finite ( x) => {
146+ let bias = Self :: signed_bias ( tcx, ty) ;
147+ let bits = x ^ bias;
148+ let env = ty:: ParamEnv :: empty ( ) . and ( ty) ;
149+ let value = mir:: Const :: from_bits ( tcx, bits, env) ;
150+ PatRangeBoundary :: Finite ( value)
151+ }
152+ JustAfterMax | PosInfinity => PatRangeBoundary :: PosInfinity ,
153+ }
154+ }
155+
156+ fn minus_one ( self ) -> Self {
157+ match self {
158+ Finite ( n) => match n. checked_sub ( 1 ) {
159+ Some ( m) => Finite ( m) ,
160+ None => NegInfinity ,
161+ } ,
162+ JustAfterMax => Finite ( u128:: MAX ) ,
163+ x => x,
164+ }
165+ }
166+ fn plus_one ( self ) -> Self {
167+ match self {
168+ Finite ( n) => match n. checked_add ( 1 ) {
169+ Some ( m) => Finite ( m) ,
170+ None => JustAfterMax ,
171+ } ,
172+ x => x,
173+ }
174+ }
175+ }
176+
94177/// An inclusive interval, used for precise integer exhaustiveness checking.
95- /// `IntRange`s always store a contiguous range. This means that values are
96- /// encoded such that `0` encodes the minimum value for the integer,
97- /// regardless of the signedness.
98- /// For example, the pattern `-128..=127i8` is encoded as `0..=255`.
99- /// This makes comparisons and arithmetic on interval endpoints much more
100- /// straightforward. See `signed_bias` for details.
178+ /// `IntRange`s always store a contiguous range.
101179///
102180/// `IntRange` is never used to encode an empty range or a "range" that wraps
103181/// around the (offset) space: i.e., `range.lo <= range.hi`.
182+ ///
183+ /// The range can have open ends.
104184#[ derive( Clone , Copy , PartialEq , Eq ) ]
105185pub ( crate ) struct IntRange {
106- pub ( crate ) lo : u128 ,
107- pub ( crate ) hi : u128 ,
186+ pub ( crate ) lo : MaybeInfiniteInt , // Must not be `PosInfinity`.
187+ pub ( crate ) hi : MaybeInfiniteInt , // Must not be `NegInfinity`.
108188}
109189
110190impl IntRange {
@@ -113,51 +193,31 @@ impl IntRange {
113193 matches ! ( ty. kind( ) , ty:: Char | ty:: Int ( _) | ty:: Uint ( _) )
114194 }
115195
196+ /// Best effort; will not know that e.g. `255u8..` is a singleton.
116197 pub ( super ) fn is_singleton ( & self ) -> bool {
198+ // Since `lo` and `hi` can't be the same `Infinity`, this correctly only detects a
199+ // `Finite(x)` singleton.
117200 self . lo == self . hi
118201 }
119202
120203 #[ inline]
121204 fn from_bits < ' tcx > ( tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > , bits : u128 ) -> IntRange {
122- let bias = IntRange :: signed_bias ( tcx, ty) ;
123- // Perform a shift if the underlying types are signed, which makes the interval arithmetic
124- // type-independent.
125- let val = bits ^ bias;
126- IntRange { lo : val, hi : val }
205+ let x = MaybeInfiniteInt :: new_finite ( tcx, ty, bits) ;
206+ IntRange { lo : x, hi : x }
127207 }
128208
129209 #[ inline]
130- fn from_range < ' tcx > (
131- tcx : TyCtxt < ' tcx > ,
132- lo : u128 ,
133- hi : u128 ,
134- ty : Ty < ' tcx > ,
135- end : RangeEnd ,
136- ) -> IntRange {
137- // Perform a shift if the underlying types are signed, which makes the interval arithmetic
138- // type-independent.
139- let bias = IntRange :: signed_bias ( tcx, ty) ;
140- let ( lo, hi) = ( lo ^ bias, hi ^ bias) ;
141- let offset = ( end == RangeEnd :: Excluded ) as u128 ;
142- 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+ }
143214 if lo > hi {
144215 // This should have been caught earlier by E0030.
145- bug ! ( "malformed range pattern: {lo}..={hi}" ) ;
216+ bug ! ( "malformed range pattern: {lo:? }..={hi:? }" ) ;
146217 }
147218 IntRange { lo, hi }
148219 }
149220
150- // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
151- fn signed_bias ( tcx : TyCtxt < ' _ > , ty : Ty < ' _ > ) -> u128 {
152- match * ty. kind ( ) {
153- ty:: Int ( ity) => {
154- let bits = Integer :: from_int_ty ( & tcx, ity) . size ( ) . bits ( ) as u128 ;
155- 1u128 << ( bits - 1 )
156- }
157- _ => 0 ,
158- }
159- }
160-
161221 fn is_subrange ( & self , other : & Self ) -> bool {
162222 other. lo <= self . lo && self . hi <= other. hi
163223 }
@@ -201,29 +261,16 @@ impl IntRange {
201261 & self ,
202262 column_ranges : impl Iterator < Item = IntRange > ,
203263 ) -> impl Iterator < Item = ( Presence , IntRange ) > {
204- /// Represents a boundary between 2 integers. Because the intervals spanning boundaries must be
205- /// able to cover every integer, we need to be able to represent 2^128 + 1 such boundaries.
206- #[ derive( Debug , Clone , Copy , PartialEq , Eq , PartialOrd , Ord ) ]
207- enum IntBoundary {
208- JustBefore ( u128 ) ,
209- AfterMax ,
210- }
211-
212- fn unpack_intrange ( range : IntRange ) -> [ IntBoundary ; 2 ] {
213- use IntBoundary :: * ;
214- let lo = JustBefore ( range. lo ) ;
215- let hi = match range. hi . checked_add ( 1 ) {
216- Some ( m) => JustBefore ( m) ,
217- None => AfterMax ,
218- } ;
219- [ lo, hi]
264+ // Make the range into an exclusive range.
265+ fn unpack_intrange ( range : IntRange ) -> [ MaybeInfiniteInt ; 2 ] {
266+ [ range. lo , range. hi . plus_one ( ) ]
220267 }
221268
222269 // The boundaries of ranges in `column_ranges` intersected with `self`.
223270 // We do parenthesis matching for input ranges. A boundary counts as +1 if it starts
224271 // a range and -1 if it ends it. When the count is > 0 between two boundaries, we
225272 // are within an input range.
226- let mut boundaries: Vec < ( IntBoundary , isize ) > = column_ranges
273+ let mut boundaries: Vec < ( MaybeInfiniteInt , isize ) > = column_ranges
227274 . filter_map ( |r| self . intersection ( & r) )
228275 . map ( unpack_intrange)
229276 . flat_map ( |[ lo, hi] | [ ( lo, 1 ) , ( hi, -1 ) ] )
@@ -233,7 +280,7 @@ impl IntRange {
233280 // the accumulated count between distinct boundary values.
234281 boundaries. sort_unstable ( ) ;
235282
236- let [ self_start, self_end] = unpack_intrange ( self . clone ( ) ) ;
283+ let [ self_start, self_end] = unpack_intrange ( * self ) ;
237284 // Accumulate parenthesis counts.
238285 let mut paren_counter = 0isize ;
239286 // Gather pairs of adjacent boundaries.
@@ -255,36 +302,26 @@ impl IntRange {
255302 . filter ( |& ( prev_bdy, _, bdy) | prev_bdy != bdy)
256303 // Convert back to ranges.
257304 . map ( move |( prev_bdy, paren_count, bdy) | {
258- use IntBoundary :: * ;
259305 use Presence :: * ;
260306 let presence = if paren_count > 0 { Seen } else { Unseen } ;
261- let ( lo, hi) = match ( prev_bdy, bdy) {
262- ( JustBefore ( n) , JustBefore ( m) ) if n < m => ( n, m - 1 ) ,
263- ( JustBefore ( n) , AfterMax ) => ( n, u128:: MAX ) ,
264- _ => unreachable ! ( ) , // Ruled out by the sorting and filtering we did
265- } ;
266- ( presence, IntRange { lo, hi } )
307+ // Turn back into an inclusive range.
308+ let range = IntRange :: from_range ( prev_bdy, bdy, RangeEnd :: Excluded ) ;
309+ ( presence, range)
267310 } )
268311 }
269312
270313 /// Only used for displaying the range.
271- pub ( super ) fn to_pat < ' tcx > ( & self , tcx : TyCtxt < ' tcx > , ty : Ty < ' tcx > ) -> Pat < ' tcx > {
272- let bias = IntRange :: signed_bias ( tcx, ty) ;
273- let ( lo_bits, hi_bits) = ( self . lo ^ bias, self . hi ^ bias) ;
274-
275- let env = ty:: ParamEnv :: empty ( ) . and ( ty) ;
276- let lo_const = mir:: Const :: from_bits ( tcx, lo_bits, env) ;
277- let hi_const = mir:: Const :: from_bits ( tcx, hi_bits, env) ;
278-
279- let kind = if lo_bits == hi_bits {
280- PatKind :: Constant { value : lo_const }
314+ pub ( super ) fn to_pat < ' tcx > ( & self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Pat < ' tcx > {
315+ let lo = self . lo . to_pat_range_bdy ( ty, tcx) ;
316+ let hi = self . hi . to_pat_range_bdy ( ty, tcx) ;
317+
318+ let kind = if self . is_singleton ( ) {
319+ let value = lo. as_finite ( ) . unwrap ( ) ;
320+ PatKind :: Constant { value }
321+ } else if matches ! ( ( self . lo, self . hi) , ( NegInfinity , PosInfinity ) ) {
322+ PatKind :: Wild
281323 } else {
282- PatKind :: Range ( Box :: new ( PatRange {
283- lo : PatRangeBoundary :: Finite ( lo_const) ,
284- hi : PatRangeBoundary :: Finite ( hi_const) ,
285- end : RangeEnd :: Included ,
286- ty,
287- } ) )
324+ PatKind :: Range ( Box :: new ( PatRange { lo, hi, end : RangeEnd :: Included , ty } ) )
288325 } ;
289326
290327 Pat { ty, span : DUMMY_SP , kind }
@@ -295,10 +332,14 @@ impl IntRange {
295332/// first.
296333impl fmt:: Debug for IntRange {
297334 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
298- let ( lo, hi) = ( self . lo , self . hi ) ;
299- write ! ( f, "{lo}" ) ?;
335+ if let Finite ( lo) = self . lo {
336+ write ! ( f, "{lo}" ) ?;
337+ }
300338 write ! ( f, "{}" , RangeEnd :: Included ) ?;
301- write ! ( f, "{hi}" )
339+ if let Finite ( hi) = self . hi {
340+ write ! ( f, "{hi}" ) ?;
341+ }
342+ Ok ( ( ) )
302343 }
303344}
304345
@@ -840,8 +881,13 @@ pub(super) struct SplitConstructorSet<'tcx> {
840881impl ConstructorSet {
841882 #[ instrument( level = "debug" , skip( cx) , ret) ]
842883 pub ( super ) fn for_ty < ' p , ' tcx > ( cx : & MatchCheckCtxt < ' p , ' tcx > , ty : Ty < ' tcx > ) -> Self {
843- let make_range =
844- |start, end| IntRange :: from_range ( cx. tcx , start, end, ty, RangeEnd :: Included ) ;
884+ let make_range = |start, end| {
885+ IntRange :: from_range (
886+ MaybeInfiniteInt :: new_finite ( cx. tcx , ty, start) ,
887+ MaybeInfiniteInt :: new_finite ( cx. tcx , ty, end) ,
888+ RangeEnd :: Included ,
889+ )
890+ } ;
845891 // This determines the set of all possible constructors for the type `ty`. For numbers,
846892 // arrays and slices we use ranges and variable-length slices when appropriate.
847893 //
@@ -1419,24 +1465,33 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
14191465 }
14201466 }
14211467 PatKind :: Range ( box PatRange { lo, hi, end, .. } ) => {
1422- use rustc_apfloat:: Float ;
14231468 let ty = pat. ty ;
1424- // FIXME: handle half-open ranges
1425- let lo = lo. eval_bits ( ty, cx. tcx , cx. param_env ) ;
1426- let hi = hi. eval_bits ( ty, cx. tcx , cx. param_env ) ;
14271469 ctor = match ty. kind ( ) {
14281470 ty:: Char | ty:: Int ( _) | ty:: Uint ( _) => {
1429- IntRange ( IntRange :: from_range ( cx. tcx , lo, hi, ty, * end) )
1430- }
1431- ty:: Float ( ty:: FloatTy :: F32 ) => {
1432- let lo = rustc_apfloat:: ieee:: Single :: from_bits ( lo) ;
1433- let hi = rustc_apfloat:: ieee:: Single :: from_bits ( hi) ;
1434- F32Range ( lo, hi, * end)
1471+ let lo =
1472+ MaybeInfiniteInt :: from_pat_range_bdy ( * lo, ty, cx. tcx , cx. param_env ) ;
1473+ let hi =
1474+ MaybeInfiniteInt :: from_pat_range_bdy ( * hi, ty, cx. tcx , cx. param_env ) ;
1475+ IntRange ( IntRange :: from_range ( lo, hi, * end) )
14351476 }
1436- ty:: Float ( ty:: FloatTy :: F64 ) => {
1437- let lo = rustc_apfloat:: ieee:: Double :: from_bits ( lo) ;
1438- let hi = rustc_apfloat:: ieee:: Double :: from_bits ( hi) ;
1439- F64Range ( lo, hi, * end)
1477+ ty:: Float ( fty) => {
1478+ use rustc_apfloat:: Float ;
1479+ let lo = lo. as_finite ( ) . map ( |c| c. eval_bits ( cx. tcx , cx. param_env ) ) ;
1480+ let hi = hi. as_finite ( ) . map ( |c| c. eval_bits ( cx. tcx , cx. param_env ) ) ;
1481+ match fty {
1482+ ty:: FloatTy :: F32 => {
1483+ use rustc_apfloat:: ieee:: Single ;
1484+ let lo = lo. map ( Single :: from_bits) . unwrap_or ( -Single :: INFINITY ) ;
1485+ let hi = hi. map ( Single :: from_bits) . unwrap_or ( Single :: INFINITY ) ;
1486+ F32Range ( lo, hi, * end)
1487+ }
1488+ ty:: FloatTy :: F64 => {
1489+ use rustc_apfloat:: ieee:: Double ;
1490+ let lo = lo. map ( Double :: from_bits) . unwrap_or ( -Double :: INFINITY ) ;
1491+ let hi = hi. map ( Double :: from_bits) . unwrap_or ( Double :: INFINITY ) ;
1492+ F64Range ( lo, hi, * end)
1493+ }
1494+ }
14401495 }
14411496 _ => bug ! ( "invalid type for range pattern: {}" , ty) ,
14421497 } ;
@@ -1706,7 +1761,7 @@ impl<'tcx> WitnessPat<'tcx> {
17061761 let mut subpatterns = self . iter_fields ( ) . map ( |p| Box :: new ( p. to_pat ( cx) ) ) ;
17071762 let kind = match & self . ctor {
17081763 Bool ( b) => PatKind :: Constant { value : mir:: Const :: from_bool ( cx. tcx , * b) } ,
1709- IntRange ( range) => return range. to_pat ( cx . tcx , self . ty ) ,
1764+ IntRange ( range) => return range. to_pat ( self . ty , cx . tcx ) ,
17101765 Single | Variant ( _) => match self . ty . kind ( ) {
17111766 ty:: Tuple ( ..) => PatKind :: Leaf {
17121767 subpatterns : subpatterns
0 commit comments