@@ -16,16 +16,18 @@ 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:: GenericArgsRef ;
23
- use rustc_middle:: ty:: { self , AdtDef , FnSig , List , Ty , UpvarArgs } ;
24
+ use rustc_middle:: ty:: { self , AdtDef , FnSig , List , Ty , TyCtxt , UpvarArgs } ;
24
25
use rustc_middle:: ty:: { CanonicalUserType , CanonicalUserTypeAnnotation } ;
25
26
use rustc_span:: def_id:: LocalDefId ;
26
27
use rustc_span:: { sym, Span , Symbol , DUMMY_SP } ;
27
- use rustc_target:: abi:: { FieldIdx , VariantIdx } ;
28
+ use rustc_target:: abi:: { FieldIdx , Integer , Size , VariantIdx } ;
28
29
use rustc_target:: asm:: InlineAsmRegOrRegClass ;
30
+ use std:: cmp:: Ordering ;
29
31
use std:: fmt;
30
32
use std:: ops:: Index ;
31
33
@@ -773,12 +775,237 @@ pub enum PatKind<'tcx> {
773
775
} ,
774
776
}
775
777
778
+ /// A range pattern.
779
+ /// The boundaries must be of the same type and that type must be numeric.
776
780
#[ derive( Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
777
781
pub 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 > ,
780
784
#[ type_visitable( ignore) ]
781
785
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
+ }
782
1009
}
783
1010
784
1011
impl < ' tcx > fmt:: Display for Pat < ' tcx > {
@@ -904,11 +1131,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
904
1131
write ! ( f, "{subpattern}" )
905
1132
}
906
1133
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}" ) ,
912
1135
PatKind :: Slice { ref prefix, ref slice, ref suffix }
913
1136
| PatKind :: Array { ref prefix, ref slice, ref suffix } => {
914
1137
write ! ( f, "[" ) ?;
0 commit comments