@@ -16,17 +16,19 @@ use rustc_hir::RangeEnd;
16
16
use rustc_index:: newtype_index;
17
17
use rustc_index:: IndexVec ;
18
18
use rustc_middle:: middle:: region;
19
- use rustc_middle:: mir:: interpret:: AllocId ;
19
+ use rustc_middle:: mir:: interpret:: { AllocId , Scalar } ;
20
20
use rustc_middle:: mir:: { self , BinOp , BorrowKind , FakeReadCause , Mutability , UnOp } ;
21
21
use rustc_middle:: ty:: adjustment:: PointerCoercion ;
22
+ use rustc_middle:: ty:: layout:: IntegerExt ;
22
23
use rustc_middle:: ty:: {
23
24
self , AdtDef , CanonicalUserType , CanonicalUserTypeAnnotation , FnSig , GenericArgsRef , List , Ty ,
24
- UpvarArgs ,
25
+ TyCtxt , UpvarArgs ,
25
26
} ;
26
27
use rustc_span:: def_id:: LocalDefId ;
27
28
use rustc_span:: { sym, ErrorGuaranteed , Span , Symbol , DUMMY_SP } ;
28
- use rustc_target:: abi:: { FieldIdx , VariantIdx } ;
29
+ use rustc_target:: abi:: { FieldIdx , Integer , Size , VariantIdx } ;
29
30
use rustc_target:: asm:: InlineAsmRegOrRegClass ;
31
+ use std:: cmp:: Ordering ;
30
32
use std:: fmt;
31
33
use std:: ops:: Index ;
32
34
@@ -810,12 +812,243 @@ pub enum PatKind<'tcx> {
810
812
Error ( ErrorGuaranteed ) ,
811
813
}
812
814
815
+ /// A range pattern.
816
+ /// The boundaries must be of the same type and that type must be numeric.
813
817
#[ derive( Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
814
818
pub 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 > ,
817
821
#[ type_visitable( ignore) ]
818
822
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 :: NegInfinity => true ,
855
+ PatRangeBoundary :: Finite ( value) => {
856
+ let lo = value. try_to_bits ( size) . unwrap ( ) ^ bias;
857
+ lo <= min
858
+ }
859
+ PatRangeBoundary :: PosInfinity => false ,
860
+ } ;
861
+ if lo_is_min {
862
+ let hi_is_max = match self . hi {
863
+ PatRangeBoundary :: NegInfinity => false ,
864
+ PatRangeBoundary :: Finite ( value) => {
865
+ let hi = value. try_to_bits ( size) . unwrap ( ) ^ bias;
866
+ hi > max || hi == max && self . end == RangeEnd :: Included
867
+ }
868
+ PatRangeBoundary :: PosInfinity => true ,
869
+ } ;
870
+ if hi_is_max {
871
+ return Some ( true ) ;
872
+ }
873
+ }
874
+ Some ( false )
875
+ }
876
+
877
+ #[ inline]
878
+ pub fn contains (
879
+ & self ,
880
+ value : mir:: Const < ' tcx > ,
881
+ tcx : TyCtxt < ' tcx > ,
882
+ param_env : ty:: ParamEnv < ' tcx > ,
883
+ ) -> Option < bool > {
884
+ use Ordering :: * ;
885
+ debug_assert_eq ! ( self . ty, value. ty( ) ) ;
886
+ let ty = self . ty ;
887
+ let value = PatRangeBoundary :: Finite ( value) ;
888
+ // For performance, it's important to only do the second comparison if necessary.
889
+ Some (
890
+ match self . lo . compare_with ( value, ty, tcx, param_env) ? {
891
+ Less | Equal => true ,
892
+ Greater => false ,
893
+ } && match value. compare_with ( self . hi , ty, tcx, param_env) ? {
894
+ Less => true ,
895
+ Equal => self . end == RangeEnd :: Included ,
896
+ Greater => false ,
897
+ } ,
898
+ )
899
+ }
900
+
901
+ #[ inline]
902
+ pub fn overlaps (
903
+ & self ,
904
+ other : & Self ,
905
+ tcx : TyCtxt < ' tcx > ,
906
+ param_env : ty:: ParamEnv < ' tcx > ,
907
+ ) -> Option < bool > {
908
+ use Ordering :: * ;
909
+ debug_assert_eq ! ( self . ty, other. ty) ;
910
+ // For performance, it's important to only do the second comparison if necessary.
911
+ Some (
912
+ match other. lo . compare_with ( self . hi , self . ty , tcx, param_env) ? {
913
+ Less => true ,
914
+ Equal => self . end == RangeEnd :: Included ,
915
+ Greater => false ,
916
+ } && match self . lo . compare_with ( other. hi , self . ty , tcx, param_env) ? {
917
+ Less => true ,
918
+ Equal => other. end == RangeEnd :: Included ,
919
+ Greater => false ,
920
+ } ,
921
+ )
922
+ }
923
+ }
924
+
925
+ impl < ' tcx > fmt:: Display for PatRange < ' tcx > {
926
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
927
+ if let PatRangeBoundary :: Finite ( value) = & self . lo {
928
+ write ! ( f, "{value}" ) ?;
929
+ }
930
+ if let PatRangeBoundary :: Finite ( value) = & self . hi {
931
+ write ! ( f, "{}" , self . end) ?;
932
+ write ! ( f, "{value}" ) ?;
933
+ } else {
934
+ // `0..` is parsed as an inclusive range, we must display it correctly.
935
+ write ! ( f, ".." ) ?;
936
+ }
937
+ Ok ( ( ) )
938
+ }
939
+ }
940
+
941
+ /// A (possibly open) boundary of a range pattern.
942
+ /// If present, the const must be of a numeric type.
943
+ #[ derive( Copy , Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
944
+ pub enum PatRangeBoundary < ' tcx > {
945
+ Finite ( mir:: Const < ' tcx > ) ,
946
+ NegInfinity ,
947
+ PosInfinity ,
948
+ }
949
+
950
+ impl < ' tcx > PatRangeBoundary < ' tcx > {
951
+ #[ inline]
952
+ pub fn is_finite ( self ) -> bool {
953
+ matches ! ( self , Self :: Finite ( ..) )
954
+ }
955
+ #[ inline]
956
+ pub fn as_finite ( self ) -> Option < mir:: Const < ' tcx > > {
957
+ match self {
958
+ Self :: Finite ( value) => Some ( value) ,
959
+ Self :: NegInfinity | Self :: PosInfinity => None ,
960
+ }
961
+ }
962
+ #[ inline]
963
+ pub fn to_const ( self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> mir:: Const < ' tcx > {
964
+ match self {
965
+ Self :: Finite ( value) => value,
966
+ Self :: NegInfinity => {
967
+ // Unwrap is ok because the type is known to be numeric.
968
+ let c = ty. numeric_min_val ( tcx) . unwrap ( ) ;
969
+ mir:: Const :: from_ty_const ( c, tcx)
970
+ }
971
+ Self :: PosInfinity => {
972
+ // Unwrap is ok because the type is known to be numeric.
973
+ let c = ty. numeric_max_val ( tcx) . unwrap ( ) ;
974
+ mir:: Const :: from_ty_const ( c, tcx)
975
+ }
976
+ }
977
+ }
978
+ pub fn eval_bits ( self , ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > , param_env : ty:: ParamEnv < ' tcx > ) -> u128 {
979
+ match self {
980
+ Self :: Finite ( value) => value. eval_bits ( tcx, param_env) ,
981
+ Self :: NegInfinity => {
982
+ // Unwrap is ok because the type is known to be numeric.
983
+ ty. numeric_min_and_max_as_bits ( tcx) . unwrap ( ) . 0
984
+ }
985
+ Self :: PosInfinity => {
986
+ // Unwrap is ok because the type is known to be numeric.
987
+ ty. numeric_min_and_max_as_bits ( tcx) . unwrap ( ) . 1
988
+ }
989
+ }
990
+ }
991
+
992
+ #[ instrument( skip( tcx, param_env) , level = "debug" , ret) ]
993
+ pub fn compare_with (
994
+ self ,
995
+ other : Self ,
996
+ ty : Ty < ' tcx > ,
997
+ tcx : TyCtxt < ' tcx > ,
998
+ param_env : ty:: ParamEnv < ' tcx > ,
999
+ ) -> Option < Ordering > {
1000
+ use PatRangeBoundary :: * ;
1001
+ match ( self , other) {
1002
+ // When comparing with infinities, we must remember that `0u8..` and `0u8..=255`
1003
+ // describe the same range. These two shortcuts are ok, but for the rest we must check
1004
+ // bit values.
1005
+ ( PosInfinity , PosInfinity ) => return Some ( Ordering :: Equal ) ,
1006
+ ( NegInfinity , NegInfinity ) => return Some ( Ordering :: Equal ) ,
1007
+
1008
+ // This code is hot when compiling matches with many ranges. So we
1009
+ // special-case extraction of evaluated scalars for speed, for types where
1010
+ // raw data comparisons are appropriate. E.g. `unicode-normalization` has
1011
+ // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
1012
+ // in this way.
1013
+ ( Finite ( mir:: Const :: Ty ( a) ) , Finite ( mir:: Const :: Ty ( b) ) )
1014
+ if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) =>
1015
+ {
1016
+ return Some ( a. kind ( ) . cmp ( & b. kind ( ) ) ) ;
1017
+ }
1018
+ (
1019
+ Finite ( mir:: Const :: Val ( mir:: ConstValue :: Scalar ( Scalar :: Int ( a) ) , _) ) ,
1020
+ Finite ( mir:: Const :: Val ( mir:: ConstValue :: Scalar ( Scalar :: Int ( b) ) , _) ) ,
1021
+ ) if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) => return Some ( a. cmp ( & b) ) ,
1022
+ _ => { }
1023
+ }
1024
+
1025
+ let a = self . eval_bits ( ty, tcx, param_env) ;
1026
+ let b = other. eval_bits ( ty, tcx, param_env) ;
1027
+
1028
+ match ty. kind ( ) {
1029
+ ty:: Float ( ty:: FloatTy :: F32 ) => {
1030
+ use rustc_apfloat:: Float ;
1031
+ let a = rustc_apfloat:: ieee:: Single :: from_bits ( a) ;
1032
+ let b = rustc_apfloat:: ieee:: Single :: from_bits ( b) ;
1033
+ a. partial_cmp ( & b)
1034
+ }
1035
+ ty:: Float ( ty:: FloatTy :: F64 ) => {
1036
+ use rustc_apfloat:: Float ;
1037
+ let a = rustc_apfloat:: ieee:: Double :: from_bits ( a) ;
1038
+ let b = rustc_apfloat:: ieee:: Double :: from_bits ( b) ;
1039
+ a. partial_cmp ( & b)
1040
+ }
1041
+ ty:: Int ( ity) => {
1042
+ use rustc_middle:: ty:: layout:: IntegerExt ;
1043
+ let size = rustc_target:: abi:: Integer :: from_int_ty ( & tcx, * ity) . size ( ) ;
1044
+ let a = size. sign_extend ( a) as i128 ;
1045
+ let b = size. sign_extend ( b) as i128 ;
1046
+ Some ( a. cmp ( & b) )
1047
+ }
1048
+ ty:: Uint ( _) | ty:: Char => Some ( a. cmp ( & b) ) ,
1049
+ _ => bug ! ( ) ,
1050
+ }
1051
+ }
819
1052
}
820
1053
821
1054
impl < ' tcx > fmt:: Display for Pat < ' tcx > {
@@ -944,11 +1177,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
944
1177
PatKind :: InlineConstant { def : _, ref subpattern } => {
945
1178
write ! ( f, "{} (from inline const)" , subpattern)
946
1179
}
947
- PatKind :: Range ( box PatRange { lo, hi, end } ) => {
948
- write ! ( f, "{lo}" ) ?;
949
- write ! ( f, "{end}" ) ?;
950
- write ! ( f, "{hi}" )
951
- }
1180
+ PatKind :: Range ( ref range) => write ! ( f, "{range}" ) ,
952
1181
PatKind :: Slice { ref prefix, ref slice, ref suffix }
953
1182
| PatKind :: Array { ref prefix, ref slice, ref suffix } => {
954
1183
write ! ( f, "[" ) ?;
0 commit comments