From ebe74a2349bdf1d2893e8011d0e4bfa71c4f69b5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 28 Jul 2025 16:59:06 +0200 Subject: [PATCH 1/6] PatKind: store constants as valtrees --- compiler/rustc_middle/src/thir.rs | 9 +- compiler/rustc_middle/src/thir/visit.rs | 2 +- .../src/builder/custom/parse/instruction.rs | 6 +- .../src/builder/matches/match_pair.rs | 6 +- .../src/builder/matches/mod.rs | 2 +- .../src/thir/pattern/const_to_pat.rs | 16 ++-- .../rustc_mir_build/src/thir/pattern/mod.rs | 13 ++- compiler/rustc_mir_build/src/thir/print.rs | 2 +- compiler/rustc_pattern_analysis/src/rustc.rs | 84 ++++++++----------- compiler/rustc_ty_utils/src/consts.rs | 2 +- .../ui/thir-print/thir-tree-loop-match.stdout | 4 +- 11 files changed, 68 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 730c1147684b4..17f81e802fcb8 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -832,15 +832,18 @@ 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. /// * `String`, if `string_deref_patterns` is enabled. Constant { - value: mir::Const<'tcx>, + // Not using `ty::Value` since this is conceptually not a type-level constant. In + // particular, it can have raw pointers. + ty: Ty<'tcx>, + value: ty::ValTree<'tcx>, }, /// Pattern obtained by converting a constant (inline or named) to its pattern diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index dcfa6c4db3274..9d3ba74ad0fd8 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -265,7 +265,7 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>( PatKind::Missing | PatKind::Wild | PatKind::Binding { subpattern: None, .. } - | PatKind::Constant { value: _ } + | PatKind::Constant { .. } | PatKind::Range(_) | PatKind::Never | PatKind::Error(_) => {} diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 9825b947fe09a..dbe4ab9937fb1 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -146,9 +146,9 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { for arm in rest { let arm = &self.thir[*arm]; let value = match arm.pattern.kind { - PatKind::Constant { value } => value, + PatKind::Constant { value, .. } => value, PatKind::ExpandedConstant { ref subpattern, def_id: _ } - if let PatKind::Constant { value } = subpattern.kind => + if let PatKind::Constant { value, .. } = subpattern.kind => { value } @@ -160,7 +160,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { }); } }; - values.push(value.eval_bits(self.tcx, self.typing_env)); + values.push(value.unwrap_leaf().to_bits_unchecked()); targets.push(self.parse_block(arm.body)?); } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 3a7854a5e118d..5b853d115247f 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -136,7 +136,11 @@ impl<'tcx> MatchPairTree<'tcx> { } } - PatKind::Constant { value } => Some(TestCase::Constant { value }), + 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::AscribeUserType { ascription: Ascription { ref annotation, variance }, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 2c29b8628417f..6ba530726a989 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1336,7 +1336,7 @@ enum TestKind<'tcx> { value: Const<'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`. + // only be `&str` or `f*`. ty: Ty<'tcx>, }, diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 91fcbb9390f7c..0bf46726581bb 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -10,11 +10,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; @@ -287,16 +287,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 { ty: ref_str_ty, value: 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 { ty, value: 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. @@ -325,15 +321,13 @@ impl<'tcx> ConstToPat<'tcx> { // Also see . 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 { ty, value: 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 { ty, value: cv } } ty::FnPtr(..) => { unreachable!( diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index a44afed5492d3..25fff0a3c237f 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -21,7 +21,7 @@ use rustc_middle::thir::{ 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, span_bug}; +use rustc_middle::{bug, mir, span_bug}; use rustc_span::def_id::DefId; use rustc_span::{ErrorGuaranteed, Span}; use tracing::{debug, instrument}; @@ -156,12 +156,15 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } // The unpeeled kind should now be a constant, giving us the endpoint value. - let PatKind::Constant { value } = kind else { + let PatKind::Constant { ty, value } = kind else { let msg = 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))) } @@ -244,7 +247,9 @@ 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() }; + // 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 }; } // `..=x` where `x == ty::MIN`. (RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {} diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 5efc4be2de2df..899f3e04ba2a1 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -761,7 +761,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_pat(subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } - PatKind::Constant { value } => { + PatKind::Constant { value, ty: _ } => { print_indented!(self, "Constant {", depth_lvl + 1); print_indented!(self, format!("value: {:?}", value), depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 0c1b0d622f229..0396cdc1aa292 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -517,78 +517,57 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ), } } - PatKind::Constant { value } => { + PatKind::Constant { ty: value_ty, 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.unwrap_leaf().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.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.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.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.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.unwrap_leaf().to_u128(); + let value = rustc_apfloat::ieee::Quad::from_bits(bits); + ctor = F128Range(value, value, RangeEnd::Included); fields = vec![]; arity = 0; } @@ -601,7 +580,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // subfields. // Note: `t` is `str`, not `&str`. let ty = self.reveal_opaque_ty(*t); - let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), 0, ty, pat); + // FIXME: why does `Str` need a `mir::Value`? + let val = mir::Const::Ty( + *value_ty, + ty::Const::new_value(self.tcx, *value, *value_ty), + ); + let subpattern = DeconstructedPat::new(Str(val), Vec::new(), 0, ty, pat); ctor = Ref; fields = vec![subpattern.at_index(0)]; arity = 1; diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index eb751da7c7363..6d994a2b967f7 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -375,7 +375,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { } match pat.kind { - thir::PatKind::Constant { value } => value.has_non_region_param(), + thir::PatKind::Constant { value, .. } => value.has_non_region_param(), thir::PatKind::Range(ref range) => { let &thir::PatRange { lo, hi, .. } = range.as_ref(); lo.has_non_region_param() || hi.has_non_region_param() diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout index 5c4c50cb15623..b3d11050bdcd4 100644 --- a/tests/ui/thir-print/thir-tree-loop-match.stdout +++ b/tests/ui/thir-print/thir-tree-loop-match.stdout @@ -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: Leaf(0x01) } } } @@ -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: Leaf(0x00) } } } From b39c019c1ca3727bc13c1a0ea06cccc5166f2ccb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 28 Jul 2025 18:16:47 +0200 Subject: [PATCH 2/6] pattern testing: store constants as valtrees --- compiler/rustc_middle/src/thir.rs | 73 +++++++------------ .../rustc_middle/src/ty/consts/valtree.rs | 13 ++++ .../rustc_middle/src/ty/structural_impls.rs | 13 +--- .../src/builder/matches/match_pair.rs | 6 +- .../src/builder/matches/mod.rs | 15 ++-- .../src/builder/matches/test.rs | 69 +++++++++--------- .../rustc_mir_build/src/thir/pattern/mod.rs | 14 +--- compiler/rustc_mir_build/src/thir/print.rs | 3 +- compiler/rustc_pattern_analysis/src/rustc.rs | 18 +++-- .../ui/thir-print/thir-tree-loop-match.stdout | 2 + 10 files changed, 106 insertions(+), 120 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 17f81e802fcb8..2baf00e312124 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -935,8 +935,8 @@ impl<'tcx> PatRange<'tcx> { // 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, @@ -944,8 +944,8 @@ impl<'tcx> PatRange<'tcx> { 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, @@ -958,22 +958,16 @@ impl<'tcx> PatRange<'tcx> { } #[inline] - pub fn contains( - &self, - value: mir::Const<'tcx>, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> Option { + pub fn contains(&self, value: ty::ValTree<'tcx>, tcx: TyCtxt<'tcx>) -> Option { 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, @@ -982,21 +976,16 @@ impl<'tcx> PatRange<'tcx> { } #[inline] - pub fn overlaps( - &self, - other: &Self, - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - ) -> Option { + pub fn overlaps(&self, other: &Self, tcx: TyCtxt<'tcx>) -> Option { 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, @@ -1007,10 +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(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 { @@ -1025,7 +1017,7 @@ 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>), + Finite(Ty<'tcx>, ty::ValTree<'tcx>), NegInfinity, PosInfinity, } @@ -1036,20 +1028,15 @@ impl<'tcx> PatRangeBoundary<'tcx> { matches!(self, Self::Finite(..)) } #[inline] - pub fn as_finite(self) -> Option> { + pub fn as_finite(self) -> Option> { 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 @@ -1061,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 { + #[instrument(skip(tcx), level = "debug", ret)] + pub fn compare_with(self, other: Self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option { use PatRangeBoundary::*; match (self, other) { // When comparing with infinities, we must remember that `0u8..` and `0u8..=255` @@ -1082,7 +1063,9 @@ impl<'tcx> PatRangeBoundary<'tcx> { // 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() { @@ -1096,8 +1079,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.eval_bits(ty, tcx); + let b = other.eval_bits(ty, tcx); match ty.kind() { ty::Float(ty::FloatTy::F16) => { diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index d95006dcf4a62..1ba05adaf9fc2 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -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. @@ -203,3 +205,14 @@ impl<'tcx> rustc_type_ir::inherent::ValueConst> 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 cx = FmtPrinter::new(tcx, Namespace::ValueNS); + cx.pretty_print_const_valtree(cv, /*print_ty*/ true)?; + f.write_str(&cx.into_buffer()) + }) + } +} diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index a5fdce93e4b2b..4c7ec31437ce6 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -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 cx = FmtPrinter::new(tcx, Namespace::ValueNS); - cx.pretty_print_const_valtree(cv, /*print_ty*/ true)?; - f.write_str(&cx.into_buffer()) - }); + write!(f, "{}", cv) + } else { + // Fall back to something verbose. + write!(f, "{:?}", self.kind()) } - // Fall back to something verbose. - write!(f, "{:?}", self.kind()) } } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 5b853d115247f..3a683ca9babd4 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -136,11 +136,7 @@ impl<'tcx> MatchPairTree<'tcx> { } } - 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 }, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 6ba530726a989..9b1105935ed81 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -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; @@ -1260,7 +1260,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>), Slice { len: usize, variable_length: bool }, Deref { temp: Place<'tcx>, mutability: Mutability }, @@ -1333,11 +1333,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. @@ -1373,7 +1374,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. @@ -1381,8 +1382,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> { + if let Self::Constant(v, _) = self { Some(*v) } else { None } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index a4609a6053ea5..6ccaafc1f35a5 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -35,7 +35,9 @@ 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, 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 @@ 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, 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 @@ 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 +189,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { eq_block, place, Mutability::Not, - ty, + cast_ty, ref_str, test.span, ); @@ -195,10 +198,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 +222,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 +237,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,6 +282,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; 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 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; 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 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // // 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 @@ 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,7 +580,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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 @@ 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, @@ -599,9 +602,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) } - (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 @@ 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 }) => { + (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 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - (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) diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 25fff0a3c237f..3a4e33b59d792 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -21,7 +21,7 @@ use rustc_middle::thir::{ 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 @@ 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)); }; - // 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 @@ 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`. @@ -247,9 +243,7 @@ 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() => { - // 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() => {} diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index 899f3e04ba2a1..e5ee60150a7e2 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -761,8 +761,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { 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); } diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 0396cdc1aa292..26574c5fcfc69 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -429,8 +429,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ) -> 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 @@ 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.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 @@ 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 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 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } 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 @@ 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(ty.inner(), max); } let hi = if let Some(hi) = range.hi.minus_one() { hi diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout index b3d11050bdcd4..13d412b5737c9 100644 --- a/tests/ui/thir-print/thir-tree-loop-match.stdout +++ b/tests/ui/thir-print/thir-tree-loop-match.stdout @@ -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) } } From 1870bd2e34d4f44b32a74cc8711f0144fcc4f277 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Jul 2025 08:54:34 +0200 Subject: [PATCH 3/6] use ty::Value instead of manual pairs of types and valtrees --- compiler/rustc_middle/src/mir/consts.rs | 5 +++ compiler/rustc_middle/src/thir.rs | 37 +++++++---------- compiler/rustc_middle/src/thir/visit.rs | 2 +- .../rustc_middle/src/ty/consts/valtree.rs | 14 +++++-- .../src/builder/custom/parse/instruction.rs | 6 +-- .../src/builder/matches/match_pair.rs | 2 +- .../src/builder/matches/mod.rs | 9 ++-- .../src/builder/matches/test.rs | 29 ++++++------- .../src/thir/pattern/const_to_pat.rs | 8 ++-- .../rustc_mir_build/src/thir/pattern/mod.rs | 6 +-- compiler/rustc_mir_build/src/thir/print.rs | 5 +-- compiler/rustc_pattern_analysis/src/rustc.rs | 41 ++++++++++--------- compiler/rustc_ty_utils/src/consts.rs | 2 +- .../ui/thir-print/thir-tree-loop-match.stdout | 6 +-- 14 files changed, 84 insertions(+), 88 deletions(-) diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs index 96131d47a177f..164433aede263 100644 --- a/compiler/rustc_middle/src/mir/consts.rs +++ b/compiler/rustc_middle/src/mir/consts.rs @@ -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, diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 2baf00e312124..54b9791872123 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -840,10 +840,7 @@ pub enum PatKind<'tcx> { /// much simpler. /// * `String`, if `string_deref_patterns` is enabled. Constant { - // Not using `ty::Value` since this is conceptually not a type-level constant. In - // particular, it can have raw pointers. - ty: Ty<'tcx>, - value: ty::ValTree<'tcx>, + value: ty::Value<'tcx>, }, /// Pattern obtained by converting a constant (inline or named) to its pattern @@ -935,8 +932,8 @@ impl<'tcx> PatRange<'tcx> { // 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(_ty, value) => { - let lo = value.unwrap_leaf().to_bits(size) ^ bias; + PatRangeBoundary::Finite(value) => { + let lo = value.try_to_scalar_int().unwrap().to_bits(size) ^ bias; lo <= min } PatRangeBoundary::PosInfinity => false, @@ -944,8 +941,8 @@ impl<'tcx> PatRange<'tcx> { if lo_is_min { let hi_is_max = match self.hi { PatRangeBoundary::NegInfinity => false, - PatRangeBoundary::Finite(_ty, value) => { - let hi = value.unwrap_leaf().to_bits(size) ^ bias; + PatRangeBoundary::Finite(value) => { + let hi = value.try_to_scalar_int().unwrap().to_bits(size) ^ bias; hi > max || hi == max && self.end == RangeEnd::Included } PatRangeBoundary::PosInfinity => true, @@ -958,10 +955,11 @@ impl<'tcx> PatRange<'tcx> { } #[inline] - pub fn contains(&self, value: ty::ValTree<'tcx>, tcx: TyCtxt<'tcx>) -> Option { + pub fn contains(&self, value: ty::Value<'tcx>, tcx: TyCtxt<'tcx>) -> Option { use Ordering::*; + debug_assert_eq!(self.ty, value.ty); let ty = self.ty; - let value = PatRangeBoundary::Finite(ty, value); + let value = PatRangeBoundary::Finite(value); // For performance, it's important to only do the second comparison if necessary. Some( match self.lo.compare_with(value, ty, tcx)? { @@ -996,13 +994,10 @@ impl<'tcx> PatRange<'tcx> { impl<'tcx> fmt::Display for PatRange<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let &PatRangeBoundary::Finite(ty, value) = &self.lo { - // `ty::Value` has a reasonable pretty-printing implementation. - let value = ty::Value { ty, valtree: value }; + if let PatRangeBoundary::Finite(value) = &self.lo { write!(f, "{value}")?; } - if let &PatRangeBoundary::Finite(ty, value) = &self.hi { - let value = ty::Value { ty, valtree: value }; + if let PatRangeBoundary::Finite(value) = &self.hi { write!(f, "{}", self.end)?; write!(f, "{value}")?; } else { @@ -1017,7 +1012,7 @@ 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(Ty<'tcx>, ty::ValTree<'tcx>), + Finite(ty::Value<'tcx>), NegInfinity, PosInfinity, } @@ -1028,15 +1023,15 @@ impl<'tcx> PatRangeBoundary<'tcx> { matches!(self, Self::Finite(..)) } #[inline] - pub fn as_finite(self) -> Option> { + pub fn as_finite(self) -> Option> { match self { - Self::Finite(_ty, value) => Some(value), + Self::Finite(value) => Some(value), Self::NegInfinity | Self::PosInfinity => None, } } pub fn eval_bits(self, ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> u128 { match self { - Self::Finite(_ty, value) => value.unwrap_leaf().to_bits_unchecked(), + 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 @@ -1063,9 +1058,7 @@ impl<'tcx> PatRangeBoundary<'tcx> { // 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() { diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs index 9d3ba74ad0fd8..dcfa6c4db3274 100644 --- a/compiler/rustc_middle/src/thir/visit.rs +++ b/compiler/rustc_middle/src/thir/visit.rs @@ -265,7 +265,7 @@ pub(crate) fn for_each_immediate_subpat<'a, 'tcx>( PatKind::Missing | PatKind::Wild | PatKind::Binding { subpattern: None, .. } - | PatKind::Constant { .. } + | PatKind::Constant { value: _ } | PatKind::Range(_) | PatKind::Never | PatKind::Error(_) => {} diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index 1ba05adaf9fc2..da74586f21782 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -135,6 +135,8 @@ pub type ConstToValTreeResult<'tcx> = Result, Ty<'tcx>>, Er /// A type-level constant value. /// /// Represents a typed, fully evaluated constant. +/// Note that this is 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> { @@ -149,15 +151,19 @@ impl<'tcx> Value<'tcx> { /// or an aggregate). #[inline] pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option { - let (ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Float(_)) = self.ty.kind() else { - return None; - }; - let scalar = self.valtree.try_to_scalar_int()?; + let scalar = self.try_to_scalar_int()?; let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty); let size = tcx.layout_of(input).ok()?.size; Some(scalar.to_bits(size)) } + pub fn try_to_scalar_int(self) -> Option { + let (ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Float(_)) = self.ty.kind() else { + return None; + }; + self.valtree.try_to_scalar_int() + } + pub fn try_to_bool(self) -> Option { if !self.ty.is_bool() { return None; diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index dbe4ab9937fb1..50e8fb15cab13 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -146,9 +146,9 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { for arm in rest { let arm = &self.thir[*arm]; let value = match arm.pattern.kind { - PatKind::Constant { value, .. } => value, + PatKind::Constant { value } => value, PatKind::ExpandedConstant { ref subpattern, def_id: _ } - if let PatKind::Constant { value, .. } = subpattern.kind => + if let PatKind::Constant { value } = subpattern.kind => { value } @@ -160,7 +160,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { }); } }; - values.push(value.unwrap_leaf().to_bits_unchecked()); + values.push(value.try_to_scalar_int().unwrap().to_bits_unchecked()); targets.push(self.parse_block(arm.body)?); } diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index 3a683ca9babd4..3a7854a5e118d 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -136,7 +136,7 @@ impl<'tcx> MatchPairTree<'tcx> { } } - PatKind::Constant { ty, value } => Some(TestCase::Constant { ty, value }), + PatKind::Constant { value } => Some(TestCase::Constant { value }), PatKind::AscribeUserType { ascription: Ascription { ref annotation, variance }, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index 9b1105935ed81..e3d891d625ed3 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1260,7 +1260,7 @@ struct Ascription<'tcx> { #[derive(Debug, Clone)] enum TestCase<'tcx> { Variant { adt_def: ty::AdtDef<'tcx>, variant_index: VariantIdx }, - Constant { ty: Ty<'tcx>, value: ty::ValTree<'tcx> }, + Constant { value: ty::Value<'tcx> }, Range(Arc>), Slice { len: usize, variable_length: bool }, Deref { temp: Place<'tcx>, mutability: Mutability }, @@ -1333,8 +1333,7 @@ enum TestKind<'tcx> { /// Test for equality with value, possibly after an unsizing coercion to /// `ty`, Eq { - value: ty::ValTree<'tcx>, - value_ty: Ty<'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` or `f*`. @@ -1374,7 +1373,7 @@ enum TestBranch<'tcx> { /// Success branch, used for tests with two possible outcomes. Success, /// Branch corresponding to this constant. - Constant(ty::ValTree<'tcx>, u128), + Constant(ty::Value<'tcx>, u128), /// Branch corresponding to this variant. Variant(VariantIdx), /// Failure branch for tests with two possible outcomes, and "otherwise" branch for other tests. @@ -1382,7 +1381,7 @@ enum TestBranch<'tcx> { } impl<'tcx> TestBranch<'tcx> { - fn as_constant(&self) -> Option> { + fn as_constant(&self) -> Option> { if let Self::Constant(v, _) = self { Some(*v) } else { None } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 6ccaafc1f35a5..6771470e45a3d 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -35,9 +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, ty: value_ty } => { - TestKind::Eq { value, value_ty, cast_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); @@ -137,14 +135,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(block, self.source_info(match_start_span), terminator); } - TestKind::Eq { value, value_ty, mut cast_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 value = Const::Ty(value_ty, ty::Const::new_value(tcx, value, 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; @@ -201,7 +198,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { cast_ty = ref_str_ty; } &ty::Pat(base, _) => { - assert_eq!(cast_ty, value_ty); + assert_eq!(cast_ty, value.ty); assert!(base.is_trivially_pure_clone_copy()); let transmuted_place = self.temp(base, test.span); @@ -282,8 +279,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; 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); + let lo = self.literal_operand(test.span, Const::from_ty_value(self.tcx, lo)); self.compare( block, intermediate_block, @@ -296,8 +292,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; 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 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, @@ -553,7 +548,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // // 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 @@ -580,7 +575,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None } else { fully_matched = true; - let bits = value.unwrap_leaf().to_bits_unchecked(); + let bits = value.try_to_scalar_int().unwrap().to_bits_unchecked(); Some(TestBranch::Constant(value, bits)) } } @@ -602,9 +597,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) } - (TestKind::If, TestCase::Constant { value, .. }) => { + (TestKind::If, TestCase::Constant { value }) => { fully_matched = true; - let value = value.unwrap_leaf().try_to_bool().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 }) @@ -687,7 +682,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 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)? { // `value` is not contained in the testing range, diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs index 0bf46726581bb..406a5257cfc28 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs @@ -287,12 +287,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 { ty: ref_str_ty, value: cv } + 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 { ty, value: cv }, + 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. @@ -321,13 +321,13 @@ impl<'tcx> ConstToPat<'tcx> { // Also see . return self.mk_err(tcx.dcx().create_err(NaNPattern { span }), ty); } else { - PatKind::Constant { ty, value: cv } + 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 { ty, value: cv } + PatKind::Constant { value: ty::Value { ty, valtree: cv } } } ty::FnPtr(..) => { unreachable!( diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 3a4e33b59d792..539ebc3a3d73c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -156,12 +156,12 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } // The unpeeled kind should now be a constant, giving us the endpoint value. - let PatKind::Constant { ty, value } = kind else { + let PatKind::Constant { value } = kind else { let msg = 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(ty, value))) + Ok(Some(PatRangeBoundary::Finite(value))) } /// Overflowing literals are linted against in a late pass. This is mostly fine, except when we @@ -243,7 +243,7 @@ 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 { ty, value: lo.as_finite().unwrap() }; + kind = PatKind::Constant { value: lo.as_finite().unwrap() }; } // `..=x` where `x == ty::MIN`. (RangeEnd::Included, Some(Ordering::Equal)) if !lo.is_finite() => {} diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs index e5ee60150a7e2..b7e8d6fea40bb 100644 --- a/compiler/rustc_mir_build/src/thir/print.rs +++ b/compiler/rustc_mir_build/src/thir/print.rs @@ -761,10 +761,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> { self.print_pat(subpattern, depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } - PatKind::Constant { value, ty } => { + PatKind::Constant { value } => { 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, format!("value: {}", value), depth_lvl + 2); print_indented!(self, "}", depth_lvl + 1); } PatKind::ExpandedConstant { def_id, subpattern } => { diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 26574c5fcfc69..1cc68f7f1da07 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -429,8 +429,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ) -> MaybeInfiniteInt { match bdy { PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity, - PatRangeBoundary::Finite(_ty, value) => { - let bits = value.unwrap_leaf().to_bits_unchecked(); + PatRangeBoundary::Finite(value) => { + 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(); @@ -517,16 +517,16 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { ), } } - PatKind::Constant { ty: value_ty, value } => { + PatKind::Constant { value } => { match ty.kind() { ty::Bool => { - ctor = Bool(value.unwrap_leaf().try_to_bool().unwrap()); + ctor = Bool(value.try_to_bool().unwrap()); fields = vec![]; arity = 0; } ty::Char | ty::Int(_) | ty::Uint(_) => { ctor = { - let bits = value.unwrap_leaf().to_bits_unchecked(); + let bits = value.try_to_scalar_int().unwrap().to_bits_unchecked(); let x = match *ty.kind() { ty::Int(ity) => { let size = Integer::from_int_ty(&cx.tcx, ity).size().bits(); @@ -541,7 +541,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Float(ty::FloatTy::F16) => { use rustc_apfloat::Float; - let bits = value.unwrap_leaf().to_u16(); + let bits = value.try_to_scalar_int().unwrap().to_u16(); let value = rustc_apfloat::ieee::Half::from_bits(bits.into()); ctor = F16Range(value, value, RangeEnd::Included); fields = vec![]; @@ -549,7 +549,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Float(ty::FloatTy::F32) => { use rustc_apfloat::Float; - let bits = value.unwrap_leaf().to_u32(); + let bits = value.try_to_scalar_int().unwrap().to_u32(); let value = rustc_apfloat::ieee::Single::from_bits(bits.into()); ctor = F32Range(value, value, RangeEnd::Included); fields = vec![]; @@ -557,7 +557,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Float(ty::FloatTy::F64) => { use rustc_apfloat::Float; - let bits = value.unwrap_leaf().to_u64(); + let bits = value.try_to_scalar_int().unwrap().to_u64(); let value = rustc_apfloat::ieee::Double::from_bits(bits.into()); ctor = F64Range(value, value, RangeEnd::Included); fields = vec![]; @@ -565,7 +565,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Float(ty::FloatTy::F128) => { use rustc_apfloat::Float; - let bits = value.unwrap_leaf().to_u128(); + let bits = value.try_to_scalar_int().unwrap().to_u128(); let value = rustc_apfloat::ieee::Quad::from_bits(bits); ctor = F128Range(value, value, RangeEnd::Included); fields = vec![]; @@ -580,11 +580,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // subfields. // Note: `t` is `str`, not `&str`. let ty = self.reveal_opaque_ty(*t); - // FIXME: why does `Str` need a `mir::Value`? - let val = mir::Const::Ty( - *value_ty, - ty::Const::new_value(self.tcx, *value, *value_ty), - ); + // FIXME: why does `Str` need a `mir::Const`? + let val = mir::Const::from_ty_value(self.tcx, *value); let subpattern = DeconstructedPat::new(Str(val), Vec::new(), 0, ty, pat); ctor = Ref; fields = vec![subpattern.at_index(0)]; @@ -614,8 +611,12 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Float(fty) => { use rustc_apfloat::Float; - 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()); + 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; @@ -723,8 +724,8 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { }; match ScalarInt::try_from_uint(bits, size) { Some(scalar) => { - let value = ty::ValTree::from_scalar_int(tcx, scalar); - PatRangeBoundary::Finite(ty.inner(), value) + let valtree = ty::ValTree::from_scalar_int(tcx, scalar); + PatRangeBoundary::Finite(ty::Value { ty: ty.inner(), 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 @@ -745,7 +746,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } else if range.is_singleton() { let lo = cx.hoist_pat_range_bdy(range.lo, ty); let value = lo.as_finite().unwrap(); - ty::Value { ty: ty.inner(), valtree: value }.to_string() + value.to_string() } else { // We convert to an inclusive range for diagnostics. let mut end = rustc_hir::RangeEnd::Included; @@ -758,7 +759,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // probably clear enough. 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); + lo = PatRangeBoundary::Finite(ty::Value { ty: ty.inner(), valtree: max }); } let hi = if let Some(hi) = range.hi.minus_one() { hi diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 6d994a2b967f7..eb751da7c7363 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -375,7 +375,7 @@ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> { } match pat.kind { - thir::PatKind::Constant { value, .. } => value.has_non_region_param(), + thir::PatKind::Constant { value } => value.has_non_region_param(), thir::PatKind::Range(ref range) => { let &thir::PatRange { lo, hi, .. } = range.as_ref(); lo.has_non_region_param() || hi.has_non_region_param() diff --git a/tests/ui/thir-print/thir-tree-loop-match.stdout b/tests/ui/thir-print/thir-tree-loop-match.stdout index 13d412b5737c9..f2b63782c596c 100644 --- a/tests/ui/thir-print/thir-tree-loop-match.stdout +++ b/tests/ui/thir-print/thir-tree-loop-match.stdout @@ -121,8 +121,7 @@ body: span: $DIR/thir-tree-loop-match.rs:12:17: 12:21 (#0) kind: PatKind { Constant { - ty: bool - value: Leaf(0x01) + value: true } } } @@ -220,8 +219,7 @@ body: span: $DIR/thir-tree-loop-match.rs:16:17: 16:22 (#0) kind: PatKind { Constant { - ty: bool - value: Leaf(0x00) + value: false } } } From 9f3f01536245ba5cfe1324061af2bc174a15012a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Jul 2025 09:01:05 +0200 Subject: [PATCH 4/6] change StrLit type to ty::Value as well --- compiler/rustc_pattern_analysis/src/rustc.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 1cc68f7f1da07..e4703ddfc454b 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -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::{ @@ -580,9 +579,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // subfields. // Note: `t` is `str`, not `&str`. let ty = self.reveal_opaque_ty(*t); - // FIXME: why does `Str` need a `mir::Const`? - let val = mir::Const::from_ty_value(self.tcx, *value); - let subpattern = DeconstructedPat::new(Str(val), Vec::new(), 0, ty, pat); + let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), 0, ty, pat); ctor = Ref; fields = vec![subpattern.at_index(0)]; arity = 1; @@ -894,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>; From 5e53d76b59401df6ed48e3ab5271bdb5c017e1ca Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Jul 2025 13:28:47 +0200 Subject: [PATCH 5/6] shrink TestBranch::Constant and PatRangeBoundary::Finite --- compiler/rustc_middle/src/thir.rs | 15 +++++++++------ .../rustc_mir_build/src/builder/matches/mod.rs | 6 +++--- .../rustc_mir_build/src/builder/matches/test.rs | 8 +++++--- compiler/rustc_mir_build/src/thir/pattern/mod.rs | 5 +++-- compiler/rustc_pattern_analysis/src/rustc.rs | 6 +++--- 5 files changed, 23 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 54b9791872123..cf930bf200b0a 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -957,9 +957,9 @@ impl<'tcx> PatRange<'tcx> { #[inline] pub fn contains(&self, value: ty::Value<'tcx>, tcx: TyCtxt<'tcx>) -> Option { 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)? { @@ -994,11 +994,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. @@ -1012,7 +1014,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(ty::Value<'tcx>), + /// The type of this valtree is stored in the surrounding `PatRange`. + Finite(ty::ValTree<'tcx>), NegInfinity, PosInfinity, } @@ -1023,7 +1026,7 @@ impl<'tcx> PatRangeBoundary<'tcx> { matches!(self, Self::Finite(..)) } #[inline] - pub fn as_finite(self) -> Option> { + pub fn as_finite(self) -> Option> { match self { Self::Finite(value) => Some(value), Self::NegInfinity | Self::PosInfinity => None, diff --git a/compiler/rustc_mir_build/src/builder/matches/mod.rs b/compiler/rustc_mir_build/src/builder/matches/mod.rs index e3d891d625ed3..06055d0106259 100644 --- a/compiler/rustc_mir_build/src/builder/matches/mod.rs +++ b/compiler/rustc_mir_build/src/builder/matches/mod.rs @@ -1372,8 +1372,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(ty::Value<'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. @@ -1382,7 +1382,7 @@ enum TestBranch<'tcx> { impl<'tcx> TestBranch<'tcx> { fn as_constant(&self) -> Option> { - if let Self::Constant(v, _) = self { Some(*v) } else { None } + if let Self::Constant(v) = self { Some(*v) } else { None } } } diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 6771470e45a3d..f2f708bddcee0 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -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.try_to_scalar_int().unwrap().to_bits_unchecked(); Some((bits, block)) } else { None @@ -279,6 +280,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; if let Some(lo) = range.lo.as_finite() { + 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, @@ -292,6 +294,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; if let Some(hi) = range.hi.as_finite() { + 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, @@ -575,8 +578,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { None } else { fully_matched = true; - let bits = value.try_to_scalar_int().unwrap().to_bits_unchecked(); - Some(TestBranch::Constant(value, bits)) + Some(TestBranch::Constant(value)) } } (TestKind::SwitchInt, TestCase::Range(range)) => { diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 539ebc3a3d73c..166e64a5fcb85 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -161,7 +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 @@ -243,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() => {} diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index e4703ddfc454b..97f1c84279774 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -722,7 +722,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { match ScalarInt::try_from_uint(bits, size) { Some(scalar) => { let valtree = ty::ValTree::from_scalar_int(tcx, scalar); - PatRangeBoundary::Finite(ty::Value { ty: ty.inner(), valtree }) + 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 @@ -742,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. @@ -756,7 +756,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { // probably clear enough. 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::Value { ty: ty.inner(), valtree: max }); + lo = PatRangeBoundary::Finite(max); } let hi = if let Some(hi) = range.hi.minus_one() { hi From cbb5fc17256f6306eb1a72523c86c4b68e99c68d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 29 Jul 2025 17:22:22 +0200 Subject: [PATCH 6/6] avoid unnecessary type sanity checks --- compiler/rustc_middle/src/ty/consts/valtree.rs | 12 ++++-------- .../src/builder/custom/parse/instruction.rs | 2 +- compiler/rustc_mir_build/src/builder/matches/test.rs | 2 +- compiler/rustc_pattern_analysis/src/rustc.rs | 10 +++++----- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_middle/src/ty/consts/valtree.rs b/compiler/rustc_middle/src/ty/consts/valtree.rs index da74586f21782..6bfaeebc2e2bb 100644 --- a/compiler/rustc_middle/src/ty/consts/valtree.rs +++ b/compiler/rustc_middle/src/ty/consts/valtree.rs @@ -151,17 +151,13 @@ impl<'tcx> Value<'tcx> { /// or an aggregate). #[inline] pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option { - let scalar = self.try_to_scalar_int()?; - let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty); - let size = tcx.layout_of(input).ok()?.size; - Some(scalar.to_bits(size)) - } - - pub fn try_to_scalar_int(self) -> Option { let (ty::Bool | ty::Char | ty::Uint(_) | ty::Int(_) | ty::Float(_)) = self.ty.kind() else { return None; }; - self.valtree.try_to_scalar_int() + let scalar = self.valtree.try_to_scalar_int()?; + let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(self.ty); + let size = tcx.layout_of(input).ok()?.size; + Some(scalar.to_bits(size)) } pub fn try_to_bool(self) -> Option { diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 50e8fb15cab13..41d3aefcbe6b5 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -160,7 +160,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { }); } }; - values.push(value.try_to_scalar_int().unwrap().to_bits_unchecked()); + values.push(value.valtree.unwrap_leaf().to_bits_unchecked()); targets.push(self.parse_block(arm.body)?); } diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index f2f708bddcee0..cefc3d0bf14b5 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -113,7 +113,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let switch_targets = SwitchTargets::new( target_blocks.iter().filter_map(|(&branch, &block)| { if let TestBranch::Constant(value) = branch { - let bits = value.try_to_scalar_int().unwrap().to_bits_unchecked(); + let bits = value.valtree.unwrap_leaf().to_bits_unchecked(); Some((bits, block)) } else { None diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs index 97f1c84279774..c9bf4fe4449b0 100644 --- a/compiler/rustc_pattern_analysis/src/rustc.rs +++ b/compiler/rustc_pattern_analysis/src/rustc.rs @@ -525,7 +525,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Char | ty::Int(_) | ty::Uint(_) => { ctor = { - let bits = value.try_to_scalar_int().unwrap().to_bits_unchecked(); + 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(); @@ -540,7 +540,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Float(ty::FloatTy::F16) => { use rustc_apfloat::Float; - let bits = value.try_to_scalar_int().unwrap().to_u16(); + 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![]; @@ -548,7 +548,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Float(ty::FloatTy::F32) => { use rustc_apfloat::Float; - let bits = value.try_to_scalar_int().unwrap().to_u32(); + 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![]; @@ -556,7 +556,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Float(ty::FloatTy::F64) => { use rustc_apfloat::Float; - let bits = value.try_to_scalar_int().unwrap().to_u64(); + 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![]; @@ -564,7 +564,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> { } ty::Float(ty::FloatTy::F128) => { use rustc_apfloat::Float; - let bits = value.try_to_scalar_int().unwrap().to_u128(); + 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![];