Auto merge of #144591 - RalfJung:pattern-valtrees, r=BoxyUwU
Patterns: represent constants as valtrees Const patterns are always valtrees now. Let's represent that in the types. We use `ty::Value` for this since it nicely packages value and type, and has some convenient methods. Cc `@Nadrieril` `@BoxyUwU`
This commit is contained in:
commit
8800ec1665
12 changed files with 146 additions and 175 deletions
|
|
@ -448,6 +448,11 @@ impl<'tcx> Const<'tcx> {
|
|||
Self::Val(val, ty)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_ty_value(tcx: TyCtxt<'tcx>, val: ty::Value<'tcx>) -> Self {
|
||||
Self::Ty(val.ty, ty::Const::new_value(tcx, val.valtree, val.ty))
|
||||
}
|
||||
|
||||
pub fn from_bits(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
bits: u128,
|
||||
|
|
|
|||
|
|
@ -832,17 +832,17 @@ pub enum PatKind<'tcx> {
|
|||
},
|
||||
|
||||
/// One of the following:
|
||||
/// * `&str` (represented as a valtree), which will be handled as a string pattern and thus
|
||||
/// * `&str`, which will be handled as a string pattern and thus
|
||||
/// exhaustiveness checking will detect if you use the same string twice in different
|
||||
/// patterns.
|
||||
/// * integer, bool, char or float (represented as a valtree), which will be handled by
|
||||
/// * integer, bool, char or float, which will be handled by
|
||||
/// exhaustiveness to cover exactly its own value, similar to `&str`, but these values are
|
||||
/// much simpler.
|
||||
/// * raw pointers derived from integers, other raw pointers will have already resulted in an
|
||||
// error.
|
||||
/// * `String`, if `string_deref_patterns` is enabled.
|
||||
Constant {
|
||||
value: mir::Const<'tcx>,
|
||||
value: ty::Value<'tcx>,
|
||||
},
|
||||
|
||||
/// Pattern obtained by converting a constant (inline or named) to its pattern
|
||||
|
|
@ -935,7 +935,7 @@ impl<'tcx> PatRange<'tcx> {
|
|||
let lo_is_min = match self.lo {
|
||||
PatRangeBoundary::NegInfinity => true,
|
||||
PatRangeBoundary::Finite(value) => {
|
||||
let lo = value.try_to_bits(size).unwrap() ^ bias;
|
||||
let lo = value.try_to_scalar_int().unwrap().to_bits(size) ^ bias;
|
||||
lo <= min
|
||||
}
|
||||
PatRangeBoundary::PosInfinity => false,
|
||||
|
|
@ -944,7 +944,7 @@ impl<'tcx> PatRange<'tcx> {
|
|||
let hi_is_max = match self.hi {
|
||||
PatRangeBoundary::NegInfinity => false,
|
||||
PatRangeBoundary::Finite(value) => {
|
||||
let hi = value.try_to_bits(size).unwrap() ^ bias;
|
||||
let hi = value.try_to_scalar_int().unwrap().to_bits(size) ^ bias;
|
||||
hi > max || hi == max && self.end == RangeEnd::Included
|
||||
}
|
||||
PatRangeBoundary::PosInfinity => true,
|
||||
|
|
@ -957,22 +957,17 @@ impl<'tcx> PatRange<'tcx> {
|
|||
}
|
||||
|
||||
#[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::Value<'tcx>, tcx: TyCtxt<'tcx>) -> Option<bool> {
|
||||
use Ordering::*;
|
||||
debug_assert_eq!(self.ty, value.ty());
|
||||
debug_assert_eq!(value.ty, self.ty);
|
||||
let ty = self.ty;
|
||||
let value = PatRangeBoundary::Finite(value);
|
||||
let value = PatRangeBoundary::Finite(value.valtree);
|
||||
// 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,
|
||||
|
|
@ -981,21 +976,16 @@ impl<'tcx> PatRange<'tcx> {
|
|||
}
|
||||
|
||||
#[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,
|
||||
|
|
@ -1006,11 +996,13 @@ impl<'tcx> PatRange<'tcx> {
|
|||
|
||||
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(valtree) = &self.lo {
|
||||
let value = ty::Value { ty: self.ty, valtree };
|
||||
write!(f, "{value}")?;
|
||||
}
|
||||
if let PatRangeBoundary::Finite(value) = &self.hi {
|
||||
if let &PatRangeBoundary::Finite(valtree) = &self.hi {
|
||||
write!(f, "{}", self.end)?;
|
||||
let value = ty::Value { ty: self.ty, valtree };
|
||||
write!(f, "{value}")?;
|
||||
} else {
|
||||
// `0..` is parsed as an inclusive range, we must display it correctly.
|
||||
|
|
@ -1024,7 +1016,8 @@ impl<'tcx> fmt::Display for PatRange<'tcx> {
|
|||
/// 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>),
|
||||
/// The type of this valtree is stored in the surrounding `PatRange`.
|
||||
Finite(ty::ValTree<'tcx>),
|
||||
NegInfinity,
|
||||
PosInfinity,
|
||||
}
|
||||
|
|
@ -1035,20 +1028,15 @@ impl<'tcx> PatRangeBoundary<'tcx> {
|
|||
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::NegInfinity | Self::PosInfinity => None,
|
||||
}
|
||||
}
|
||||
pub fn eval_bits(
|
||||
self,
|
||||
ty: Ty<'tcx>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
) -> u128 {
|
||||
pub fn to_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> u128 {
|
||||
match self {
|
||||
Self::Finite(value) => value.eval_bits(tcx, typing_env),
|
||||
Self::Finite(value) => value.try_to_scalar_int().unwrap().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
|
||||
|
|
@ -1060,14 +1048,8 @@ impl<'tcx> PatRangeBoundary<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[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`
|
||||
|
|
@ -1095,8 +1077,8 @@ impl<'tcx> PatRangeBoundary<'tcx> {
|
|||
_ => {}
|
||||
}
|
||||
|
||||
let a = self.eval_bits(ty, tcx, typing_env);
|
||||
let b = other.eval_bits(ty, tcx, typing_env);
|
||||
let a = self.to_bits(ty, tcx);
|
||||
let b = other.to_bits(ty, tcx);
|
||||
|
||||
match ty.kind() {
|
||||
ty::Float(ty::FloatTy::F16) => {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@ use std::fmt;
|
|||
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.
|
||||
|
|
@ -133,6 +135,8 @@ pub type ConstToValTreeResult<'tcx> = Result<Result<ValTree<'tcx>, Ty<'tcx>>, Er
|
|||
/// A type-level constant value.
|
||||
///
|
||||
/// Represents a typed, fully evaluated constant.
|
||||
/// Note that this is also used by pattern elaboration to represent values which cannot occur in types,
|
||||
/// such as raw pointers and floats.
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable, Lift)]
|
||||
pub struct Value<'tcx> {
|
||||
|
|
@ -203,3 +207,14 @@ impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'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_hir::def_id::LocalDefId;
|
|||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> {
|
|||
});
|
||||
}
|
||||
};
|
||||
values.push(value.eval_bits(self.tcx, self.typing_env));
|
||||
values.push(value.valtree.unwrap_leaf().to_bits_unchecked());
|
||||
targets.push(self.parse_block(arm.body)?);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
|
|||
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 { value: ty::Value<'tcx> },
|
||||
Range(Arc<PatRange<'tcx>>),
|
||||
Slice { len: usize, variable_length: bool },
|
||||
Deref { temp: Place<'tcx>, mutability: Mutability },
|
||||
|
|
@ -1316,13 +1316,13 @@ enum TestKind<'tcx> {
|
|||
If,
|
||||
|
||||
/// Test for equality with value, possibly after an unsizing coercion to
|
||||
/// `ty`,
|
||||
/// `cast_ty`,
|
||||
Eq {
|
||||
value: Const<'tcx>,
|
||||
value: ty::Value<'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`, `f32` or `f64`.
|
||||
ty: Ty<'tcx>,
|
||||
// only be `&str` or floats.
|
||||
cast_ty: Ty<'tcx>,
|
||||
},
|
||||
|
||||
/// Test whether the value falls within an inclusive or exclusive range.
|
||||
|
|
@ -1357,8 +1357,8 @@ pub(crate) struct Test<'tcx> {
|
|||
enum TestBranch<'tcx> {
|
||||
/// Success branch, used for tests with two possible outcomes.
|
||||
Success,
|
||||
/// Branch corresponding to this constant.
|
||||
Constant(Const<'tcx>, u128),
|
||||
/// Branch corresponding to this constant. Must be a scalar.
|
||||
Constant(ty::Value<'tcx>),
|
||||
/// Branch corresponding to this variant.
|
||||
Variant(VariantIdx),
|
||||
/// Failure branch for tests with two possible outcomes, and "otherwise" branch for other tests.
|
||||
|
|
@ -1366,8 +1366,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::Value<'tcx>> {
|
||||
if let Self::Constant(v) = self { Some(*v) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
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 } => TestKind::Eq { value, cast_ty: match_pair.pattern_ty },
|
||||
|
||||
TestCase::Range(ref range) => {
|
||||
assert_eq!(range.ty, match_pair.pattern_ty);
|
||||
|
|
@ -112,7 +112,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let otherwise_block = target_block(TestBranch::Failure);
|
||||
let switch_targets = SwitchTargets::new(
|
||||
target_blocks.iter().filter_map(|(&branch, &block)| {
|
||||
if let TestBranch::Constant(_, bits) = branch {
|
||||
if let TestBranch::Constant(value) = branch {
|
||||
let bits = value.valtree.unwrap_leaf().to_bits_unchecked();
|
||||
Some((bits, block))
|
||||
} else {
|
||||
None
|
||||
|
|
@ -135,17 +136,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
self.cfg.terminate(block, self.source_info(match_start_span), terminator);
|
||||
}
|
||||
|
||||
TestKind::Eq { value, mut ty } => {
|
||||
TestKind::Eq { value, 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 = self.literal_operand(test.span, value);
|
||||
let mut expect_ty = value.ty;
|
||||
let mut expect = self.literal_operand(test.span, Const::from_ty_value(tcx, 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 +168,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
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 +187,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
eq_block,
|
||||
place,
|
||||
Mutability::Not,
|
||||
ty,
|
||||
cast_ty,
|
||||
ref_str,
|
||||
test.span,
|
||||
);
|
||||
|
|
@ -195,10 +196,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// 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 +220,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
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 +235,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// 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,7 +280,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
};
|
||||
|
||||
if let Some(lo) = range.lo.as_finite() {
|
||||
let lo = self.literal_operand(test.span, lo);
|
||||
let lo = ty::Value { ty: range.ty, valtree: lo };
|
||||
let lo = self.literal_operand(test.span, Const::from_ty_value(self.tcx, lo));
|
||||
self.compare(
|
||||
block,
|
||||
intermediate_block,
|
||||
|
|
@ -289,7 +294,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
};
|
||||
|
||||
if let Some(hi) = range.hi.as_finite() {
|
||||
let hi = self.literal_operand(test.span, hi);
|
||||
let hi = ty::Value { ty: range.ty, valtree: hi };
|
||||
let hi = self.literal_operand(test.span, Const::from_ty_value(self.tcx, hi));
|
||||
let op = match range.end {
|
||||
RangeEnd::Included => BinOp::Le,
|
||||
RangeEnd::Excluded => BinOp::Lt,
|
||||
|
|
@ -555,10 +561,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// 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,8 +578,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
None
|
||||
} else {
|
||||
fully_matched = true;
|
||||
let bits = value.eval_bits(self.tcx, self.typing_env());
|
||||
Some(TestBranch::Constant(value, bits))
|
||||
Some(TestBranch::Constant(value))
|
||||
}
|
||||
}
|
||||
(TestKind::SwitchInt, TestCase::Range(range)) => {
|
||||
|
|
@ -585,12 +587,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// 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,
|
||||
|
|
@ -601,7 +601,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
(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.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 +681,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
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 }) => {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ use rustc_index::Idx;
|
|||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind};
|
||||
use rustc_middle::ty::{
|
||||
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, ValTree,
|
||||
};
|
||||
use rustc_middle::{mir, span_bug};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::{DUMMY_SP, Span};
|
||||
use rustc_trait_selection::traits::ObligationCause;
|
||||
|
|
@ -288,16 +288,12 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
// when lowering to MIR in `Builder::perform_test`, treat the constant as a `&str`.
|
||||
// This works because `str` and `&str` have the same valtree representation.
|
||||
let ref_str_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, ty);
|
||||
PatKind::Constant {
|
||||
value: mir::Const::Ty(ref_str_ty, ty::Const::new_value(tcx, cv, ref_str_ty)),
|
||||
}
|
||||
PatKind::Constant { value: ty::Value { ty: ref_str_ty, valtree: cv } }
|
||||
}
|
||||
ty::Ref(_, pointee_ty, ..) => match *pointee_ty.kind() {
|
||||
// `&str` is represented as a valtree, let's keep using this
|
||||
// optimization for now.
|
||||
ty::Str => PatKind::Constant {
|
||||
value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
|
||||
},
|
||||
ty::Str => PatKind::Constant { value: ty::Value { ty, valtree: cv } },
|
||||
// All other references are converted into deref patterns and then recursively
|
||||
// convert the dereferenced constant to a pattern that is the sub-pattern of the
|
||||
// deref pattern.
|
||||
|
|
@ -326,15 +322,13 @@ impl<'tcx> ConstToPat<'tcx> {
|
|||
// Also see <https://github.com/rust-lang/rfcs/pull/3535>.
|
||||
return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty);
|
||||
} else {
|
||||
PatKind::Constant {
|
||||
value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)),
|
||||
}
|
||||
PatKind::Constant { value: ty::Value { ty, valtree: cv } }
|
||||
}
|
||||
}
|
||||
ty::Pat(..) | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::RawPtr(..) => {
|
||||
// The raw pointers we see here have been "vetted" by valtree construction to be
|
||||
// just integers, so we simply allow them.
|
||||
PatKind::Constant { value: mir::Const::Ty(ty, ty::Const::new_value(tcx, cv, ty)) }
|
||||
PatKind::Constant { value: ty::Value { ty, valtree: cv } }
|
||||
}
|
||||
ty::FnPtr(..) => {
|
||||
unreachable!(
|
||||
|
|
|
|||
|
|
@ -161,8 +161,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
format!("found bad range pattern endpoint `{expr:?}` outside of error recovery");
|
||||
return Err(self.tcx.dcx().span_delayed_bug(expr.span, msg));
|
||||
};
|
||||
|
||||
Ok(Some(PatRangeBoundary::Finite(value)))
|
||||
Ok(Some(PatRangeBoundary::Finite(value.valtree)))
|
||||
}
|
||||
|
||||
/// Overflowing literals are linted against in a late pass. This is mostly fine, except when we
|
||||
|
|
@ -235,7 +234,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
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`.
|
||||
|
|
@ -244,7 +243,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
(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() => {
|
||||
kind = PatKind::Constant { value: lo.as_finite().unwrap() };
|
||||
let value = ty::Value { ty, valtree: lo.as_finite().unwrap() };
|
||||
kind = PatKind::Constant { value };
|
||||
}
|
||||
// `..=x` where `x == ty::MIN`.
|
||||
(RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {}
|
||||
|
|
|
|||
|
|
@ -763,7 +763,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
|
|||
}
|
||||
PatKind::Constant { value } => {
|
||||
print_indented!(self, "Constant {", depth_lvl + 1);
|
||||
print_indented!(self, format!("value: {:?}", value), depth_lvl + 2);
|
||||
print_indented!(self, format!("value: {}", value), depth_lvl + 2);
|
||||
print_indented!(self, "}", depth_lvl + 1);
|
||||
}
|
||||
PatKind::ExpandedConstant { def_id, subpattern } => {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ use rustc_hir::HirId;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
use rustc_middle::middle::stability::EvalResult;
|
||||
use rustc_middle::mir::{self, Const};
|
||||
use rustc_middle::thir::{self, Pat, PatKind, PatRange, PatRangeBoundary};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
|
|
@ -430,7 +429,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
match bdy {
|
||||
PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity,
|
||||
PatRangeBoundary::Finite(value) => {
|
||||
let bits = value.eval_bits(self.tcx, self.typing_env);
|
||||
let bits = value.try_to_scalar_int().unwrap().to_bits_unchecked();
|
||||
match *ty.kind() {
|
||||
ty::Int(ity) => {
|
||||
let size = Integer::from_int_ty(&self.tcx, ity).size().bits();
|
||||
|
|
@ -520,75 +519,54 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
PatKind::Constant { value } => {
|
||||
match ty.kind() {
|
||||
ty::Bool => {
|
||||
ctor = match value.try_eval_bool(cx.tcx, cx.typing_env) {
|
||||
Some(b) => Bool(b),
|
||||
None => Opaque(OpaqueId::new()),
|
||||
};
|
||||
ctor = Bool(value.try_to_bool().unwrap());
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
ty::Char | ty::Int(_) | ty::Uint(_) => {
|
||||
ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
|
||||
Some(bits) => {
|
||||
let x = match *ty.kind() {
|
||||
ty::Int(ity) => {
|
||||
let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
|
||||
MaybeInfiniteInt::new_finite_int(bits, size)
|
||||
}
|
||||
_ => MaybeInfiniteInt::new_finite_uint(bits),
|
||||
};
|
||||
IntRange(IntRange::from_singleton(x))
|
||||
}
|
||||
None => Opaque(OpaqueId::new()),
|
||||
ctor = {
|
||||
let bits = value.valtree.unwrap_leaf().to_bits_unchecked();
|
||||
let x = match *ty.kind() {
|
||||
ty::Int(ity) => {
|
||||
let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
|
||||
MaybeInfiniteInt::new_finite_int(bits, size)
|
||||
}
|
||||
_ => MaybeInfiniteInt::new_finite_uint(bits),
|
||||
};
|
||||
IntRange(IntRange::from_singleton(x))
|
||||
};
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
ty::Float(ty::FloatTy::F16) => {
|
||||
ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
|
||||
Some(bits) => {
|
||||
use rustc_apfloat::Float;
|
||||
let value = rustc_apfloat::ieee::Half::from_bits(bits);
|
||||
F16Range(value, value, RangeEnd::Included)
|
||||
}
|
||||
None => Opaque(OpaqueId::new()),
|
||||
};
|
||||
use rustc_apfloat::Float;
|
||||
let bits = value.valtree.unwrap_leaf().to_u16();
|
||||
let value = rustc_apfloat::ieee::Half::from_bits(bits.into());
|
||||
ctor = F16Range(value, value, RangeEnd::Included);
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
ty::Float(ty::FloatTy::F32) => {
|
||||
ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
|
||||
Some(bits) => {
|
||||
use rustc_apfloat::Float;
|
||||
let value = rustc_apfloat::ieee::Single::from_bits(bits);
|
||||
F32Range(value, value, RangeEnd::Included)
|
||||
}
|
||||
None => Opaque(OpaqueId::new()),
|
||||
};
|
||||
use rustc_apfloat::Float;
|
||||
let bits = value.valtree.unwrap_leaf().to_u32();
|
||||
let value = rustc_apfloat::ieee::Single::from_bits(bits.into());
|
||||
ctor = F32Range(value, value, RangeEnd::Included);
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
ty::Float(ty::FloatTy::F64) => {
|
||||
ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
|
||||
Some(bits) => {
|
||||
use rustc_apfloat::Float;
|
||||
let value = rustc_apfloat::ieee::Double::from_bits(bits);
|
||||
F64Range(value, value, RangeEnd::Included)
|
||||
}
|
||||
None => Opaque(OpaqueId::new()),
|
||||
};
|
||||
use rustc_apfloat::Float;
|
||||
let bits = value.valtree.unwrap_leaf().to_u64();
|
||||
let value = rustc_apfloat::ieee::Double::from_bits(bits.into());
|
||||
ctor = F64Range(value, value, RangeEnd::Included);
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
ty::Float(ty::FloatTy::F128) => {
|
||||
ctor = match value.try_eval_bits(cx.tcx, cx.typing_env) {
|
||||
Some(bits) => {
|
||||
use rustc_apfloat::Float;
|
||||
let value = rustc_apfloat::ieee::Quad::from_bits(bits);
|
||||
F128Range(value, value, RangeEnd::Included)
|
||||
}
|
||||
None => Opaque(OpaqueId::new()),
|
||||
};
|
||||
use rustc_apfloat::Float;
|
||||
let bits = value.valtree.unwrap_leaf().to_u128();
|
||||
let value = rustc_apfloat::ieee::Quad::from_bits(bits);
|
||||
ctor = F128Range(value, value, RangeEnd::Included);
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
|
|
@ -630,8 +608,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'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.try_to_scalar_int().unwrap().to_bits_unchecked());
|
||||
let hi = hi
|
||||
.as_finite()
|
||||
.map(|c| c.try_to_scalar_int().unwrap().to_bits_unchecked());
|
||||
match fty {
|
||||
ty::FloatTy::F16 => {
|
||||
use rustc_apfloat::ieee::Half;
|
||||
|
|
@ -739,8 +721,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
};
|
||||
match ScalarInt::try_from_uint(bits, size) {
|
||||
Some(scalar) => {
|
||||
let value = mir::Const::from_scalar(tcx, scalar.into(), ty.inner());
|
||||
PatRangeBoundary::Finite(value)
|
||||
let valtree = ty::ValTree::from_scalar_int(tcx, scalar);
|
||||
PatRangeBoundary::Finite(valtree)
|
||||
}
|
||||
// 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
|
||||
|
|
@ -760,7 +742,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
"_".to_string()
|
||||
} else if range.is_singleton() {
|
||||
let lo = cx.hoist_pat_range_bdy(range.lo, ty);
|
||||
let value = lo.as_finite().unwrap();
|
||||
let value = ty::Value { ty: ty.inner(), valtree: lo.as_finite().unwrap() };
|
||||
value.to_string()
|
||||
} else {
|
||||
// We convert to an inclusive range for diagnostics.
|
||||
|
|
@ -772,7 +754,9 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
|
|||
// 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(max);
|
||||
}
|
||||
let hi = if let Some(hi) = range.hi.minus_one() {
|
||||
hi
|
||||
|
|
@ -907,7 +891,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
|
|||
type Ty = RevealedTy<'tcx>;
|
||||
type Error = ErrorGuaranteed;
|
||||
type VariantIdx = VariantIdx;
|
||||
type StrLit = Const<'tcx>;
|
||||
type StrLit = ty::Value<'tcx>;
|
||||
type ArmData = HirId;
|
||||
type PatData = &'p Pat<'tcx>;
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ body:
|
|||
span: $DIR/thir-tree-loop-match.rs:12:17: 12:21 (#0)
|
||||
kind: PatKind {
|
||||
Constant {
|
||||
value: Ty(bool, true)
|
||||
value: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -219,7 +219,7 @@ body:
|
|||
span: $DIR/thir-tree-loop-match.rs:16:17: 16:22 (#0)
|
||||
kind: PatKind {
|
||||
Constant {
|
||||
value: Ty(bool, false)
|
||||
value: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue