pattern testing: store constants as valtrees

This commit is contained in:
Ralf Jung
2025-07-28 18:16:47 +02:00
parent 3f1e99dca4
commit d61fdbf266
10 changed files with 106 additions and 120 deletions
+28 -45
View File
@@ -937,8 +937,8 @@ pub fn is_full_range(&self, tcx: TyCtxt<'tcx>) -> Option<bool> {
// Also, for performance, it's important to only do the second `try_to_bits` if necessary.
let lo_is_min = match self.lo {
PatRangeBoundary::NegInfinity => true,
PatRangeBoundary::Finite(value) => {
let lo = value.try_to_bits(size).unwrap() ^ bias;
PatRangeBoundary::Finite(_ty, value) => {
let lo = value.unwrap_leaf().to_bits(size) ^ bias;
lo <= min
}
PatRangeBoundary::PosInfinity => false,
@@ -946,8 +946,8 @@ pub fn is_full_range(&self, tcx: TyCtxt<'tcx>) -> Option<bool> {
if lo_is_min {
let hi_is_max = match self.hi {
PatRangeBoundary::NegInfinity => false,
PatRangeBoundary::Finite(value) => {
let hi = value.try_to_bits(size).unwrap() ^ bias;
PatRangeBoundary::Finite(_ty, value) => {
let hi = value.unwrap_leaf().to_bits(size) ^ bias;
hi > max || hi == max && self.end == RangeEnd::Included
}
PatRangeBoundary::PosInfinity => true,
@@ -960,22 +960,16 @@ pub fn is_full_range(&self, tcx: TyCtxt<'tcx>) -> Option<bool> {
}
#[inline]
pub fn contains(
&self,
value: mir::Const<'tcx>,
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
) -> Option<bool> {
pub fn contains(&self, value: ty::ValTree<'tcx>, tcx: TyCtxt<'tcx>) -> Option<bool> {
use Ordering::*;
debug_assert_eq!(self.ty, value.ty());
let ty = self.ty;
let value = PatRangeBoundary::Finite(value);
let value = PatRangeBoundary::Finite(ty, value);
// For performance, it's important to only do the second comparison if necessary.
Some(
match self.lo.compare_with(value, ty, tcx, typing_env)? {
match self.lo.compare_with(value, ty, tcx)? {
Less | Equal => true,
Greater => false,
} && match value.compare_with(self.hi, ty, tcx, typing_env)? {
} && match value.compare_with(self.hi, ty, tcx)? {
Less => true,
Equal => self.end == RangeEnd::Included,
Greater => false,
@@ -984,21 +978,16 @@ pub fn contains(
}
#[inline]
pub fn overlaps(
&self,
other: &Self,
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
) -> Option<bool> {
pub fn overlaps(&self, other: &Self, tcx: TyCtxt<'tcx>) -> Option<bool> {
use Ordering::*;
debug_assert_eq!(self.ty, other.ty);
// For performance, it's important to only do the second comparison if necessary.
Some(
match other.lo.compare_with(self.hi, self.ty, tcx, typing_env)? {
match other.lo.compare_with(self.hi, self.ty, tcx)? {
Less => true,
Equal => self.end == RangeEnd::Included,
Greater => false,
} && match self.lo.compare_with(other.hi, self.ty, tcx, typing_env)? {
} && match self.lo.compare_with(other.hi, self.ty, tcx)? {
Less => true,
Equal => other.end == RangeEnd::Included,
Greater => false,
@@ -1009,10 +998,13 @@ pub fn overlaps(
impl<'tcx> fmt::Display for PatRange<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let PatRangeBoundary::Finite(value) = &self.lo {
if let &PatRangeBoundary::Finite(ty, value) = &self.lo {
// `ty::Value` has a reasonable pretty-printing implementation.
let value = ty::Value { ty, valtree: value };
write!(f, "{value}")?;
}
if let PatRangeBoundary::Finite(value) = &self.hi {
if let &PatRangeBoundary::Finite(ty, value) = &self.hi {
let value = ty::Value { ty, valtree: value };
write!(f, "{}", self.end)?;
write!(f, "{value}")?;
} else {
@@ -1027,7 +1019,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// If present, the const must be of a numeric type.
#[derive(Copy, Clone, Debug, PartialEq, HashStable, TypeVisitable)]
pub enum PatRangeBoundary<'tcx> {
Finite(mir::Const<'tcx>),
Finite(Ty<'tcx>, ty::ValTree<'tcx>),
NegInfinity,
PosInfinity,
}
@@ -1038,20 +1030,15 @@ pub fn is_finite(self) -> bool {
matches!(self, Self::Finite(..))
}
#[inline]
pub fn as_finite(self) -> Option<mir::Const<'tcx>> {
pub fn as_finite(self) -> Option<ty::ValTree<'tcx>> {
match self {
Self::Finite(value) => Some(value),
Self::Finite(_ty, value) => Some(value),
Self::NegInfinity | Self::PosInfinity => None,
}
}
pub fn eval_bits(
self,
ty: Ty<'tcx>,
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
) -> u128 {
pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> u128 {
match self {
Self::Finite(value) => value.eval_bits(tcx, typing_env),
Self::Finite(_ty, value) => value.unwrap_leaf().to_bits_unchecked(),
Self::NegInfinity => {
// Unwrap is ok because the type is known to be numeric.
ty.numeric_min_and_max_as_bits(tcx).unwrap().0
@@ -1063,14 +1050,8 @@ pub fn eval_bits(
}
}
#[instrument(skip(tcx, typing_env), level = "debug", ret)]
pub fn compare_with(
self,
other: Self,
ty: Ty<'tcx>,
tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>,
) -> Option<Ordering> {
#[instrument(skip(tcx), level = "debug", ret)]
pub fn compare_with(self, other: Self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<Ordering> {
use PatRangeBoundary::*;
match (self, other) {
// When comparing with infinities, we must remember that `0u8..` and `0u8..=255`
@@ -1084,7 +1065,9 @@ pub fn compare_with(
// we can do scalar comparisons. E.g. `unicode-normalization` has
// many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
// in this way.
(Finite(a), Finite(b)) if matches!(ty.kind(), ty::Int(_) | ty::Uint(_) | ty::Char) => {
(Finite(_, a), Finite(_, b))
if matches!(ty.kind(), ty::Int(_) | ty::Uint(_) | ty::Char) =>
{
if let (Some(a), Some(b)) = (a.try_to_scalar_int(), b.try_to_scalar_int()) {
let sz = ty.primitive_size(tcx);
let cmp = match ty.kind() {
@@ -1098,8 +1081,8 @@ pub fn compare_with(
_ => {}
}
let a = self.eval_bits(ty, tcx, typing_env);
let b = other.eval_bits(ty, tcx, typing_env);
let a = self.eval_bits(ty, tcx);
let b = other.eval_bits(ty, tcx);
match ty.kind() {
ty::Float(ty::FloatTy::F16) => {
@@ -2,10 +2,12 @@
use std::ops::Deref;
use rustc_data_structures::intern::Interned;
use rustc_hir::def::Namespace;
use rustc_macros::{HashStable, Lift, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use super::ScalarInt;
use crate::mir::interpret::{ErrorHandled, Scalar};
use crate::ty::print::{FmtPrinter, PrettyPrinter};
use crate::ty::{self, Ty, TyCtxt};
/// This datastructure is used to represent the value of constants used in the type system.
@@ -203,3 +205,14 @@ fn valtree(self) -> ValTree<'tcx> {
self.valtree
}
}
impl<'tcx> fmt::Display for Value<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
ty::tls::with(move |tcx| {
let cv = tcx.lift(*self).unwrap();
let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
p.pretty_print_const_valtree(cv, /*print_ty*/ true)?;
f.write_str(&p.into_buffer())
})
}
}
@@ -12,7 +12,6 @@
use rustc_span::source_map::Spanned;
use rustc_type_ir::{ConstKind, TypeFolder, VisitorResult, try_visit};
use super::print::PrettyPrinter;
use super::{GenericArg, GenericArgKind, Pattern, Region};
use crate::mir::PlaceElem;
use crate::ty::print::{FmtPrinter, Printer, with_no_trimmed_paths};
@@ -168,15 +167,11 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// If this is a value, we spend some effort to make it look nice.
if let ConstKind::Value(cv) = self.kind() {
return ty::tls::with(move |tcx| {
let cv = tcx.lift(cv).unwrap();
let mut p = FmtPrinter::new(tcx, Namespace::ValueNS);
p.pretty_print_const_valtree(cv, /*print_ty*/ true)?;
f.write_str(&p.into_buffer())
});
write!(f, "{}", cv)
} else {
// Fall back to something verbose.
write!(f, "{:?}", self.kind())
}
// Fall back to something verbose.
write!(f, "{:?}", self.kind())
}
}
@@ -146,11 +146,7 @@ pub(super) fn for_pattern(
}
}
PatKind::Constant { ty: value_ty, value } => {
// FIXME: `TestCase::Constant` should probably represent that it is always a `ValTree`.
let value = Const::Ty(value_ty, ty::Const::new_value(cx.tcx, value, value_ty));
Some(TestCase::Constant { value })
}
PatKind::Constant { ty, value } => Some(TestCase::Constant { ty, value }),
PatKind::AscribeUserType {
ascription: Ascription { ref annotation, variance },
@@ -16,7 +16,7 @@
use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node};
use rustc_middle::bug;
use rustc_middle::middle::region;
use rustc_middle::mir::{self, *};
use rustc_middle::mir::*;
use rustc_middle::thir::{self, *};
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};
use rustc_pattern_analysis::constructor::RangeEnd;
@@ -1245,7 +1245,7 @@ struct Ascription<'tcx> {
#[derive(Debug, Clone)]
enum TestCase<'tcx> {
Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx },
Constant { value: mir::Const<'tcx> },
Constant { ty: Ty<'tcx>, value: ty::ValTree<'tcx> },
Range(Arc<PatRange<'tcx>>),
Slice { len: usize, variable_length: bool },
Deref { temp: Place<'tcx>, mutability: Mutability },
@@ -1318,11 +1318,12 @@ enum TestKind<'tcx> {
/// Test for equality with value, possibly after an unsizing coercion to
/// `ty`,
Eq {
value: Const<'tcx>,
value: ty::ValTree<'tcx>,
value_ty: Ty<'tcx>,
// Integer types are handled by `SwitchInt`, and constants with ADT
// types and `&[T]` types are converted back into patterns, so this can
// only be `&str` or `f*`.
ty: Ty<'tcx>,
cast_ty: Ty<'tcx>,
},
/// Test whether the value falls within an inclusive or exclusive range.
@@ -1358,7 +1359,7 @@ enum TestBranch<'tcx> {
/// Success branch, used for tests with two possible outcomes.
Success,
/// Branch corresponding to this constant.
Constant(Const<'tcx>, u128),
Constant(ty::ValTree<'tcx>, u128),
/// Branch corresponding to this variant.
Variant(VariantIdx),
/// Failure branch for tests with two possible outcomes, and "otherwise" branch for other tests.
@@ -1366,8 +1367,8 @@ enum TestBranch<'tcx> {
}
impl<'tcx> TestBranch<'tcx> {
fn as_constant(&self) -> Option<&Const<'tcx>> {
if let Self::Constant(v, _) = self { Some(v) } else { None }
fn as_constant(&self) -> Option<ty::ValTree<'tcx>> {
if let Self::Constant(v, _) = self { Some(*v) } else { None }
}
}
@@ -35,7 +35,9 @@ pub(super) fn pick_test_for_match_pair(
TestCase::Constant { .. } if match_pair.pattern_ty.is_bool() => TestKind::If,
TestCase::Constant { .. } if is_switch_ty(match_pair.pattern_ty) => TestKind::SwitchInt,
TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern_ty },
TestCase::Constant { value, ty: value_ty } => {
TestKind::Eq { value, value_ty, cast_ty: match_pair.pattern_ty }
}
TestCase::Range(ref range) => {
assert_eq!(range.ty, match_pair.pattern_ty);
@@ -135,17 +137,18 @@ pub(super) fn perform_test(
self.cfg.terminate(block, self.source_info(match_start_span), terminator);
}
TestKind::Eq { value, mut ty } => {
TestKind::Eq { value, value_ty, mut cast_ty } => {
let tcx = self.tcx;
let success_block = target_block(TestBranch::Success);
let fail_block = target_block(TestBranch::Failure);
let mut expect_ty = value.ty();
let mut expect_ty = value_ty;
let value = Const::Ty(value_ty, ty::Const::new_value(tcx, value, value_ty));
let mut expect = self.literal_operand(test.span, value);
let mut place = place;
let mut block = block;
match ty.kind() {
match cast_ty.kind() {
ty::Str => {
// String literal patterns may have type `str` if `deref_patterns` is
// enabled, in order to allow `deref!("..."): String`. In this case, `value`
@@ -167,7 +170,7 @@ pub(super) fn perform_test(
Rvalue::Ref(re_erased, BorrowKind::Shared, place),
);
place = ref_place;
ty = ref_str_ty;
cast_ty = ref_str_ty;
}
ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => {
if !tcx.features().string_deref_patterns() {
@@ -186,7 +189,7 @@ pub(super) fn perform_test(
eq_block,
place,
Mutability::Not,
ty,
cast_ty,
ref_str,
test.span,
);
@@ -195,10 +198,10 @@ pub(super) fn perform_test(
// Similarly, the normal test code should be generated for the `&str`, instead of the `String`.
block = eq_block;
place = ref_str;
ty = ref_str_ty;
cast_ty = ref_str_ty;
}
&ty::Pat(base, _) => {
assert_eq!(ty, value.ty());
assert_eq!(cast_ty, value_ty);
assert!(base.is_trivially_pure_clone_copy());
let transmuted_place = self.temp(base, test.span);
@@ -219,14 +222,14 @@ pub(super) fn perform_test(
place = transmuted_place;
expect = Operand::Copy(transmuted_expect);
ty = base;
cast_ty = base;
expect_ty = base;
}
_ => {}
}
assert_eq!(expect_ty, ty);
if !ty.is_scalar() {
assert_eq!(expect_ty, cast_ty);
if !cast_ty.is_scalar() {
// Use `PartialEq::eq` instead of `BinOp::Eq`
// (the binop can only handle primitives)
// Make sure that we do *not* call any user-defined code here.
@@ -234,10 +237,13 @@ pub(super) fn perform_test(
// comparison defined in `core`.
// (Interestingly this means that exhaustiveness analysis relies, for soundness,
// on the `PartialEq` impl for `str` to b correct!)
match *ty.kind() {
match *cast_ty.kind() {
ty::Ref(_, deref_ty, _) if deref_ty == self.tcx.types.str_ => {}
_ => {
span_bug!(source_info.span, "invalid type for non-scalar compare: {ty}")
span_bug!(
source_info.span,
"invalid type for non-scalar compare: {cast_ty}"
)
}
};
self.string_compare(
@@ -276,6 +282,7 @@ pub(super) fn perform_test(
};
if let Some(lo) = range.lo.as_finite() {
let lo = Const::Ty(range.ty, ty::Const::new_value(self.tcx, lo, range.ty));
let lo = self.literal_operand(test.span, lo);
self.compare(
block,
@@ -289,6 +296,7 @@ pub(super) fn perform_test(
};
if let Some(hi) = range.hi.as_finite() {
let hi = Const::Ty(range.ty, ty::Const::new_value(self.tcx, hi, range.ty));
let hi = self.literal_operand(test.span, hi);
let op = match range.end {
RangeEnd::Included => BinOp::Le,
@@ -545,7 +553,7 @@ pub(super) fn sort_candidate(
//
// FIXME(#29623) we could use PatKind::Range to rule
// things out here, in some cases.
(TestKind::SwitchInt, &TestCase::Constant { value })
(TestKind::SwitchInt, &TestCase::Constant { value, .. })
if is_switch_ty(match_pair.pattern_ty) =>
{
// An important invariant of candidate sorting is that a candidate
@@ -555,10 +563,7 @@ pub(super) fn sort_candidate(
// not to add such values here.
let is_covering_range = |test_case: &TestCase<'tcx>| {
test_case.as_range().is_some_and(|range| {
matches!(
range.contains(value, self.tcx, self.typing_env()),
None | Some(true)
)
matches!(range.contains(value, self.tcx), None | Some(true))
})
};
let is_conflicting_candidate = |candidate: &&mut Candidate<'tcx>| {
@@ -575,7 +580,7 @@ pub(super) fn sort_candidate(
None
} else {
fully_matched = true;
let bits = value.eval_bits(self.tcx, self.typing_env());
let bits = value.unwrap_leaf().to_bits_unchecked();
Some(TestBranch::Constant(value, bits))
}
}
@@ -585,12 +590,10 @@ pub(super) fn sort_candidate(
// the values being tested. (This restricts what values can be
// added to the test by subsequent candidates.)
fully_matched = false;
let not_contained =
sorted_candidates.keys().filter_map(|br| br.as_constant()).copied().all(
|val| {
matches!(range.contains(val, self.tcx, self.typing_env()), Some(false))
},
);
let not_contained = sorted_candidates
.keys()
.filter_map(|br| br.as_constant())
.all(|val| matches!(range.contains(val, self.tcx), Some(false)));
not_contained.then(|| {
// No switch values are contained in the pattern range,
@@ -599,9 +602,9 @@ pub(super) fn sort_candidate(
})
}
(TestKind::If, TestCase::Constant { value }) => {
(TestKind::If, TestCase::Constant { value, .. }) => {
fully_matched = true;
let value = value.try_eval_bool(self.tcx, self.typing_env()).unwrap_or_else(|| {
let value = value.unwrap_leaf().try_to_bool().unwrap_or_else(|_| {
span_bug!(test.span, "expected boolean value but got {value:?}")
});
Some(if value { TestBranch::Success } else { TestBranch::Failure })
@@ -681,16 +684,12 @@ pub(super) fn sort_candidate(
fully_matched = false;
// If the testing range does not overlap with pattern range,
// the pattern can be matched only if this test fails.
if !test.overlaps(pat, self.tcx, self.typing_env())? {
Some(TestBranch::Failure)
} else {
None
}
if !test.overlaps(pat, self.tcx)? { Some(TestBranch::Failure) } else { None }
}
}
(TestKind::Range(range), &TestCase::Constant { value }) => {
(TestKind::Range(range), &TestCase::Constant { value, .. }) => {
fully_matched = false;
if !range.contains(value, self.tcx, self.typing_env())? {
if !range.contains(value, self.tcx)? {
// `value` is not contained in the testing range,
// so `value` can be matched only if this test fails.
Some(TestBranch::Failure)
@@ -699,7 +698,7 @@ pub(super) fn sort_candidate(
}
}
(TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val }) => {
(TestKind::Eq { value: test_val, .. }, TestCase::Constant { value: case_val, .. }) => {
if test_val == case_val {
fully_matched = true;
Some(TestBranch::Success)
@@ -21,7 +21,7 @@
use rustc_middle::ty::adjustment::{PatAdjust, PatAdjustment};
use rustc_middle::ty::layout::IntegerExt;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, TyCtxt, TypingMode};
use rustc_middle::{bug, mir, span_bug};
use rustc_middle::{bug, span_bug};
use rustc_span::def_id::DefId;
use rustc_span::{ErrorGuaranteed, Span};
use tracing::{debug, instrument};
@@ -161,11 +161,7 @@ fn lower_pattern_range_endpoint(
format!("found bad range pattern endpoint `{expr:?}` outside of error recovery");
return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg));
};
// FIXME: `Finite` should probably take a `ValTree` or even a `ScalarInt`
// (but it should also be the same type as what `TestCase::Constant` uses, or at least
// easy to convert).
let value = mir::Const::Ty(ty, ty::Const::new_value(self.tcx, value, ty));
Ok(Some(PatRangeBoundary::Finite(value)))
Ok(Some(PatRangeBoundary::Finite(ty, value)))
}
/// Overflowing literals are linted against in a late pass. This is mostly fine, except when we
@@ -238,7 +234,7 @@ fn lower_pattern_range(
let lo = lower_endpoint(lo_expr)?.unwrap_or(PatRangeBoundary::NegInfinity);
let hi = lower_endpoint(hi_expr)?.unwrap_or(PatRangeBoundary::PosInfinity);
let cmp = lo.compare_with(hi, ty, self.tcx, self.typing_env);
let cmp = lo.compare_with(hi, ty, self.tcx);
let mut kind = PatKind::Range(Arc::new(PatRange { lo, hi, end, ty }));
match (end, cmp) {
// `x..y` where `x < y`.
@@ -247,9 +243,7 @@ fn lower_pattern_range(
(RangeEnd::Included, Some(Ordering::Less)) => {}
// `x..=y` where `x == y` and `x` and `y` are finite.
(RangeEnd::Included, Some(Ordering::Equal)) if lo.is_finite() && hi.is_finite() => {
// FIXME: silly conversion because not all pattern stuff uses valtrees yet.
let mir::Const::Ty(ty, val) = lo.as_finite().unwrap() else { unreachable!() };
kind = PatKind::Constant { ty, value: val.to_value().valtree };
kind = PatKind::Constant { ty, value: lo.as_finite().unwrap() };
}
// `..=x` where `x == ty::MIN`.
(RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {}
+2 -1
View File
@@ -761,8 +761,9 @@ fn print_pat_kind(&mut self, pat_kind: &PatKind<'tcx>, depth_lvl: usize) {
self.print_pat(subpattern, depth_lvl + 2);
print_indented!(self, "}", depth_lvl + 1);
}
PatKind::Constant { value, ty: _ } => {
PatKind::Constant { value, ty } => {
print_indented!(self, "Constant {", depth_lvl + 1);
print_indented!(self, format!("ty: {:?}", ty), depth_lvl + 2);
print_indented!(self, format!("value: {:?}", value), depth_lvl + 2);
print_indented!(self, "}", depth_lvl + 1);
}
+10 -8
View File
@@ -429,8 +429,8 @@ pub(crate) fn lower_pat_range_bdy(
) -> MaybeInfiniteInt {
match bdy {
PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity,
PatRangeBoundary::Finite(value) => {
let bits = value.eval_bits(self.tcx, self.typing_env);
PatRangeBoundary::Finite(_ty, value) => {
let bits = value.unwrap_leaf().to_bits_unchecked();
match *ty.kind() {
ty::Int(ity) => {
let size = Integer::from_int_ty(&self.tcx, ity).size().bits();
@@ -614,8 +614,8 @@ pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
}
ty::Float(fty) => {
use rustc_apfloat::Float;
let lo = lo.as_finite().map(|c| c.eval_bits(cx.tcx, cx.typing_env));
let hi = hi.as_finite().map(|c| c.eval_bits(cx.tcx, cx.typing_env));
let lo = lo.as_finite().map(|c| c.unwrap_leaf().to_bits_unchecked());
let hi = hi.as_finite().map(|c| c.unwrap_leaf().to_bits_unchecked());
match fty {
ty::FloatTy::F16 => {
use rustc_apfloat::ieee::Half;
@@ -723,8 +723,8 @@ fn hoist_pat_range_bdy(
};
match ScalarInt::try_from_uint(bits, size) {
Some(scalar) => {
let value = mir::Const::from_scalar(tcx, scalar.into(), ty.inner());
PatRangeBoundary::Finite(value)
let value = ty::ValTree::from_scalar_int(tcx, scalar);
PatRangeBoundary::Finite(ty.inner(), value)
}
// The value doesn't fit. Since `x >= 0` and 0 always encodes the minimum value
// for a type, the problem isn't that the value is too small. So it must be too
@@ -745,7 +745,7 @@ fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String {
} else if range.is_singleton() {
let lo = cx.hoist_pat_range_bdy(range.lo, ty);
let value = lo.as_finite().unwrap();
value.to_string()
ty::Value { ty: ty.inner(), valtree: value }.to_string()
} else {
// We convert to an inclusive range for diagnostics.
let mut end = rustc_hir::RangeEnd::Included;
@@ -756,7 +756,9 @@ fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String {
// fictitious values after `{u,i}size::MAX` (see [`IntRange::split`] for why we do
// this). We show this to the user as `usize::MAX..` which is slightly incorrect but
// probably clear enough.
lo = PatRangeBoundary::Finite(ty.numeric_max_val(cx.tcx).unwrap());
let max = ty.numeric_max_val(cx.tcx).unwrap();
let max = ty::ValTree::from_scalar_int(cx.tcx, max.try_to_scalar_int().unwrap());
lo = PatRangeBoundary::Finite(ty.inner(), max);
}
let hi = if let Some(hi) = range.hi.minus_one() {
hi
@@ -121,6 +121,7 @@ body:
span: $DIR/thir-tree-loop-match.rs:12:17: 12:21 (#0)
kind: PatKind {
Constant {
ty: bool
value: Leaf(0x01)
}
}
@@ -219,6 +220,7 @@ body:
span: $DIR/thir-tree-loop-match.rs:16:17: 16:22 (#0)
kind: PatKind {
Constant {
ty: bool
value: Leaf(0x00)
}
}