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