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