@@ -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,238 @@ 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 > , 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
+ }
782
1010
}
783
1011
784
1012
impl < ' tcx > fmt:: Display for Pat < ' tcx > {
@@ -904,11 +1132,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
904
1132
write ! ( f, "{subpattern}" )
905
1133
}
906
1134
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}" ) ,
912
1136
PatKind :: Slice { ref prefix, ref slice, ref suffix }
913
1137
| PatKind :: Array { ref prefix, ref slice, ref suffix } => {
914
1138
write ! ( f, "[" ) ?;
0 commit comments