@@ -16,16 +16,18 @@ 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:: GenericArgsRef ;
23- use rustc_middle:: ty:: { self , AdtDef , FnSig , List , Ty , UpvarArgs } ;
24+ use rustc_middle:: ty:: { self , AdtDef , FnSig , List , Ty , TyCtxt , UpvarArgs } ;
2425use rustc_middle:: ty:: { CanonicalUserType , CanonicalUserTypeAnnotation } ;
2526use rustc_span:: def_id:: LocalDefId ;
2627use rustc_span:: { sym, Span , Symbol , DUMMY_SP } ;
27- use rustc_target:: abi:: { FieldIdx , VariantIdx } ;
28+ use rustc_target:: abi:: { FieldIdx , Integer , Size , VariantIdx } ;
2829use rustc_target:: asm:: InlineAsmRegOrRegClass ;
30+ use std:: cmp:: Ordering ;
2931use std:: fmt;
3032use std:: ops:: Index ;
3133
@@ -773,12 +775,237 @@ pub enum PatKind<'tcx> {
773775 } ,
774776}
775777
778+ /// A range pattern.
779+ /// The boundaries must be of the same type and that type must be numeric.
776780#[ derive( Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
777781pub struct PatRange < ' tcx > {
778- pub lo : mir :: Const < ' tcx > ,
779- pub hi : mir :: Const < ' tcx > ,
782+ pub lo : PatRangeBoundary < ' tcx > ,
783+ pub hi : PatRangeBoundary < ' tcx > ,
780784 #[ type_visitable( ignore) ]
781785 pub end : RangeEnd ,
786+ pub ty : Ty < ' tcx > ,
787+ }
788+
789+ impl < ' tcx > PatRange < ' tcx > {
790+ /// Whether this range covers the full extent of possible values (best-effort, we ignore floats).
791+ #[ inline]
792+ pub fn is_full_range ( & self , tcx : TyCtxt < ' tcx > ) -> Option < bool > {
793+ let ( min, max, size, bias) = match * self . ty . kind ( ) {
794+ ty:: Char => ( 0 , std:: char:: MAX as u128 , Size :: from_bits ( 32 ) , 0 ) ,
795+ ty:: Int ( ity) => {
796+ let size = Integer :: from_int_ty ( & tcx, ity) . size ( ) ;
797+ let max = size. truncate ( u128:: MAX ) ;
798+ let bias = 1u128 << ( size. bits ( ) - 1 ) ;
799+ ( 0 , max, size, bias)
800+ }
801+ ty:: Uint ( uty) => {
802+ let size = Integer :: from_uint_ty ( & tcx, uty) . size ( ) ;
803+ let max = size. unsigned_int_max ( ) ;
804+ ( 0 , max, size, 0 )
805+ }
806+ _ => return None ,
807+ } ;
808+
809+ // We want to compare ranges numerically, but the order of the bitwise representation of
810+ // signed integers does not match their numeric order. Thus, to correct the ordering, we
811+ // need to shift the range of signed integers to correct the comparison. This is achieved by
812+ // XORing with a bias (see pattern/deconstruct_pat.rs for another pertinent example of this
813+ // pattern).
814+ //
815+ // Also, for performance, it's important to only do the second `try_to_bits` if necessary.
816+ let lo_is_min = match self . lo {
817+ PatRangeBoundary :: NegInfinity => true ,
818+ PatRangeBoundary :: Finite ( value) => {
819+ let lo = value. try_to_bits ( size) . unwrap ( ) ^ bias;
820+ lo <= min
821+ }
822+ PatRangeBoundary :: PosInfinity => false ,
823+ } ;
824+ if lo_is_min {
825+ let hi_is_max = match self . hi {
826+ PatRangeBoundary :: NegInfinity => false ,
827+ PatRangeBoundary :: Finite ( value) => {
828+ let hi = value. try_to_bits ( size) . unwrap ( ) ^ bias;
829+ hi > max || hi == max && self . end == RangeEnd :: Included
830+ }
831+ PatRangeBoundary :: PosInfinity => true ,
832+ } ;
833+ if hi_is_max {
834+ return Some ( true ) ;
835+ }
836+ }
837+ Some ( false )
838+ }
839+
840+ #[ inline]
841+ pub fn contains (
842+ & self ,
843+ value : mir:: Const < ' tcx > ,
844+ tcx : TyCtxt < ' tcx > ,
845+ param_env : ty:: ParamEnv < ' tcx > ,
846+ ) -> Option < bool > {
847+ use Ordering :: * ;
848+ debug_assert_eq ! ( self . ty, value. ty( ) ) ;
849+ let ty = self . ty ;
850+ let value = PatRangeBoundary :: Finite ( value) ;
851+ // For performance, it's important to only do the second comparison if necessary.
852+ Some (
853+ match self . lo . compare_with ( value, ty, tcx, param_env) ? {
854+ Less | Equal => true ,
855+ Greater => false ,
856+ } && match value. compare_with ( self . hi , ty, tcx, param_env) ? {
857+ Less => true ,
858+ Equal => self . end == RangeEnd :: Included ,
859+ Greater => false ,
860+ } ,
861+ )
862+ }
863+
864+ #[ inline]
865+ pub fn overlaps (
866+ & self ,
867+ other : & Self ,
868+ tcx : TyCtxt < ' tcx > ,
869+ param_env : ty:: ParamEnv < ' tcx > ,
870+ ) -> Option < bool > {
871+ use Ordering :: * ;
872+ debug_assert_eq ! ( self . ty, other. ty) ;
873+ // For performance, it's important to only do the second comparison if necessary.
874+ Some (
875+ match other. lo . compare_with ( self . hi , self . ty , tcx, param_env) ? {
876+ Less => true ,
877+ Equal => self . end == RangeEnd :: Included ,
878+ Greater => false ,
879+ } && match self . lo . compare_with ( other. hi , self . ty , tcx, param_env) ? {
880+ Less => true ,
881+ Equal => other. end == RangeEnd :: Included ,
882+ Greater => false ,
883+ } ,
884+ )
885+ }
886+ }
887+
888+ impl < ' tcx > fmt:: Display for PatRange < ' tcx > {
889+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
890+ if let PatRangeBoundary :: Finite ( value) = & self . lo {
891+ write ! ( f, "{value}" ) ?;
892+ }
893+ write ! ( f, "{}" , self . end) ?;
894+ if let PatRangeBoundary :: Finite ( value) = & self . hi {
895+ write ! ( f, "{value}" ) ?;
896+ }
897+ Ok ( ( ) )
898+ }
899+ }
900+
901+ /// A (possibly open) boundary of a range pattern.
902+ /// If present, the const must be of a numeric type.
903+ #[ derive( Copy , Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
904+ pub enum PatRangeBoundary < ' tcx > {
905+ Finite ( mir:: Const < ' tcx > ) ,
906+ NegInfinity ,
907+ PosInfinity ,
908+ }
909+
910+ impl < ' tcx > PatRangeBoundary < ' tcx > {
911+ #[ inline]
912+ pub fn is_finite ( self ) -> bool {
913+ matches ! ( self , Self :: Finite ( ..) )
914+ }
915+ #[ inline]
916+ pub fn as_finite ( self ) -> Option < mir:: Const < ' tcx > > {
917+ match self {
918+ Self :: Finite ( value) => Some ( value) ,
919+ Self :: NegInfinity | Self :: PosInfinity => None ,
920+ }
921+ }
922+ #[ inline]
923+ pub fn to_const ( self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> mir:: Const < ' tcx > {
924+ match self {
925+ Self :: Finite ( value) => value,
926+ Self :: NegInfinity => {
927+ // Unwrap is ok because the type is known to be numeric.
928+ let c = ty. numeric_min_val ( tcx) . unwrap ( ) ;
929+ mir:: Const :: from_ty_const ( c, tcx)
930+ }
931+ Self :: PosInfinity => {
932+ // Unwrap is ok because the type is known to be numeric.
933+ let c = ty. numeric_max_val ( tcx) . unwrap ( ) ;
934+ mir:: Const :: from_ty_const ( c, tcx)
935+ }
936+ }
937+ }
938+ pub fn eval_bits ( self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > , param_env : ty:: ParamEnv < ' tcx > ) -> u128 {
939+ match self {
940+ Self :: Finite ( value) => value. eval_bits ( tcx, param_env) ,
941+ Self :: NegInfinity => {
942+ // Unwrap is ok because the type is known to be numeric.
943+ ty. numeric_min_and_max_as_bits ( tcx) . unwrap ( ) . 0
944+ }
945+ Self :: PosInfinity => {
946+ // Unwrap is ok because the type is known to be numeric.
947+ ty. numeric_min_and_max_as_bits ( tcx) . unwrap ( ) . 1
948+ }
949+ }
950+ }
951+
952+ #[ instrument( skip( tcx, param_env) , level = "debug" , ret) ]
953+ pub fn compare_with (
954+ self ,
955+ other : Self ,
956+ ty : Ty < ' tcx > ,
957+ tcx : TyCtxt < ' tcx > ,
958+ param_env : ty:: ParamEnv < ' tcx > ,
959+ ) -> Option < Ordering > {
960+ use PatRangeBoundary :: * ;
961+ match ( self , other) {
962+ ( PosInfinity , PosInfinity ) => return Some ( Ordering :: Equal ) ,
963+ ( NegInfinity , NegInfinity ) => return Some ( Ordering :: Equal ) ,
964+
965+ // This code is hot when compiling matches with many ranges. So we
966+ // special-case extraction of evaluated scalars for speed, for types where
967+ // raw data comparisons are appropriate. E.g. `unicode-normalization` has
968+ // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
969+ // in this way.
970+ ( Finite ( mir:: Const :: Ty ( a) ) , Finite ( mir:: Const :: Ty ( b) ) )
971+ if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) =>
972+ {
973+ return Some ( a. kind ( ) . cmp ( & b. kind ( ) ) ) ;
974+ }
975+ (
976+ Finite ( mir:: Const :: Val ( mir:: ConstValue :: Scalar ( Scalar :: Int ( a) ) , _) ) ,
977+ Finite ( mir:: Const :: Val ( mir:: ConstValue :: Scalar ( Scalar :: Int ( b) ) , _) ) ,
978+ ) if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) => return Some ( a. cmp ( & b) ) ,
979+ _ => { }
980+ }
981+
982+ let a = self . eval_bits ( ty, tcx, param_env) ;
983+ let b = other. eval_bits ( ty, tcx, param_env) ;
984+
985+ match ty. kind ( ) {
986+ ty:: Float ( ty:: FloatTy :: F32 ) => {
987+ use rustc_apfloat:: Float ;
988+ let a = rustc_apfloat:: ieee:: Single :: from_bits ( a) ;
989+ let b = rustc_apfloat:: ieee:: Single :: from_bits ( b) ;
990+ a. partial_cmp ( & b)
991+ }
992+ ty:: Float ( ty:: FloatTy :: F64 ) => {
993+ use rustc_apfloat:: Float ;
994+ let a = rustc_apfloat:: ieee:: Double :: from_bits ( a) ;
995+ let b = rustc_apfloat:: ieee:: Double :: from_bits ( b) ;
996+ a. partial_cmp ( & b)
997+ }
998+ ty:: Int ( ity) => {
999+ use rustc_middle:: ty:: layout:: IntegerExt ;
1000+ let size = rustc_target:: abi:: Integer :: from_int_ty ( & tcx, * ity) . size ( ) ;
1001+ let a = size. sign_extend ( a) as i128 ;
1002+ let b = size. sign_extend ( b) as i128 ;
1003+ Some ( a. cmp ( & b) )
1004+ }
1005+ ty:: Uint ( _) | ty:: Char => Some ( a. cmp ( & b) ) ,
1006+ _ => bug ! ( ) ,
1007+ }
1008+ }
7821009}
7831010
7841011impl < ' tcx > fmt:: Display for Pat < ' tcx > {
@@ -904,11 +1131,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
9041131 write ! ( f, "{subpattern}" )
9051132 }
9061133 PatKind :: Constant { value } => write ! ( f, "{value}" ) ,
907- PatKind :: Range ( box PatRange { lo, hi, end } ) => {
908- write ! ( f, "{lo}" ) ?;
909- write ! ( f, "{end}" ) ?;
910- write ! ( f, "{hi}" )
911- }
1134+ PatKind :: Range ( ref range) => write ! ( f, "{range}" ) ,
9121135 PatKind :: Slice { ref prefix, ref slice, ref suffix }
9131136 | PatKind :: Array { ref prefix, ref slice, ref suffix } => {
9141137 write ! ( f, "[" ) ?;
0 commit comments