@@ -19,13 +19,15 @@ use rustc_middle::middle::region;
1919use rustc_middle:: mir:: interpret:: AllocId ;
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,240 @@ 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+ #[ allow( irrefutable_let_patterns) ]
879+ if let PatRangeBoundary :: Finite { value, .. } = & self . lo {
880+ write ! ( f, "{value}" ) ?;
881+ }
882+ write ! ( f, "{}" , self . end) ?;
883+ #[ allow( irrefutable_let_patterns) ]
884+ if let PatRangeBoundary :: Finite { value, .. } = & self . hi {
885+ write ! ( f, "{value}" ) ?;
886+ }
887+ Ok ( ( ) )
888+ }
889+ }
890+
891+ /// A (possibly open) boundary of a range pattern.
892+ /// If present, the const must be of a numeric type.
893+ #[ derive( Copy , Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
894+ pub enum PatRangeBoundary < ' tcx > {
895+ Finite { value : mir:: Const < ' tcx > } ,
896+ // PosInfinity,
897+ // NegInfinity,
898+ }
899+
900+ impl < ' tcx > PatRangeBoundary < ' tcx > {
901+ #[ inline]
902+ pub fn new_finite (
903+ value : mir:: Const < ' tcx > ,
904+ _tcx : TyCtxt < ' tcx > ,
905+ _param_env : ty:: ParamEnv < ' tcx > ,
906+ ) -> Self {
907+ Self :: Finite { value }
908+ }
909+ #[ inline]
910+ pub fn lower_bound ( ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
911+ // Self::NegInfinity
912+ // Unwrap is ok because the type is known to be numeric.
913+ let c = ty. numeric_min_val ( tcx) . unwrap ( ) ;
914+ let value = mir:: Const :: from_ty_const ( c, tcx) ;
915+ Self :: Finite { value }
916+ }
917+ #[ inline]
918+ pub fn upper_bound ( ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
919+ // Self::PosInfinity
920+ // Unwrap is ok because the type is known to be numeric.
921+ let c = ty. numeric_max_val ( tcx) . unwrap ( ) ;
922+ let value = mir:: Const :: from_ty_const ( c, tcx) ;
923+ Self :: Finite { value }
924+ }
925+
926+ #[ inline]
927+ pub fn to_const (
928+ self ,
929+ _ty : Ty < ' tcx > ,
930+ _tcx : TyCtxt < ' tcx > ,
931+ _param_env : ty:: ParamEnv < ' tcx > ,
932+ ) -> mir:: Const < ' tcx > {
933+ match self {
934+ Self :: Finite { value } => value,
935+ // Self::PosInfinity | Self::NegInfinity => unreachable!(),
936+ }
937+ }
938+ #[ inline]
939+ pub fn eval_bits (
940+ self ,
941+ _ty : Ty < ' tcx > ,
942+ tcx : TyCtxt < ' tcx > ,
943+ param_env : ty:: ParamEnv < ' tcx > ,
944+ ) -> u128 {
945+ match self {
946+ Self :: Finite { value } => value. eval_bits ( tcx, param_env) ,
947+ // Self::NegInfinity => {
948+ // // Unwrap is ok because the type is known to be numeric.
949+ // ty.numeric_min_val_as_bits(tcx).unwrap()
950+ // }
951+ // Self::PosInfinity => {
952+ // // Unwrap is ok because the type is known to be numeric.
953+ // ty.numeric_max_val_as_bits(tcx).unwrap()
954+ // }
955+ }
956+ }
957+
958+ #[ instrument( skip( tcx) , level = "debug" ) ]
959+ #[ inline]
960+ pub fn compare_with (
961+ self ,
962+ other : Self ,
963+ ty : Ty < ' tcx > ,
964+ tcx : TyCtxt < ' tcx > ,
965+ param_env : ty:: ParamEnv < ' tcx > ,
966+ ) -> Option < Ordering > {
967+ use PatRangeBoundary :: * ;
968+ match ( self , other) {
969+ // (PosInfinity, PosInfinity) => return Some(Ordering::Equal),
970+ // (NegInfinity, NegInfinity) => return Some(Ordering::Equal),
971+
972+ // This code is hot when compiling matches with many ranges. So we
973+ // special-case extraction of evaluated scalars for speed, for types where
974+ // raw data comparisons are appropriate. E.g. `unicode-normalization` has
975+ // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
976+ // in this way.
977+ ( Finite { value : mir:: Const :: Ty ( a) } , Finite { value : mir:: Const :: Ty ( b) } )
978+ if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) =>
979+ {
980+ return Some ( a. kind ( ) . cmp ( & b. kind ( ) ) ) ;
981+ }
982+ _ => { }
983+ }
984+
985+ let a = self . eval_bits ( ty, tcx, param_env) ;
986+ let b = other. eval_bits ( ty, tcx, param_env) ;
987+
988+ match ty. kind ( ) {
989+ ty:: Float ( ty:: FloatTy :: F32 ) => {
990+ use rustc_apfloat:: Float ;
991+ let a = rustc_apfloat:: ieee:: Single :: from_bits ( a) ;
992+ let b = rustc_apfloat:: ieee:: Single :: from_bits ( b) ;
993+ a. partial_cmp ( & b)
994+ }
995+ ty:: Float ( ty:: FloatTy :: F64 ) => {
996+ use rustc_apfloat:: Float ;
997+ let a = rustc_apfloat:: ieee:: Double :: from_bits ( a) ;
998+ let b = rustc_apfloat:: ieee:: Double :: from_bits ( b) ;
999+ a. partial_cmp ( & b)
1000+ }
1001+ ty:: Int ( ity) => {
1002+ use rustc_middle:: ty:: layout:: IntegerExt ;
1003+ let size = rustc_target:: abi:: Integer :: from_int_ty ( & tcx, * ity) . size ( ) ;
1004+ let a = size. sign_extend ( a) as i128 ;
1005+ let b = size. sign_extend ( b) as i128 ;
1006+ Some ( a. cmp ( & b) )
1007+ }
1008+ ty:: Uint ( _) | ty:: Char => Some ( a. cmp ( & b) ) ,
1009+ _ => bug ! ( ) ,
1010+ }
1011+ }
7821012}
7831013
7841014impl < ' tcx > fmt:: Display for Pat < ' tcx > {
@@ -904,11 +1134,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
9041134 write ! ( f, "{subpattern}" )
9051135 }
9061136 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- }
1137+ PatKind :: Range ( ref range) => write ! ( f, "{range}" ) ,
9121138 PatKind :: Slice { ref prefix, ref slice, ref suffix }
9131139 | PatKind :: Array { ref prefix, ref slice, ref suffix } => {
9141140 write ! ( f, "[" ) ?;
0 commit comments