@@ -19,13 +19,15 @@ use rustc_middle::middle::region;
19
19
use rustc_middle:: mir:: interpret:: AllocId ;
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,240 @@ 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
+ #[ allow( irrefutable_let_patterns) ]
879
+ if let PatRangeBoundary :: Finite { value, .. } = & self . lo {
880
+ write ! ( f, "{value}" ) ?;
881
+ }
882
+ write ! ( f, "{}" , self . end) ?;
883
+ #[ allow( irrefutable_let_patterns) ]
884
+ if let PatRangeBoundary :: Finite { value, .. } = & self . hi {
885
+ write ! ( f, "{value}" ) ?;
886
+ }
887
+ Ok ( ( ) )
888
+ }
889
+ }
890
+
891
+ /// A (possibly open) boundary of a range pattern.
892
+ /// If present, the const must be of a numeric type.
893
+ #[ derive( Copy , Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
894
+ pub enum PatRangeBoundary < ' tcx > {
895
+ Finite { value : mir:: Const < ' tcx > } ,
896
+ // PosInfinity,
897
+ // NegInfinity,
898
+ }
899
+
900
+ impl < ' tcx > PatRangeBoundary < ' tcx > {
901
+ #[ inline]
902
+ pub fn new_finite (
903
+ value : mir:: Const < ' tcx > ,
904
+ _tcx : TyCtxt < ' tcx > ,
905
+ _param_env : ty:: ParamEnv < ' tcx > ,
906
+ ) -> Self {
907
+ Self :: Finite { value }
908
+ }
909
+ #[ inline]
910
+ pub fn lower_bound ( ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
911
+ // Self::NegInfinity
912
+ // Unwrap is ok because the type is known to be numeric.
913
+ let c = ty. numeric_min_val ( tcx) . unwrap ( ) ;
914
+ let value = mir:: Const :: from_ty_const ( c, tcx) ;
915
+ Self :: Finite { value }
916
+ }
917
+ #[ inline]
918
+ pub fn upper_bound ( ty : Ty < ' tcx > , tcx : TyCtxt < ' tcx > ) -> Self {
919
+ // Self::PosInfinity
920
+ // Unwrap is ok because the type is known to be numeric.
921
+ let c = ty. numeric_max_val ( tcx) . unwrap ( ) ;
922
+ let value = mir:: Const :: from_ty_const ( c, tcx) ;
923
+ Self :: Finite { value }
924
+ }
925
+
926
+ #[ inline]
927
+ pub fn to_const (
928
+ self ,
929
+ _ty : Ty < ' tcx > ,
930
+ _tcx : TyCtxt < ' tcx > ,
931
+ _param_env : ty:: ParamEnv < ' tcx > ,
932
+ ) -> mir:: Const < ' tcx > {
933
+ match self {
934
+ Self :: Finite { value } => value,
935
+ // Self::PosInfinity | Self::NegInfinity => unreachable!(),
936
+ }
937
+ }
938
+ #[ inline]
939
+ pub fn eval_bits (
940
+ self ,
941
+ _ty : Ty < ' tcx > ,
942
+ tcx : TyCtxt < ' tcx > ,
943
+ param_env : ty:: ParamEnv < ' tcx > ,
944
+ ) -> u128 {
945
+ match self {
946
+ Self :: Finite { value } => value. eval_bits ( tcx, param_env) ,
947
+ // Self::NegInfinity => {
948
+ // // Unwrap is ok because the type is known to be numeric.
949
+ // ty.numeric_min_val_as_bits(tcx).unwrap()
950
+ // }
951
+ // Self::PosInfinity => {
952
+ // // Unwrap is ok because the type is known to be numeric.
953
+ // ty.numeric_max_val_as_bits(tcx).unwrap()
954
+ // }
955
+ }
956
+ }
957
+
958
+ #[ instrument( skip( tcx) , level = "debug" ) ]
959
+ #[ inline]
960
+ pub fn compare_with (
961
+ self ,
962
+ other : Self ,
963
+ ty : Ty < ' tcx > ,
964
+ tcx : TyCtxt < ' tcx > ,
965
+ param_env : ty:: ParamEnv < ' tcx > ,
966
+ ) -> Option < Ordering > {
967
+ use PatRangeBoundary :: * ;
968
+ match ( self , other) {
969
+ // (PosInfinity, PosInfinity) => return Some(Ordering::Equal),
970
+ // (NegInfinity, NegInfinity) => return Some(Ordering::Equal),
971
+
972
+ // This code is hot when compiling matches with many ranges. So we
973
+ // special-case extraction of evaluated scalars for speed, for types where
974
+ // raw data comparisons are appropriate. E.g. `unicode-normalization` has
975
+ // many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
976
+ // in this way.
977
+ ( Finite { value : mir:: Const :: Ty ( a) } , Finite { value : mir:: Const :: Ty ( b) } )
978
+ if matches ! ( ty. kind( ) , ty:: Uint ( _) | ty:: Char ) =>
979
+ {
980
+ return Some ( a. kind ( ) . cmp ( & b. kind ( ) ) ) ;
981
+ }
982
+ _ => { }
983
+ }
984
+
985
+ let a = self . eval_bits ( ty, tcx, param_env) ;
986
+ let b = other. eval_bits ( ty, tcx, param_env) ;
987
+
988
+ match ty. kind ( ) {
989
+ ty:: Float ( ty:: FloatTy :: F32 ) => {
990
+ use rustc_apfloat:: Float ;
991
+ let a = rustc_apfloat:: ieee:: Single :: from_bits ( a) ;
992
+ let b = rustc_apfloat:: ieee:: Single :: from_bits ( b) ;
993
+ a. partial_cmp ( & b)
994
+ }
995
+ ty:: Float ( ty:: FloatTy :: F64 ) => {
996
+ use rustc_apfloat:: Float ;
997
+ let a = rustc_apfloat:: ieee:: Double :: from_bits ( a) ;
998
+ let b = rustc_apfloat:: ieee:: Double :: from_bits ( b) ;
999
+ a. partial_cmp ( & b)
1000
+ }
1001
+ ty:: Int ( ity) => {
1002
+ use rustc_middle:: ty:: layout:: IntegerExt ;
1003
+ let size = rustc_target:: abi:: Integer :: from_int_ty ( & tcx, * ity) . size ( ) ;
1004
+ let a = size. sign_extend ( a) as i128 ;
1005
+ let b = size. sign_extend ( b) as i128 ;
1006
+ Some ( a. cmp ( & b) )
1007
+ }
1008
+ ty:: Uint ( _) | ty:: Char => Some ( a. cmp ( & b) ) ,
1009
+ _ => bug ! ( ) ,
1010
+ }
1011
+ }
782
1012
}
783
1013
784
1014
impl < ' tcx > fmt:: Display for Pat < ' tcx > {
@@ -904,11 +1134,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
904
1134
write ! ( f, "{subpattern}" )
905
1135
}
906
1136
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
- }
1137
+ PatKind :: Range ( ref range) => write ! ( f, "{range}" ) ,
912
1138
PatKind :: Slice { ref prefix, ref slice, ref suffix }
913
1139
| PatKind :: Array { ref prefix, ref slice, ref suffix } => {
914
1140
write ! ( f, "[" ) ?;
0 commit comments