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