mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-26 08:44:35 +00:00
Propagate half-open ranges through THIR
This commit is contained in:
parent
8a77b3248f
commit
0ba6c4ab67
@ -851,17 +851,21 @@ impl<'tcx> PatRange<'tcx> {
|
|||||||
//
|
//
|
||||||
// Also, for performance, it's important to only do the second `try_to_bits` if necessary.
|
// Also, for performance, it's important to only do the second `try_to_bits` if necessary.
|
||||||
let lo_is_min = match self.lo {
|
let lo_is_min = match self.lo {
|
||||||
|
PatRangeBoundary::NegInfinity => true,
|
||||||
PatRangeBoundary::Finite(value) => {
|
PatRangeBoundary::Finite(value) => {
|
||||||
let lo = value.try_to_bits(size).unwrap() ^ bias;
|
let lo = value.try_to_bits(size).unwrap() ^ bias;
|
||||||
lo <= min
|
lo <= min
|
||||||
}
|
}
|
||||||
|
PatRangeBoundary::PosInfinity => false,
|
||||||
};
|
};
|
||||||
if lo_is_min {
|
if lo_is_min {
|
||||||
let hi_is_max = match self.hi {
|
let hi_is_max = match self.hi {
|
||||||
|
PatRangeBoundary::NegInfinity => false,
|
||||||
PatRangeBoundary::Finite(value) => {
|
PatRangeBoundary::Finite(value) => {
|
||||||
let hi = value.try_to_bits(size).unwrap() ^ bias;
|
let hi = value.try_to_bits(size).unwrap() ^ bias;
|
||||||
hi > max || hi == max && self.end == RangeEnd::Included
|
hi > max || hi == max && self.end == RangeEnd::Included
|
||||||
}
|
}
|
||||||
|
PatRangeBoundary::PosInfinity => true,
|
||||||
};
|
};
|
||||||
if hi_is_max {
|
if hi_is_max {
|
||||||
return Some(true);
|
return Some(true);
|
||||||
@ -920,11 +924,16 @@ impl<'tcx> PatRange<'tcx> {
|
|||||||
|
|
||||||
impl<'tcx> fmt::Display for PatRange<'tcx> {
|
impl<'tcx> fmt::Display for PatRange<'tcx> {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let PatRangeBoundary::Finite(value) = &self.lo;
|
if let PatRangeBoundary::Finite(value) = &self.lo {
|
||||||
write!(f, "{value}")?;
|
write!(f, "{value}")?;
|
||||||
write!(f, "{}", self.end)?;
|
}
|
||||||
let PatRangeBoundary::Finite(value) = &self.hi;
|
if let PatRangeBoundary::Finite(value) = &self.hi {
|
||||||
write!(f, "{value}")?;
|
write!(f, "{}", self.end)?;
|
||||||
|
write!(f, "{value}")?;
|
||||||
|
} else {
|
||||||
|
// `0..` is parsed as an inclusive range, we must display it correctly.
|
||||||
|
write!(f, "..")?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -934,38 +943,49 @@ impl<'tcx> fmt::Display for PatRange<'tcx> {
|
|||||||
#[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)]
|
#[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)]
|
||||||
pub enum PatRangeBoundary<'tcx> {
|
pub enum PatRangeBoundary<'tcx> {
|
||||||
Finite(mir::Const<'tcx>),
|
Finite(mir::Const<'tcx>),
|
||||||
|
NegInfinity,
|
||||||
|
PosInfinity,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> PatRangeBoundary<'tcx> {
|
impl<'tcx> PatRangeBoundary<'tcx> {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn lower_bound(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
|
pub fn is_finite(self) -> bool {
|
||||||
// Unwrap is ok because the type is known to be numeric.
|
matches!(self, Self::Finite(..))
|
||||||
let c = ty.numeric_min_val(tcx).unwrap();
|
|
||||||
let value = mir::Const::from_ty_const(c, tcx);
|
|
||||||
Self::Finite(value)
|
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn upper_bound(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
|
pub fn as_finite(self) -> Option<mir::Const<'tcx>> {
|
||||||
// Unwrap is ok because the type is known to be numeric.
|
|
||||||
let c = ty.numeric_max_val(tcx).unwrap();
|
|
||||||
let value = mir::Const::from_ty_const(c, tcx);
|
|
||||||
Self::Finite(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn to_const(self, _ty: Ty<'tcx>, _tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
|
|
||||||
match self {
|
match self {
|
||||||
Self::Finite(value) => value,
|
Self::Finite(value) => Some(value),
|
||||||
|
Self::NegInfinity | Self::PosInfinity => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn eval_bits(
|
#[inline]
|
||||||
self,
|
pub fn to_const(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> mir::Const<'tcx> {
|
||||||
_ty: Ty<'tcx>,
|
match self {
|
||||||
tcx: TyCtxt<'tcx>,
|
Self::Finite(value) => value,
|
||||||
param_env: ty::ParamEnv<'tcx>,
|
Self::NegInfinity => {
|
||||||
) -> u128 {
|
// Unwrap is ok because the type is known to be numeric.
|
||||||
|
let c = ty.numeric_min_val(tcx).unwrap();
|
||||||
|
mir::Const::from_ty_const(c, tcx)
|
||||||
|
}
|
||||||
|
Self::PosInfinity => {
|
||||||
|
// Unwrap is ok because the type is known to be numeric.
|
||||||
|
let c = ty.numeric_max_val(tcx).unwrap();
|
||||||
|
mir::Const::from_ty_const(c, tcx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> u128 {
|
||||||
match self {
|
match self {
|
||||||
Self::Finite(value) => value.eval_bits(tcx, param_env),
|
Self::Finite(value) => value.eval_bits(tcx, param_env),
|
||||||
|
Self::NegInfinity => {
|
||||||
|
// Unwrap is ok because the type is known to be numeric.
|
||||||
|
ty.numeric_min_and_max_as_bits(tcx).unwrap().0
|
||||||
|
}
|
||||||
|
Self::PosInfinity => {
|
||||||
|
// Unwrap is ok because the type is known to be numeric.
|
||||||
|
ty.numeric_min_and_max_as_bits(tcx).unwrap().1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -979,6 +999,12 @@ impl<'tcx> PatRangeBoundary<'tcx> {
|
|||||||
) -> Option<Ordering> {
|
) -> Option<Ordering> {
|
||||||
use PatRangeBoundary::*;
|
use PatRangeBoundary::*;
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
// When comparing with infinities, we must remember that `0u8..` and `0u8..=255`
|
||||||
|
// describe the same range. These two shortcuts are ok, but for the rest we must check
|
||||||
|
// bit values.
|
||||||
|
(PosInfinity, PosInfinity) => return Some(Ordering::Equal),
|
||||||
|
(NegInfinity, NegInfinity) => return Some(Ordering::Equal),
|
||||||
|
|
||||||
// This code is hot when compiling matches with many ranges. So we
|
// This code is hot when compiling matches with many ranges. So we
|
||||||
// special-case extraction of evaluated scalars for speed, for types where
|
// special-case extraction of evaluated scalars for speed, for types where
|
||||||
// raw data comparisons are appropriate. E.g. `unicode-normalization` has
|
// raw data comparisons are appropriate. E.g. `unicode-normalization` has
|
||||||
|
@ -187,24 +187,25 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||||||
let (lo, lo_ascr, lo_inline) = self.lower_pattern_range_endpoint(lo_expr)?;
|
let (lo, lo_ascr, lo_inline) = self.lower_pattern_range_endpoint(lo_expr)?;
|
||||||
let (hi, hi_ascr, hi_inline) = self.lower_pattern_range_endpoint(hi_expr)?;
|
let (hi, hi_ascr, hi_inline) = self.lower_pattern_range_endpoint(hi_expr)?;
|
||||||
|
|
||||||
let lo = lo.unwrap_or_else(|| PatRangeBoundary::lower_bound(ty, self.tcx));
|
let lo = lo.unwrap_or(PatRangeBoundary::NegInfinity);
|
||||||
let hi = hi.unwrap_or_else(|| PatRangeBoundary::upper_bound(ty, self.tcx));
|
let hi = hi.unwrap_or(PatRangeBoundary::PosInfinity);
|
||||||
|
|
||||||
let cmp = lo.compare_with(hi, ty, self.tcx, self.param_env);
|
let cmp = lo.compare_with(hi, ty, self.tcx, self.param_env);
|
||||||
let mut kind = match (end, cmp) {
|
let mut kind = PatKind::Range(Box::new(PatRange { lo, hi, end, ty }));
|
||||||
|
match (end, cmp) {
|
||||||
// `x..y` where `x < y`.
|
// `x..y` where `x < y`.
|
||||||
// Non-empty because the range includes at least `x`.
|
(RangeEnd::Excluded, Some(Ordering::Less)) => {}
|
||||||
(RangeEnd::Excluded, Some(Ordering::Less)) => {
|
|
||||||
PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
|
|
||||||
}
|
|
||||||
// `x..=y` where `x == y`.
|
|
||||||
(RangeEnd::Included, Some(Ordering::Equal)) => {
|
|
||||||
PatKind::Constant { value: lo.to_const(ty, self.tcx) }
|
|
||||||
}
|
|
||||||
// `x..=y` where `x < y`.
|
// `x..=y` where `x < y`.
|
||||||
(RangeEnd::Included, Some(Ordering::Less)) => {
|
(RangeEnd::Included, Some(Ordering::Less)) => {}
|
||||||
PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
|
// `x..=y` where `x == y` and `x` and `y` are finite.
|
||||||
|
(RangeEnd::Included, Some(Ordering::Equal)) if lo.is_finite() && hi.is_finite() => {
|
||||||
|
kind = PatKind::Constant { value: lo.as_finite().unwrap() };
|
||||||
}
|
}
|
||||||
|
// `..=x` where `x == ty::MIN`.
|
||||||
|
(RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {}
|
||||||
|
// `x..` where `x == ty::MAX` (yes, `x..` gives `RangeEnd::Included` since it is meant
|
||||||
|
// to include `ty::MAX`).
|
||||||
|
(RangeEnd::Included, Some(Ordering::Equal)) if !hi.is_finite() => {}
|
||||||
// `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
|
// `x..y` where `x >= y`, or `x..=y` where `x > y`. The range is empty => error.
|
||||||
_ => {
|
_ => {
|
||||||
// Emit a more appropriate message if there was overflow.
|
// Emit a more appropriate message if there was overflow.
|
||||||
@ -223,7 +224,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||||||
};
|
};
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// If we are handling a range with associated constants (e.g.
|
// If we are handling a range with associated constants (e.g.
|
||||||
// `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
|
// `Foo::<'a>::A..=Foo::B`), we need to put the ascriptions for the associated
|
||||||
|
Loading…
Reference in New Issue
Block a user