Skip to content

Commit 0ba6c4a

Browse files
committed
Propagate half-open ranges through THIR
1 parent 8a77b32 commit 0ba6c4a

File tree

2 files changed

+65
-38
lines changed
  • compiler

2 files changed

+65
-38
lines changed

compiler/rustc_middle/src/thir.rs

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -851,17 +851,21 @@ impl<'tcx> PatRange<'tcx> {
851851
//
852852
// Also, for performance, it's important to only do the second `try_to_bits` if necessary.
853853
let lo_is_min = match self.lo {
854+
PatRangeBoundary::NegInfinity => true,
854855
PatRangeBoundary::Finite(value) => {
855856
let lo = value.try_to_bits(size).unwrap() ^ bias;
856857
lo <= min
857858
}
859+
PatRangeBoundary::PosInfinity => false,
858860
};
859861
if lo_is_min {
860862
let hi_is_max = match self.hi {
863+
PatRangeBoundary::NegInfinity => false,
861864
PatRangeBoundary::Finite(value) => {
862865
let hi = value.try_to_bits(size).unwrap() ^ bias;
863866
hi > max || hi == max && self.end == RangeEnd::Included
864867
}
868+
PatRangeBoundary::PosInfinity => true,
865869
};
866870
if hi_is_max {
867871
return Some(true);
@@ -920,11 +924,16 @@ impl<'tcx> PatRange<'tcx> {
920924

921925
impl<'tcx> fmt::Display for PatRange<'tcx> {
922926
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}")?;
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+
}
928937
Ok(())
929938
}
930939
}
@@ -934,38 +943,49 @@ impl<'tcx> fmt::Display for PatRange<'tcx> {
934943
#[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)]
935944
pub enum PatRangeBoundary<'tcx> {
936945
Finite(mir::Const<'tcx>),
946+
NegInfinity,
947+
PosInfinity,
937948
}
938949

939950
impl<'tcx> PatRangeBoundary<'tcx> {
940951
#[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)
952+
pub fn is_finite(self) -> bool {
953+
matches!(self, Self::Finite(..))
946954
}
947955
#[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)
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+
}
953961
}
954-
955962
#[inline]
956-
pub fn to_const(self, _ty: Ty<'tcx>, _tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
963+
pub fn to_const(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
957964
match self {
958965
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+
}
959976
}
960977
}
961-
pub fn eval_bits(
962-
self,
963-
_ty: Ty<'tcx>,
964-
tcx: TyCtxt<'tcx>,
965-
param_env: ty::ParamEnv<'tcx>,
966-
) -> u128 {
978+
pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u128 {
967979
match self {
968980
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+
}
969989
}
970990
}
971991

@@ -979,6 +999,12 @@ impl<'tcx> PatRangeBoundary<'tcx> {
979999
) -> Option<Ordering> {
9801000
use PatRangeBoundary::*;
9811001
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+
9821008
// This code is hot when compiling matches with many ranges. So we
9831009
// special-case extraction of evaluated scalars for speed, for types where
9841010
// raw data comparisons are appropriate. E.g. `unicode-normalization` has

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

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -187,24 +187,25 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
187187
let (lo, lo_ascr, lo_inline) = self.lower_pattern_range_endpoint(lo_expr)?;
188188
let (hi, hi_ascr, hi_inline) = self.lower_pattern_range_endpoint(hi_expr)?;
189189

190-
let lo = lo.unwrap_or_else(|| PatRangeBoundary::lower_bound(ty, self.tcx));
191-
let hi = hi.unwrap_or_else(|| PatRangeBoundary::upper_bound(ty, self.tcx));
190+
let lo = lo.unwrap_or(PatRangeBoundary::NegInfinity);
191+
let hi = hi.unwrap_or(PatRangeBoundary::PosInfinity);
192192

193193
let cmp = lo.compare_with(hi, ty, self.tcx, self.param_env);
194-
let mut kind = match (end, cmp) {
194+
let mut kind = PatKind::Range(Box::new(PatRange { lo, hi, end, ty }));
195+
match (end, cmp) {
195196
// `x..y` where `x < y`.
196-
// Non-empty because the range includes at least `x`.
197-
(RangeEnd::Excluded, Some(Ordering::Less)) => {
198-
PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
199-
}
200-
// `x..=y` where `x == y`.
201-
(RangeEnd::Included, Some(Ordering::Equal)) => {
202-
PatKind::Constant { value: lo.to_const(ty, self.tcx) }
203-
}
197+
(RangeEnd::Excluded, Some(Ordering::Less)) => {}
204198
// `x..=y` where `x < y`.
205-
(RangeEnd::Included, Some(Ordering::Less)) => {
206-
PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
207-
}
199+
(RangeEnd::Included, Some(Ordering::Less)) => {}
200+
// `x..=y` where `x == y` and `x` and `y` are finite.
201+
(RangeEnd::Included, Some(Ordering::Equal)) if lo.is_finite() && hi.is_finite() => {
202+
kind = PatKind::Constant { value: lo.as_finite().unwrap() };
203+
}
204+
// `..=x` where `x == ty::MIN`.
205+
(RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {}
206+
// `x..` where `x == ty::MAX` (yes, `x..` gives `RangeEnd::Included` since it is meant
207+
// to include `ty::MAX`).
208+
(RangeEnd::Included, Some(Ordering::Equal)) if !hi.is_finite() => {}
208209
// `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
209210
_ => {
210211
// Emit a more appropriate message if there was overflow.
@@ -223,7 +224,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
223224
};
224225
return Err(e);
225226
}
226-
};
227+
}
227228

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

0 commit comments

Comments
 (0)