@@ -16,17 +16,19 @@ use rustc_hir::RangeEnd;
1616use rustc_index:: newtype_index;
1717use rustc_index:: IndexVec ;
1818use rustc_middle:: middle:: region;
19- use rustc_middle:: mir:: interpret:: AllocId ;
19+ use rustc_middle:: mir:: interpret:: { AllocId , Scalar } ;
2020use rustc_middle:: mir:: { self , BinOp , BorrowKind , FakeReadCause , Mutability , UnOp } ;
2121use rustc_middle:: ty:: adjustment:: PointerCoercion ;
22+ use rustc_middle:: ty:: layout:: IntegerExt ;
2223use rustc_middle:: ty:: {
2324 self , AdtDef , CanonicalUserType , CanonicalUserTypeAnnotation , FnSig , GenericArgsRef , List , Ty ,
24- UpvarArgs ,
25+ TyCtxt , UpvarArgs ,
2526} ;
2627use rustc_span:: def_id:: LocalDefId ;
2728use rustc_span:: { sym, ErrorGuaranteed , Span , Symbol , DUMMY_SP } ;
28- use rustc_target:: abi:: { FieldIdx , VariantIdx } ;
29+ use rustc_target:: abi:: { FieldIdx , Integer , Size , VariantIdx } ;
2930use rustc_target:: asm:: InlineAsmRegOrRegClass ;
31+ use std:: cmp:: Ordering ;
3032use std:: fmt;
3133use std:: ops:: Index ;
3234
@@ -793,12 +795,243 @@ pub enum PatKind<'tcx> {
793795 Error ( ErrorGuaranteed ) ,
794796}
795797
798+ /// A range pattern.
799+ /// The boundaries must be of the same type and that type must be numeric.
796800#[ derive( Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
797801pub struct PatRange < ' tcx > {
798- pub lo : mir :: Const < ' tcx > ,
799- pub hi : mir :: Const < ' tcx > ,
802+ pub lo : PatRangeBoundary < ' tcx > ,
803+ pub hi : PatRangeBoundary < ' tcx > ,
800804 #[ type_visitable( ignore) ]
801805 pub end : RangeEnd ,
806+ pub ty : Ty < ' tcx > ,
807+ }
808+
809+ impl < ' tcx > PatRange < ' tcx > {
810+ /// Whether this range covers the full extent of possible values (best-effort, we ignore floats).
811+ #[ inline]
812+ pub fn is_full_range ( & self , tcx : TyCtxt < ' tcx > ) -> Option < bool > {
813+ let ( min, max, size, bias) = match * self . ty . kind ( ) {
814+ ty:: Char => ( 0 , std:: char:: MAX as u128 , Size :: from_bits ( 32 ) , 0 ) ,
815+ ty:: Int ( ity) => {
816+ let size = Integer :: from_int_ty ( & tcx, ity) . size ( ) ;
817+ let max = size. truncate ( u128:: MAX ) ;
818+ let bias = 1u128 << ( size. bits ( ) - 1 ) ;
819+ ( 0 , max, size, bias)
820+ }
821+ ty:: Uint ( uty) => {
822+ let size = Integer :: from_uint_ty ( & tcx, uty) . size ( ) ;
823+ let max = size. unsigned_int_max ( ) ;
824+ ( 0 , max, size, 0 )
825+ }
826+ _ => return None ,
827+ } ;
828+
829+ // We want to compare ranges numerically, but the order of the bitwise representation of
830+ // signed integers does not match their numeric order. Thus, to correct the ordering, we
831+ // need to shift the range of signed integers to correct the comparison. This is achieved by
832+ // XORing with a bias (see pattern/deconstruct_pat.rs for another pertinent example of this
833+ // pattern).
834+ //
835+ // Also, for performance, it's important to only do the second `try_to_bits` if necessary.
836+ let lo_is_min = match self . lo {
837+ PatRangeBoundary :: NegInfinity => true ,
838+ PatRangeBoundary :: Finite ( value) => {
839+ let lo = value. try_to_bits ( size) . unwrap ( ) ^ bias;
840+ lo <= min
841+ }
842+ PatRangeBoundary :: PosInfinity => false ,
843+ } ;
844+ if lo_is_min {
845+ let hi_is_max = match self . hi {
846+ PatRangeBoundary :: NegInfinity => false ,
847+ PatRangeBoundary :: Finite ( value) => {
848+ let hi = value. try_to_bits ( size) . unwrap ( ) ^ bias;
849+ hi > max || hi == max && self . end == RangeEnd :: Included
850+ }
851+ PatRangeBoundary :: PosInfinity => true ,
852+ } ;
853+ if hi_is_max {
854+ return Some ( true ) ;
855+ }
856+ }
857+ Some ( false )
858+ }
859+
860+ #[ inline]
861+ pub fn contains (
862+ & self ,
863+ value : mir:: Const < ' tcx > ,
864+ tcx : TyCtxt < ' tcx > ,
865+ param_env : ty:: ParamEnv < ' tcx > ,
866+ ) -> Option < bool > {
867+ use Ordering :: * ;
868+ debug_assert_eq ! ( self . ty, value. ty( ) ) ;
869+ let ty = self . ty ;
870+ let value = PatRangeBoundary :: Finite ( value) ;
871+ // For performance, it's important to only do the second comparison if necessary.
872+ Some (
873+ match self . lo . compare_with ( value, ty, tcx, param_env) ? {
874+ Less | Equal => true ,
875+ Greater => false ,
876+ } && match value. compare_with ( self . hi , ty, tcx, param_env) ? {
877+ Less => true ,
878+ Equal => self . end == RangeEnd :: Included ,
879+ Greater => false ,
880+ } ,
881+ )
882+ }
883+
884+ #[ inline]
885+ pub fn overlaps (
886+ & self ,
887+ other : & Self ,
888+ tcx : TyCtxt < ' tcx > ,
889+ param_env : ty:: ParamEnv < ' tcx > ,
890+ ) -> Option < bool > {
891+ use Ordering :: * ;
892+ debug_assert_eq ! ( self . ty, other. ty) ;
893+ // For performance, it's important to only do the second comparison if necessary.
894+ Some (
895+ match other. lo . compare_with ( self . hi , self . ty , tcx, param_env) ? {
896+ Less => true ,
897+ Equal => self . end == RangeEnd :: Included ,
898+ Greater => false ,
899+ } && match self . lo . compare_with ( other. hi , self . ty , tcx, param_env) ? {
900+ Less => true ,
901+ Equal => other. end == RangeEnd :: Included ,
902+ Greater => false ,
903+ } ,
904+ )
905+ }
906+ }
907+
908+ impl < ' tcx > fmt:: Display for PatRange < ' tcx > {
909+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
910+ if let PatRangeBoundary :: Finite ( value) = & self . lo {
911+ write ! ( f, "{value}" ) ?;
912+ }
913+ if let PatRangeBoundary :: Finite ( value) = & self . hi {
914+ write ! ( f, "{}" , self . end) ?;
915+ write ! ( f, "{value}" ) ?;
916+ } else {
917+ // `0..` is parsed as an inclusive range, we must display it correctly.
918+ write ! ( f, ".." ) ?;
919+ }
920+ Ok ( ( ) )
921+ }
922+ }
923+
924+ /// A (possibly open) boundary of a range pattern.
925+ /// If present, the const must be of a numeric type.
926+ #[ derive( Copy , Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
927+ pub enum PatRangeBoundary < ' tcx > {
928+ Finite ( mir:: Const < ' tcx > ) ,
929+ NegInfinity ,
930+ PosInfinity ,
931+ }
932+
933+ impl < ' tcx > PatRangeBoundary < ' tcx > {
934+ #[ inline]
935+ pub fn is_finite ( self ) -> bool {
936+ matches ! ( self , Self :: Finite ( ..) )
937+ }
938+ #[ inline]
939+ pub fn as_finite ( self ) -> Option < mir:: Const < ' tcx > > {
940+ match self {
941+ Self :: Finite ( value) => Some ( value) ,
942+ Self :: NegInfinity | Self :: PosInfinity => None ,
943+ }
944+ }
945+ #[ inline]
946+ pub fn to_const ( self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> mir:: Const < ' tcx > {
947+ match self {
948+ Self :: Finite ( value) => value,
949+ Self :: NegInfinity => {
950+ // Unwrap is ok because the type is known to be numeric.
951+ let c = ty. numeric_min_val ( tcx) . unwrap ( ) ;
952+ mir:: Const :: from_ty_const ( c, tcx)
953+ }
954+ Self :: PosInfinity => {
955+ // Unwrap is ok because the type is known to be numeric.
956+ let c = ty. numeric_max_val ( tcx) . unwrap ( ) ;
957+ mir:: Const :: from_ty_const ( c, tcx)
958+ }
959+ }
960+ }
961+ pub fn eval_bits ( self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > , param_env : ty:: ParamEnv < ' tcx > ) -> u128 {
962+ match self {
963+ Self :: Finite ( value) => value. eval_bits ( tcx, param_env) ,
964+ Self :: NegInfinity => {
965+ // Unwrap is ok because the type is known to be numeric.
966+ ty. numeric_min_and_max_as_bits ( tcx) . unwrap ( ) . 0
967+ }
968+ Self :: PosInfinity => {
969+ // Unwrap is ok because the type is known to be numeric.
970+ ty. numeric_min_and_max_as_bits ( tcx) . unwrap ( ) . 1
971+ }
972+ }
973+ }
974+
975+ #[ instrument( skip( tcx, param_env) , level = "debug" , ret) ]
976+ pub fn compare_with (
977+ self ,
978+ other : Self ,
979+ ty : Ty < ' tcx > ,
980+ tcx : TyCtxt < ' tcx > ,
981+ param_env : ty:: ParamEnv < ' tcx > ,
982+ ) -> Option < Ordering > {
983+ use PatRangeBoundary :: * ;
984+ match ( self , other) {
985+ // When comparing with infinities, we must remember that `0u8..` and `0u8..=255`
986+ // describe the same range. These two shortcuts are ok, but for the rest we must check
987+ // bit values.
988+ ( PosInfinity , PosInfinity ) => return Some ( Ordering :: Equal ) ,
989+ ( NegInfinity , NegInfinity ) => return Some ( Ordering :: Equal ) ,
990+
991+ // This code is hot when compiling matches with many ranges. So we
992+ // special-case extraction of evaluated scalars for speed, for types where
993+ // raw data comparisons are appropriate. E.g. `unicode-normalization` has
994+ // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
995+ // in this way.
996+ ( Finite ( mir:: Const :: Ty ( a) ) , Finite ( mir:: Const :: Ty ( b) ) )
997+ if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) =>
998+ {
999+ return Some ( a. kind ( ) . cmp ( & b. kind ( ) ) ) ;
1000+ }
1001+ (
1002+ Finite ( mir:: Const :: Val ( mir:: ConstValue :: Scalar ( Scalar :: Int ( a) ) , _) ) ,
1003+ Finite ( mir:: Const :: Val ( mir:: ConstValue :: Scalar ( Scalar :: Int ( b) ) , _) ) ,
1004+ ) if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) => return Some ( a. cmp ( & b) ) ,
1005+ _ => { }
1006+ }
1007+
1008+ let a = self . eval_bits ( ty, tcx, param_env) ;
1009+ let b = other. eval_bits ( ty, tcx, param_env) ;
1010+
1011+ match ty. kind ( ) {
1012+ ty:: Float ( ty:: FloatTy :: F32 ) => {
1013+ use rustc_apfloat:: Float ;
1014+ let a = rustc_apfloat:: ieee:: Single :: from_bits ( a) ;
1015+ let b = rustc_apfloat:: ieee:: Single :: from_bits ( b) ;
1016+ a. partial_cmp ( & b)
1017+ }
1018+ ty:: Float ( ty:: FloatTy :: F64 ) => {
1019+ use rustc_apfloat:: Float ;
1020+ let a = rustc_apfloat:: ieee:: Double :: from_bits ( a) ;
1021+ let b = rustc_apfloat:: ieee:: Double :: from_bits ( b) ;
1022+ a. partial_cmp ( & b)
1023+ }
1024+ ty:: Int ( ity) => {
1025+ use rustc_middle:: ty:: layout:: IntegerExt ;
1026+ let size = rustc_target:: abi:: Integer :: from_int_ty ( & tcx, * ity) . size ( ) ;
1027+ let a = size. sign_extend ( a) as i128 ;
1028+ let b = size. sign_extend ( b) as i128 ;
1029+ Some ( a. cmp ( & b) )
1030+ }
1031+ ty:: Uint ( _) | ty:: Char => Some ( a. cmp ( & b) ) ,
1032+ _ => bug ! ( ) ,
1033+ }
1034+ }
8021035}
8031036
8041037impl < ' tcx > fmt:: Display for Pat < ' tcx > {
@@ -924,11 +1157,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
9241157 write ! ( f, "{subpattern}" )
9251158 }
9261159 PatKind :: Constant { value } => write ! ( f, "{value}" ) ,
927- PatKind :: Range ( box PatRange { lo, hi, end } ) => {
928- write ! ( f, "{lo}" ) ?;
929- write ! ( f, "{end}" ) ?;
930- write ! ( f, "{hi}" )
931- }
1160+ PatKind :: Range ( ref range) => write ! ( f, "{range}" ) ,
9321161 PatKind :: Slice { ref prefix, ref slice, ref suffix }
9331162 | PatKind :: Array { ref prefix, ref slice, ref suffix } => {
9341163 write ! ( f, "[" ) ?;
0 commit comments