@@ -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
@@ -810,12 +812,217 @@ pub enum PatKind<'tcx> {
810812 Error ( ErrorGuaranteed ) ,
811813}
812814
815+ /// A range pattern.
816+ /// The boundaries must be of the same type and that type must be numeric.
813817#[ derive( Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
814818pub struct PatRange < ' tcx > {
815- pub lo : mir :: Const < ' tcx > ,
816- pub hi : mir :: Const < ' tcx > ,
819+ pub lo : PatRangeBoundary < ' tcx > ,
820+ pub hi : PatRangeBoundary < ' tcx > ,
817821 #[ type_visitable( ignore) ]
818822 pub end : RangeEnd ,
823+ pub ty : Ty < ' tcx > ,
824+ }
825+
826+ impl < ' tcx > PatRange < ' tcx > {
827+ /// Whether this range covers the full extent of possible values (best-effort, we ignore floats).
828+ #[ inline]
829+ pub fn is_full_range ( & self , tcx : TyCtxt < ' tcx > ) -> Option < bool > {
830+ let ( min, max, size, bias) = match * self . ty . kind ( ) {
831+ ty:: Char => ( 0 , std:: char:: MAX as u128 , Size :: from_bits ( 32 ) , 0 ) ,
832+ ty:: Int ( ity) => {
833+ let size = Integer :: from_int_ty ( & tcx, ity) . size ( ) ;
834+ let max = size. truncate ( u128:: MAX ) ;
835+ let bias = 1u128 << ( size. bits ( ) - 1 ) ;
836+ ( 0 , max, size, bias)
837+ }
838+ ty:: Uint ( uty) => {
839+ let size = Integer :: from_uint_ty ( & tcx, uty) . size ( ) ;
840+ let max = size. unsigned_int_max ( ) ;
841+ ( 0 , max, size, 0 )
842+ }
843+ _ => return None ,
844+ } ;
845+
846+ // We want to compare ranges numerically, but the order of the bitwise representation of
847+ // signed integers does not match their numeric order. Thus, to correct the ordering, we
848+ // need to shift the range of signed integers to correct the comparison. This is achieved by
849+ // XORing with a bias (see pattern/deconstruct_pat.rs for another pertinent example of this
850+ // pattern).
851+ //
852+ // Also, for performance, it's important to only do the second `try_to_bits` if necessary.
853+ let lo_is_min = match self . lo {
854+ PatRangeBoundary :: Finite ( value) => {
855+ let lo = value. try_to_bits ( size) . unwrap ( ) ^ bias;
856+ lo <= min
857+ }
858+ } ;
859+ if lo_is_min {
860+ let hi_is_max = match self . hi {
861+ PatRangeBoundary :: Finite ( value) => {
862+ let hi = value. try_to_bits ( size) . unwrap ( ) ^ bias;
863+ hi > max || hi == max && self . end == RangeEnd :: Included
864+ }
865+ } ;
866+ if hi_is_max {
867+ return Some ( true ) ;
868+ }
869+ }
870+ Some ( false )
871+ }
872+
873+ #[ inline]
874+ pub fn contains (
875+ & self ,
876+ value : mir:: Const < ' tcx > ,
877+ tcx : TyCtxt < ' tcx > ,
878+ param_env : ty:: ParamEnv < ' tcx > ,
879+ ) -> Option < bool > {
880+ use Ordering :: * ;
881+ debug_assert_eq ! ( self . ty, value. ty( ) ) ;
882+ let ty = self . ty ;
883+ let value = PatRangeBoundary :: Finite ( value) ;
884+ // For performance, it's important to only do the second comparison if necessary.
885+ Some (
886+ match self . lo . compare_with ( value, ty, tcx, param_env) ? {
887+ Less | Equal => true ,
888+ Greater => false ,
889+ } && match value. compare_with ( self . hi , ty, tcx, param_env) ? {
890+ Less => true ,
891+ Equal => self . end == RangeEnd :: Included ,
892+ Greater => false ,
893+ } ,
894+ )
895+ }
896+
897+ #[ inline]
898+ pub fn overlaps (
899+ & self ,
900+ other : & Self ,
901+ tcx : TyCtxt < ' tcx > ,
902+ param_env : ty:: ParamEnv < ' tcx > ,
903+ ) -> Option < bool > {
904+ use Ordering :: * ;
905+ debug_assert_eq ! ( self . ty, other. ty) ;
906+ // For performance, it's important to only do the second comparison if necessary.
907+ Some (
908+ match other. lo . compare_with ( self . hi , self . ty , tcx, param_env) ? {
909+ Less => true ,
910+ Equal => self . end == RangeEnd :: Included ,
911+ Greater => false ,
912+ } && match self . lo . compare_with ( other. hi , self . ty , tcx, param_env) ? {
913+ Less => true ,
914+ Equal => other. end == RangeEnd :: Included ,
915+ Greater => false ,
916+ } ,
917+ )
918+ }
919+ }
920+
921+ impl < ' tcx > fmt:: Display for PatRange < ' tcx > {
922+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
923+ let PatRangeBoundary :: Finite ( value) = & self . lo ;
924+ write ! ( f, "{value}" ) ?;
925+ write ! ( f, "{}" , self . end) ?;
926+ let PatRangeBoundary :: Finite ( value) = & self . hi ;
927+ write ! ( f, "{value}" ) ?;
928+ Ok ( ( ) )
929+ }
930+ }
931+
932+ /// A (possibly open) boundary of a range pattern.
933+ /// If present, the const must be of a numeric type.
934+ #[ derive( Copy , Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
935+ pub enum PatRangeBoundary < ' tcx > {
936+ Finite ( mir:: Const < ' tcx > ) ,
937+ }
938+
939+ impl < ' tcx > PatRangeBoundary < ' tcx > {
940+ #[ inline]
941+ pub fn lower_bound ( ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
942+ // Unwrap is ok because the type is known to be numeric.
943+ let c = ty. numeric_min_val ( tcx) . unwrap ( ) ;
944+ let value = mir:: Const :: from_ty_const ( c, tcx) ;
945+ Self :: Finite ( value)
946+ }
947+ #[ inline]
948+ pub fn upper_bound ( ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
949+ // Unwrap is ok because the type is known to be numeric.
950+ let c = ty. numeric_max_val ( tcx) . unwrap ( ) ;
951+ let value = mir:: Const :: from_ty_const ( c, tcx) ;
952+ Self :: Finite ( value)
953+ }
954+
955+ #[ inline]
956+ pub fn to_const ( self , _ty : Ty < ' tcx > , _tcx : TyCtxt < ' tcx > ) -> mir:: Const < ' tcx > {
957+ match self {
958+ Self :: Finite ( value) => value,
959+ }
960+ }
961+ pub fn eval_bits (
962+ self ,
963+ _ty : Ty < ' tcx > ,
964+ tcx : TyCtxt < ' tcx > ,
965+ param_env : ty:: ParamEnv < ' tcx > ,
966+ ) -> u128 {
967+ match self {
968+ Self :: Finite ( value) => value. eval_bits ( tcx, param_env) ,
969+ }
970+ }
971+
972+ #[ instrument( skip( tcx, param_env) , level = "debug" , ret) ]
973+ pub fn compare_with (
974+ self ,
975+ other : Self ,
976+ ty : Ty < ' tcx > ,
977+ tcx : TyCtxt < ' tcx > ,
978+ param_env : ty:: ParamEnv < ' tcx > ,
979+ ) -> Option < Ordering > {
980+ use PatRangeBoundary :: * ;
981+ match ( self , other) {
982+ // This code is hot when compiling matches with many ranges. So we
983+ // special-case extraction of evaluated scalars for speed, for types where
984+ // raw data comparisons are appropriate. E.g. `unicode-normalization` has
985+ // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
986+ // in this way.
987+ ( Finite ( mir:: Const :: Ty ( a) ) , Finite ( mir:: Const :: Ty ( b) ) )
988+ if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) =>
989+ {
990+ return Some ( a. kind ( ) . cmp ( & b. kind ( ) ) ) ;
991+ }
992+ (
993+ Finite ( mir:: Const :: Val ( mir:: ConstValue :: Scalar ( Scalar :: Int ( a) ) , _) ) ,
994+ Finite ( mir:: Const :: Val ( mir:: ConstValue :: Scalar ( Scalar :: Int ( b) ) , _) ) ,
995+ ) if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) => return Some ( a. cmp ( & b) ) ,
996+ _ => { }
997+ }
998+
999+ let a = self . eval_bits ( ty, tcx, param_env) ;
1000+ let b = other. eval_bits ( ty, tcx, param_env) ;
1001+
1002+ match ty. kind ( ) {
1003+ ty:: Float ( ty:: FloatTy :: F32 ) => {
1004+ use rustc_apfloat:: Float ;
1005+ let a = rustc_apfloat:: ieee:: Single :: from_bits ( a) ;
1006+ let b = rustc_apfloat:: ieee:: Single :: from_bits ( b) ;
1007+ a. partial_cmp ( & b)
1008+ }
1009+ ty:: Float ( ty:: FloatTy :: F64 ) => {
1010+ use rustc_apfloat:: Float ;
1011+ let a = rustc_apfloat:: ieee:: Double :: from_bits ( a) ;
1012+ let b = rustc_apfloat:: ieee:: Double :: from_bits ( b) ;
1013+ a. partial_cmp ( & b)
1014+ }
1015+ ty:: Int ( ity) => {
1016+ use rustc_middle:: ty:: layout:: IntegerExt ;
1017+ let size = rustc_target:: abi:: Integer :: from_int_ty ( & tcx, * ity) . size ( ) ;
1018+ let a = size. sign_extend ( a) as i128 ;
1019+ let b = size. sign_extend ( b) as i128 ;
1020+ Some ( a. cmp ( & b) )
1021+ }
1022+ ty:: Uint ( _) | ty:: Char => Some ( a. cmp ( & b) ) ,
1023+ _ => bug ! ( ) ,
1024+ }
1025+ }
8191026}
8201027
8211028impl < ' tcx > fmt:: Display for Pat < ' tcx > {
@@ -944,11 +1151,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
9441151 PatKind :: InlineConstant { def : _, ref subpattern } => {
9451152 write ! ( f, "{} (from inline const)" , subpattern)
9461153 }
947- PatKind :: Range ( box PatRange { lo, hi, end } ) => {
948- write ! ( f, "{lo}" ) ?;
949- write ! ( f, "{end}" ) ?;
950- write ! ( f, "{hi}" )
951- }
1154+ PatKind :: Range ( ref range) => write ! ( f, "{range}" ) ,
9521155 PatKind :: Slice { ref prefix, ref slice, ref suffix }
9531156 | PatKind :: Array { ref prefix, ref slice, ref suffix } => {
9541157 write ! ( f, "[" ) ?;
0 commit comments