@@ -19,13 +19,15 @@ use rustc_middle::middle::region;
1919use rustc_middle:: mir:: interpret:: AllocId ;
2020use rustc_middle:: mir:: { self , BinOp , BorrowKind , FakeReadCause , Mutability , UnOp } ;
2121use rustc_middle:: ty:: adjustment:: PointerCoercion ;
22+ use rustc_middle:: ty:: layout:: IntegerExt ;
2223use 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 } ;
2425use rustc_middle:: ty:: { CanonicalUserType , CanonicalUserTypeAnnotation } ;
2526use rustc_span:: def_id:: LocalDefId ;
2627use rustc_span:: { sym, Span , Symbol , DUMMY_SP } ;
27- use rustc_target:: abi:: { FieldIdx , VariantIdx } ;
28+ use rustc_target:: abi:: { FieldIdx , Integer , Size , VariantIdx } ;
2829use rustc_target:: asm:: InlineAsmRegOrRegClass ;
30+ use std:: cmp:: Ordering ;
2931use std:: fmt;
3032use std:: ops:: Index ;
3133
@@ -773,12 +775,213 @@ pub enum PatKind<'tcx> {
773775 } ,
774776}
775777
778+ /// A range pattern.
779+ /// The boundaries must be of the same type and that type must be numeric.
776780#[ derive( Clone , Debug , PartialEq , HashStable , TypeVisitable ) ]
777781pub 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 > ,
780784 #[ type_visitable( ignore) ]
781785 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+ }
957+
958+ let a = self . eval_bits ( ty, tcx, param_env) ;
959+ let b = other. eval_bits ( ty, tcx, param_env) ;
960+
961+ match ty. kind ( ) {
962+ ty:: Float ( ty:: FloatTy :: F32 ) => {
963+ use rustc_apfloat:: Float ;
964+ let a = rustc_apfloat:: ieee:: Single :: from_bits ( a) ;
965+ let b = rustc_apfloat:: ieee:: Single :: from_bits ( b) ;
966+ a. partial_cmp ( & b)
967+ }
968+ ty:: Float ( ty:: FloatTy :: F64 ) => {
969+ use rustc_apfloat:: Float ;
970+ let a = rustc_apfloat:: ieee:: Double :: from_bits ( a) ;
971+ let b = rustc_apfloat:: ieee:: Double :: from_bits ( b) ;
972+ a. partial_cmp ( & b)
973+ }
974+ ty:: Int ( ity) => {
975+ use rustc_middle:: ty:: layout:: IntegerExt ;
976+ let size = rustc_target:: abi:: Integer :: from_int_ty ( & tcx, * ity) . size ( ) ;
977+ let a = size. sign_extend ( a) as i128 ;
978+ let b = size. sign_extend ( b) as i128 ;
979+ Some ( a. cmp ( & b) )
980+ }
981+ ty:: Uint ( _) | ty:: Char => Some ( a. cmp ( & b) ) ,
982+ _ => bug ! ( ) ,
983+ }
984+ }
782985}
783986
784987impl < ' tcx > fmt:: Display for Pat < ' tcx > {
@@ -904,11 +1107,7 @@ impl<'tcx> fmt::Display for Pat<'tcx> {
9041107 write ! ( f, "{subpattern}" )
9051108 }
9061109 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- }
1110+ PatKind :: Range ( ref range) => write ! ( f, "{range}" ) ,
9121111 PatKind :: Slice { ref prefix, ref slice, ref suffix }
9131112 | PatKind :: Array { ref prefix, ref slice, ref suffix } => {
9141113 write ! ( f, "[" ) ?;
0 commit comments