Skip to content

Commit aaa1b64

Browse files
committed
Propagate half-open ranges through THIR
1 parent 479d493 commit aaa1b64

File tree

2 files changed

+61
-37
lines changed
  • compiler

2 files changed

+61
-37
lines changed

compiler/rustc_middle/src/thir.rs

+45-22
Original file line numberDiff line numberDiff line change
@@ -834,17 +834,21 @@ impl<'tcx> PatRange<'tcx> {
834834
//
835835
// Also, for performance, it's important to only do the second `try_to_bits` if necessary.
836836
let lo_is_min = match self.lo {
837+
PatRangeBoundary::NegInfinity => true,
837838
PatRangeBoundary::Finite(value) => {
838839
let lo = value.try_to_bits(size).unwrap() ^ bias;
839840
lo <= min
840841
}
842+
PatRangeBoundary::PosInfinity => false,
841843
};
842844
if lo_is_min {
843845
let hi_is_max = match self.hi {
846+
PatRangeBoundary::NegInfinity => false,
844847
PatRangeBoundary::Finite(value) => {
845848
let hi = value.try_to_bits(size).unwrap() ^ bias;
846849
hi > max || hi == max && self.end == RangeEnd::Included
847850
}
851+
PatRangeBoundary::PosInfinity => true,
848852
};
849853
if hi_is_max {
850854
return Some(true);
@@ -903,11 +907,13 @@ impl<'tcx> PatRange<'tcx> {
903907

904908
impl<'tcx> fmt::Display for PatRange<'tcx> {
905909
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
906-
let PatRangeBoundary::Finite(value) = &self.lo;
907-
write!(f, "{value}")?;
910+
if let PatRangeBoundary::Finite(value) = &self.lo {
911+
write!(f, "{value}")?;
912+
}
908913
write!(f, "{}", self.end)?;
909-
let PatRangeBoundary::Finite(value) = &self.hi;
910-
write!(f, "{value}")?;
914+
if let PatRangeBoundary::Finite(value) = &self.hi {
915+
write!(f, "{value}")?;
916+
}
911917
Ok(())
912918
}
913919
}
@@ -917,38 +923,49 @@ impl<'tcx> fmt::Display for PatRange<'tcx> {
917923
#[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)]
918924
pub enum PatRangeBoundary<'tcx> {
919925
Finite(mir::Const<'tcx>),
926+
NegInfinity,
927+
PosInfinity,
920928
}
921929

922930
impl<'tcx> PatRangeBoundary<'tcx> {
923931
#[inline]
924-
pub fn lower_bound(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
925-
// Unwrap is ok because the type is known to be numeric.
926-
let c = ty.numeric_min_val(tcx).unwrap();
927-
let value = mir::Const::from_ty_const(c, tcx);
928-
Self::Finite(value)
932+
pub fn is_finite(self) -> bool {
933+
matches!(self, Self::Finite(..))
929934
}
930935
#[inline]
931-
pub fn upper_bound(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
932-
// Unwrap is ok because the type is known to be numeric.
933-
let c = ty.numeric_max_val(tcx).unwrap();
934-
let value = mir::Const::from_ty_const(c, tcx);
935-
Self::Finite(value)
936+
pub fn as_finite(self) -> Option<mir::Const<'tcx>> {
937+
match self {
938+
Self::Finite(value) => Some(value),
939+
Self::NegInfinity | Self::PosInfinity => None,
940+
}
936941
}
937-
938942
#[inline]
939-
pub fn to_const(self, _ty: Ty<'tcx>, _tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
943+
pub fn to_const(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
940944
match self {
941945
Self::Finite(value) => value,
946+
Self::NegInfinity => {
947+
// Unwrap is ok because the type is known to be numeric.
948+
let c = ty.numeric_min_val(tcx).unwrap();
949+
mir::Const::from_ty_const(c, tcx)
950+
}
951+
Self::PosInfinity => {
952+
// Unwrap is ok because the type is known to be numeric.
953+
let c = ty.numeric_max_val(tcx).unwrap();
954+
mir::Const::from_ty_const(c, tcx)
955+
}
942956
}
943957
}
944-
pub fn eval_bits(
945-
self,
946-
_ty: Ty<'tcx>,
947-
tcx: TyCtxt<'tcx>,
948-
param_env: ty::ParamEnv<'tcx>,
949-
) -> u128 {
958+
pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u128 {
950959
match self {
951960
Self::Finite(value) => value.eval_bits(tcx, param_env),
961+
Self::NegInfinity => {
962+
// Unwrap is ok because the type is known to be numeric.
963+
ty.numeric_min_and_max_as_bits(tcx).unwrap().0
964+
}
965+
Self::PosInfinity => {
966+
// Unwrap is ok because the type is known to be numeric.
967+
ty.numeric_min_and_max_as_bits(tcx).unwrap().1
968+
}
952969
}
953970
}
954971

@@ -962,6 +979,12 @@ impl<'tcx> PatRangeBoundary<'tcx> {
962979
) -> Option<Ordering> {
963980
use PatRangeBoundary::*;
964981
match (self, other) {
982+
// When comparing with infinities, we must remember that `0u8..` and `0u8..=255`
983+
// describe the same range. These two shortcuts are ok, but for the rest we must check
984+
// bit values.
985+
(PosInfinity, PosInfinity) => return Some(Ordering::Equal),
986+
(NegInfinity, NegInfinity) => return Some(Ordering::Equal),
987+
965988
// This code is hot when compiling matches with many ranges. So we
966989
// special-case extraction of evaluated scalars for speed, for types where
967990
// raw data comparisons are appropriate. E.g. `unicode-normalization` has

compiler/rustc_mir_build/src/thir/pattern/mod.rs

+16-15
Original file line numberDiff line numberDiff line change
@@ -180,24 +180,25 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
180180
let (lo, lo_ascr) = self.lower_pattern_range_endpoint(lo_expr)?;
181181
let (hi, hi_ascr) = self.lower_pattern_range_endpoint(hi_expr)?;
182182

183-
let lo = lo.unwrap_or_else(|| PatRangeBoundary::lower_bound(ty, self.tcx));
184-
let hi = hi.unwrap_or_else(|| PatRangeBoundary::upper_bound(ty, self.tcx));
183+
let lo = lo.unwrap_or(PatRangeBoundary::NegInfinity);
184+
let hi = hi.unwrap_or(PatRangeBoundary::PosInfinity);
185185

186186
let cmp = lo.compare_with(hi, ty, self.tcx, self.param_env);
187-
let mut kind = match (end, cmp) {
187+
let mut kind = PatKind::Range(Box::new(PatRange { lo, hi, end, ty }));
188+
match (end, cmp) {
188189
// `x..y` where `x < y`.
189-
// Non-empty because the range includes at least `x`.
190-
(RangeEnd::Excluded, Some(Ordering::Less)) => {
191-
PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
192-
}
193-
// `x..=y` where `x == y`.
194-
(RangeEnd::Included, Some(Ordering::Equal)) => {
195-
PatKind::Constant { value: lo.to_const(ty, self.tcx) }
196-
}
190+
(RangeEnd::Excluded, Some(Ordering::Less)) => {}
197191
// `x..=y` where `x < y`.
198-
(RangeEnd::Included, Some(Ordering::Less)) => {
199-
PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
200-
}
192+
(RangeEnd::Included, Some(Ordering::Less)) => {}
193+
// `x..=y` where `x == y` and `x` and `y` are finite.
194+
(RangeEnd::Included, Some(Ordering::Equal)) if lo.is_finite() && hi.is_finite() => {
195+
kind = PatKind::Constant { value: lo.as_finite().unwrap() };
196+
}
197+
// `..=x` where `x == ty::MIN`.
198+
(RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {}
199+
// `x..` where `x == ty::MAX` (yes, `x..` gives `RangeEnd::Included` since it is meant
200+
// to include `ty::MAX`).
201+
(RangeEnd::Included, Some(Ordering::Equal)) if !hi.is_finite() => {}
201202
// `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
202203
_ => {
203204
// Emit a more appropriate message if there was overflow.
@@ -216,7 +217,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
216217
};
217218
return Err(e);
218219
}
219-
};
220+
}
220221

221222
// If we are handling a range with associated constants (e.g.
222223
// `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated

0 commit comments

Comments
 (0)