@@ -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,217 @@ 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 :: Finite ( value) => {
818
+ let lo = value. try_to_bits ( size) . unwrap ( ) ^ bias;
819
+ lo <= min
820
+ }
821
+ } ;
822
+ if lo_is_min {
823
+ let hi_is_max = match self . hi {
824
+ PatRangeBoundary :: Finite ( value) => {
825
+ let hi = value. try_to_bits ( size) . unwrap ( ) ^ bias;
826
+ hi > max || hi == max && self . end == RangeEnd :: Included
827
+ }
828
+ } ;
829
+ if hi_is_max {
830
+ return Some ( true ) ;
831
+ }
832
+ }
833
+ Some ( false )
834
+ }
835
+
836
+ #[ inline]
837
+ pub fn contains (
838
+ & self ,
839
+ value : mir:: Const < ' tcx > ,
840
+ tcx : TyCtxt < ' tcx > ,
841
+ param_env : ty:: ParamEnv < ' tcx > ,
842
+ ) -> Option < bool > {
843
+ use Ordering :: * ;
844
+ debug_assert_eq ! ( self . ty, value. ty( ) ) ;
845
+ let ty = self . ty ;
846
+ let value = PatRangeBoundary :: Finite ( value) ;
847
+ // For performance, it's important to only do the second comparison if necessary.
848
+ Some (
849
+ match self . lo . compare_with ( value, ty, tcx, param_env) ? {
850
+ Less | Equal => true ,
851
+ Greater => false ,
852
+ } && match value. compare_with ( self . hi , ty, tcx, param_env) ? {
853
+ Less => true ,
854
+ Equal => self . end == RangeEnd :: Included ,
855
+ Greater => false ,
856
+ } ,
857
+ )
858
+ }
859
+
860
+ #[ inline]
861
+ pub fn overlaps (
862
+ & self ,
863
+ other : & Self ,
864
+ tcx : TyCtxt < ' tcx > ,
865
+ param_env : ty:: ParamEnv < ' tcx > ,
866
+ ) -> Option < bool > {
867
+ use Ordering :: * ;
868
+ debug_assert_eq ! ( self . ty, other. ty) ;
869
+ // For performance, it's important to only do the second comparison if necessary.
870
+ Some (
871
+ match other. lo . compare_with ( self . hi , self . ty , tcx, param_env) ? {
872
+ Less => true ,
873
+ Equal => self . end == RangeEnd :: Included ,
874
+ Greater => false ,
875
+ } && match self . lo . compare_with ( other. hi , self . ty , tcx, param_env) ? {
876
+ Less => true ,
877
+ Equal => other. end == RangeEnd :: Included ,
878
+ Greater => false ,
879
+ } ,
880
+ )
881
+ }
882
+ }
883
+
884
+ impl < ' tcx > fmt:: Display for PatRange < ' tcx > {
885
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
886
+ let PatRangeBoundary :: Finite ( value) = & self . lo ;
887
+ write ! ( f, "{value}" ) ?;
888
+ write ! ( f, "{}" , self . end) ?;
889
+ let PatRangeBoundary :: Finite ( value) = & self . hi ;
890
+ write ! ( f, "{value}" ) ?;
891
+ Ok ( ( ) )
892
+ }
893
+ }
894
+
895
+ /// A (possibly open) boundary of a range pattern.
896
+ /// If present, the const must be of a numeric type.
897
+ #[ derive( Copy , Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
898
+ pub enum PatRangeBoundary < ' tcx > {
899
+ Finite ( mir:: Const < ' tcx > ) ,
900
+ }
901
+
902
+ impl < ' tcx > PatRangeBoundary < ' tcx > {
903
+ #[ inline]
904
+ pub fn lower_bound ( ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
905
+ // Unwrap is ok because the type is known to be numeric.
906
+ let c = ty. numeric_min_val ( tcx) . unwrap ( ) ;
907
+ let value = mir:: Const :: from_ty_const ( c, tcx) ;
908
+ Self :: Finite ( value)
909
+ }
910
+ #[ inline]
911
+ pub fn upper_bound ( ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
912
+ // Unwrap is ok because the type is known to be numeric.
913
+ let c = ty. numeric_max_val ( tcx) . unwrap ( ) ;
914
+ let value = mir:: Const :: from_ty_const ( c, tcx) ;
915
+ Self :: Finite ( value)
916
+ }
917
+
918
+ #[ inline]
919
+ pub fn to_const ( self , _ty : Ty < ' tcx > , _tcx : TyCtxt < ' tcx > ) -> mir:: Const < ' tcx > {
920
+ match self {
921
+ Self :: Finite ( value) => value,
922
+ }
923
+ }
924
+ pub fn eval_bits (
925
+ self ,
926
+ _ty : Ty < ' tcx > ,
927
+ tcx : TyCtxt < ' tcx > ,
928
+ param_env : ty:: ParamEnv < ' tcx > ,
929
+ ) -> u128 {
930
+ match self {
931
+ Self :: Finite ( value) => value. eval_bits ( tcx, param_env) ,
932
+ }
933
+ }
934
+
935
+ #[ instrument( skip( tcx, param_env) , level = "debug" , ret) ]
936
+ pub fn compare_with (
937
+ self ,
938
+ other : Self ,
939
+ ty : Ty < ' tcx > ,
940
+ tcx : TyCtxt < ' tcx > ,
941
+ param_env : ty:: ParamEnv < ' tcx > ,
942
+ ) -> Option < Ordering > {
943
+ use PatRangeBoundary :: * ;
944
+ match ( self , other) {
945
+ // This code is hot when compiling matches with many ranges. So we
946
+ // special-case extraction of evaluated scalars for speed, for types where
947
+ // raw data comparisons are appropriate. E.g. `unicode-normalization` has
948
+ // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
949
+ // in this way.
950
+ ( Finite ( mir:: Const :: Ty ( a) ) , Finite ( mir:: Const :: Ty ( b) ) )
951
+ if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) =>
952
+ {
953
+ return Some ( a. kind ( ) . cmp ( & b. kind ( ) ) ) ;
954
+ }
955
+ (
956
+ Finite ( mir:: Const :: Val ( mir:: ConstValue :: Scalar ( Scalar :: Int ( a) ) , _) ) ,
957
+ Finite ( mir:: Const :: Val ( mir:: ConstValue :: Scalar ( Scalar :: Int ( b) ) , _) ) ,
958
+ ) if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) => return Some ( a. cmp ( & b) ) ,
959
+ _ => { }
960
+ }
961
+
962
+ let a = self . eval_bits ( ty, tcx, param_env) ;
963
+ let b = other. eval_bits ( ty, tcx, param_env) ;
964
+
965
+ match ty. kind ( ) {
966
+ ty:: Float ( ty:: FloatTy :: F32 ) => {
967
+ use rustc_apfloat:: Float ;
968
+ let a = rustc_apfloat:: ieee:: Single :: from_bits ( a) ;
969
+ let b = rustc_apfloat:: ieee:: Single :: from_bits ( b) ;
970
+ a. partial_cmp ( & b)
971
+ }
972
+ ty:: Float ( ty:: FloatTy :: F64 ) => {
973
+ use rustc_apfloat:: Float ;
974
+ let a = rustc_apfloat:: ieee:: Double :: from_bits ( a) ;
975
+ let b = rustc_apfloat:: ieee:: Double :: from_bits ( b) ;
976
+ a. partial_cmp ( & b)
977
+ }
978
+ ty:: Int ( ity) => {
979
+ use rustc_middle:: ty:: layout:: IntegerExt ;
980
+ let size = rustc_target:: abi:: Integer :: from_int_ty ( & tcx, * ity) . size ( ) ;
981
+ let a = size. sign_extend ( a) as i128 ;
982
+ let b = size. sign_extend ( b) as i128 ;
983
+ Some ( a. cmp ( & b) )
984
+ }
985
+ ty:: Uint ( _) | ty:: Char => Some ( a. cmp ( & b) ) ,
986
+ _ => bug ! ( ) ,
987
+ }
988
+ }
782
989
}
783
990
784
991
impl < ' tcx > fmt:: Display for Pat < ' tcx > {
@@ -904,11 +1111,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
904
1111
write ! ( f, "{subpattern}" )
905
1112
}
906
1113
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
- }
1114
+ PatKind :: Range ( ref range) => write ! ( f, "{range}" ) ,
912
1115
PatKind :: Slice { ref prefix, ref slice, ref suffix }
913
1116
| PatKind :: Array { ref prefix, ref slice, ref suffix } => {
914
1117
write ! ( f, "[" ) ?;
0 commit comments