introduce ty::Value

Co-authored-by: FedericoBruzzone <federico.bruzzone.i@gmail.com>
This commit is contained in:
Lukas Markeffsky 2025-01-27 04:30:00 +01:00
parent 5a45ab9738
commit 10fc0b159e
44 changed files with 214 additions and 205 deletions

View file

@ -129,12 +129,7 @@ pub(super) fn codegen_simd_intrinsic_call<'tcx>(
return;
}
let idx = generic_args[2]
.expect_const()
.try_to_valtree()
.expect("expected monomorphic const in codegen")
.0
.unwrap_branch();
let idx = generic_args[2].expect_const().to_value().valtree.unwrap_branch();
assert_eq!(x.layout(), y.layout());
let layout = x.layout();

View file

@ -1329,7 +1329,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
}
if name == sym::simd_shuffle_generic {
let idx = fn_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch();
let idx = fn_args[2].expect_const().to_value().valtree.unwrap_branch();
let n = idx.len() as u64;
let (out_len, out_ty) = require_simd!(ret_ty, SimdReturn);

View file

@ -673,25 +673,23 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
ty::ConstKind::Param(param) => {
write!(output, "{}", param.name)
}
ty::ConstKind::Value(ty, valtree) => {
match ty.kind() {
ty::ConstKind::Value(cv) => {
match cv.ty.kind() {
ty::Int(ity) => {
// FIXME: directly extract the bits from a valtree instead of evaluating an
// already evaluated `Const` in order to get the bits.
let bits = ct
let bits = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in codegen");
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
write!(output, "{val}")
}
ty::Uint(_) => {
let val = ct
let val = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in codegen");
write!(output, "{val}")
}
ty::Bool => {
let val = ct.try_to_bool().expect("expected monomorphic const in codegen");
let val = cv.try_to_bool().expect("expected monomorphic const in codegen");
write!(output, "{val}")
}
_ => {
@ -703,9 +701,7 @@ fn push_const_param<'tcx>(tcx: TyCtxt<'tcx>, ct: ty::Const<'tcx>, output: &mut S
// avoiding collisions and will make the emitted type names shorter.
let hash_short = tcx.with_stable_hashing_context(|mut hcx| {
let mut hasher = StableHasher::new();
hcx.while_hashing_spans(false, |hcx| {
(ty, valtree).hash_stable(hcx, &mut hasher)
});
hcx.while_hashing_spans(false, |hcx| cv.hash_stable(hcx, &mut hasher));
hasher.finish::<Hash64>()
});

View file

@ -43,7 +43,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Const::Ty(_, c) => match c.kind() {
// A constant that came from a const generic but was then used as an argument to
// old-style simd_shuffle (passing as argument instead of as a generic param).
rustc_type_ir::ConstKind::Value(_, valtree) => return Ok(Ok(valtree)),
rustc_type_ir::ConstKind::Value(cv) => return Ok(Ok(cv.valtree)),
other => span_bug!(constant.span, "{other:#?}"),
},
// We should never encounter `Const::Val` unless MIR opts (like const prop) evaluate

View file

@ -345,7 +345,7 @@ where
Const::Ty(_, ct)
if matches!(
ct.kind(),
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_, _)
ty::ConstKind::Param(_) | ty::ConstKind::Error(_) | ty::ConstKind::Value(_)
) =>
{
None

View file

@ -46,8 +46,13 @@ pub fn provide(providers: &mut Providers) {
};
providers.hooks.try_destructure_mir_constant_for_user_output =
const_eval::try_destructure_mir_constant_for_user_output;
providers.valtree_to_const_val = |tcx, (ty, valtree)| {
const_eval::valtree_to_const_value(tcx, ty::TypingEnv::fully_monomorphized(), ty, valtree)
providers.valtree_to_const_val = |tcx, cv| {
const_eval::valtree_to_const_value(
tcx,
ty::TypingEnv::fully_monomorphized(),
cv.ty,
cv.valtree,
)
};
providers.check_validity_requirement = |tcx, (init_kind, param_env_and_ty)| {
util::check_validity_requirement(tcx, init_kind, param_env_and_ty)

View file

@ -170,7 +170,7 @@ impl<'a, 'tcx> TypeFolder<TyCtxt<'tcx>> for TypeFreshener<'a, 'tcx> {
}
ty::ConstKind::Param(_)
| ty::ConstKind::Value(_, _)
| ty::ConstKind::Value(_)
| ty::ConstKind::Unevaluated(..)
| ty::ConstKind::Expr(..)
| ty::ConstKind::Error(_) => ct.super_fold_with(self),

View file

@ -1055,7 +1055,7 @@ impl<'tcx> InferCtxt<'tcx> {
| ty::ConstKind::Bound(_, _)
| ty::ConstKind::Placeholder(_)
| ty::ConstKind::Unevaluated(_)
| ty::ConstKind::Value(_, _)
| ty::ConstKind::Value(_)
| ty::ConstKind::Error(_)
| ty::ConstKind::Expr(_) => ct,
}

View file

@ -250,7 +250,7 @@ impl<'tcx> Const<'tcx> {
// Dont use the outer ty as on invalid code we can wind up with them not being the same.
// this then results in allowing const eval to add `1_i64 + 1_usize` in cases where the mir
// was originally `({N: usize} + 1_usize)` under `generic_const_exprs`.
ty::ConstKind::Value(ty, _) => ty,
ty::ConstKind::Value(cv) => cv.ty,
_ => *ty,
}
}
@ -264,7 +264,7 @@ impl<'tcx> Const<'tcx> {
pub fn is_required_const(&self) -> bool {
match self {
Const::Ty(_, c) => match c.kind() {
ty::ConstKind::Value(_, _) => false, // already a value, cannot error
ty::ConstKind::Value(_) => false, // already a value, cannot error
_ => true,
},
Const::Val(..) => false, // already a value, cannot error
@ -276,11 +276,11 @@ impl<'tcx> Const<'tcx> {
pub fn try_to_scalar(self) -> Option<Scalar> {
match self {
Const::Ty(_, c) => match c.kind() {
ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => {
ty::ConstKind::Value(cv) if cv.ty.is_primitive() => {
// A valtree of a type where leaves directly represent the scalar const value.
// Just checking whether it is a leaf is insufficient as e.g. references are leafs
// but the leaf value is the value they point to, not the reference itself!
Some(valtree.unwrap_leaf().into())
Some(cv.valtree.unwrap_leaf().into())
}
_ => None,
},
@ -295,9 +295,7 @@ impl<'tcx> Const<'tcx> {
match self {
Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
Const::Ty(_, c) => match c.kind() {
ty::ConstKind::Value(ty, valtree) if ty.is_primitive() => {
Some(valtree.unwrap_leaf())
}
ty::ConstKind::Value(cv) if cv.ty.is_primitive() => Some(cv.valtree.unwrap_leaf()),
_ => None,
},
_ => None,
@ -328,7 +326,7 @@ impl<'tcx> Const<'tcx> {
}
match c.kind() {
ConstKind::Value(ty, val) => Ok(tcx.valtree_to_const_val((ty, val))),
ConstKind::Value(cv) => Ok(tcx.valtree_to_const_val(cv)),
ConstKind::Expr(_) => {
bug!("Normalization of `ty::ConstKind::Expr` is unimplemented")
}
@ -353,13 +351,13 @@ impl<'tcx> Const<'tcx> {
typing_env: ty::TypingEnv<'tcx>,
) -> Option<Scalar> {
if let Const::Ty(_, c) = self
&& let ty::ConstKind::Value(ty, val) = c.kind()
&& ty.is_primitive()
&& let ty::ConstKind::Value(cv) = c.kind()
&& cv.ty.is_primitive()
{
// Avoid the `valtree_to_const_val` query. Can only be done on primitive types that
// are valtree leaves, and *not* on references. (References should return the
// pointer here, which valtrees don't represent.)
Some(val.unwrap_leaf().into())
Some(cv.valtree.unwrap_leaf().into())
} else {
self.eval(tcx, typing_env, DUMMY_SP).ok()?.try_to_scalar()
}
@ -473,7 +471,7 @@ impl<'tcx> Const<'tcx> {
// A valtree may be a reference. Valtree references correspond to a
// different allocation each time they are evaluated. Valtrees for primitive
// types are fine though.
ty::ConstKind::Value(ty, _) => ty.is_primitive(),
ty::ConstKind::Value(cv) => cv.ty.is_primitive(),
ty::ConstKind::Unevaluated(..) | ty::ConstKind::Expr(..) => false,
// This can happen if evaluation of a constant failed. The result does not matter
// much since compilation is doomed.

View file

@ -1441,7 +1441,9 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
ty::ConstKind::Unevaluated(uv) => {
format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
}
ty::ConstKind::Value(_, val) => format!("ty::Valtree({})", fmt_valtree(&val)),
ty::ConstKind::Value(cv) => {
format!("ty::Valtree({})", fmt_valtree(&cv.valtree))
}
// No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
ty::ConstKind::Error(_) => "Error".to_string(),
// These variants shouldn't exist in the MIR.

View file

@ -550,7 +550,7 @@ impl<'tcx> Key for (ty::Instance<'tcx>, &'tcx ty::List<Ty<'tcx>>) {
}
}
impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) {
impl<'tcx> Key for ty::Value<'tcx> {
type Cache<V> = DefaultCache<Self, V>;
fn default_span(&self, _: TyCtxt<'_>) -> Span {

View file

@ -1256,9 +1256,9 @@ rustc_queries! {
desc { "evaluating type-level constant" }
}
/// Converts a type level constant value into `ConstValue`
query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> mir::ConstValue<'tcx> {
desc { "converting type-level constant value to mir constant value"}
/// Converts a type-level constant value into a MIR constant value.
query valtree_to_const_val(key: ty::Value<'tcx>) -> mir::ConstValue<'tcx> {
desc { "converting type-level constant value to MIR constant value"}
}
/// Destructures array, ADT or tuple constants into the constants

View file

@ -110,8 +110,8 @@ impl<'tcx> Const<'tcx> {
}
#[inline]
pub fn new_value(tcx: TyCtxt<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
Const::new(tcx, ty::ConstKind::Value(ty, val))
pub fn new_value(tcx: TyCtxt<'tcx>, valtree: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> Const<'tcx> {
Const::new(tcx, ty::ConstKind::Value(ty::Value { ty, valtree }))
}
#[inline]
@ -214,47 +214,31 @@ impl<'tcx> Const<'tcx> {
Self::from_bits(tcx, n as u128, ty::TypingEnv::fully_monomorphized(), tcx.types.usize)
}
/// Panics if self.kind != ty::ConstKind::Value
pub fn to_valtree(self) -> (ty::ValTree<'tcx>, Ty<'tcx>) {
/// Panics if `self.kind != ty::ConstKind::Value`.
pub fn to_value(self) -> ty::Value<'tcx> {
match self.kind() {
ty::ConstKind::Value(ty, valtree) => (valtree, ty),
ty::ConstKind::Value(cv) => cv,
_ => bug!("expected ConstKind::Value, got {:?}", self.kind()),
}
}
/// Attempts to convert to a `ValTree`
pub fn try_to_valtree(self) -> Option<(ty::ValTree<'tcx>, Ty<'tcx>)> {
/// Attempts to convert to a value.
pub fn try_to_value(self) -> Option<ty::Value<'tcx>> {
match self.kind() {
ty::ConstKind::Value(ty, valtree) => Some((valtree, ty)),
ty::ConstKind::Value(cv) => Some(cv),
_ => None,
}
}
#[inline]
pub fn try_to_scalar(self) -> Option<(Scalar, Ty<'tcx>)> {
let (valtree, ty) = self.try_to_valtree()?;
Some((valtree.try_to_scalar()?, ty))
}
pub fn try_to_bool(self) -> Option<bool> {
self.try_to_valtree()?.0.try_to_scalar_int()?.try_to_bool().ok()
let cv = self.try_to_value()?;
Some((cv.valtree.try_to_scalar()?, cv.ty))
}
#[inline]
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
self.try_to_valtree()?.0.try_to_target_usize(tcx)
}
/// Attempts to evaluate the given constant to bits. Can fail to evaluate in the presence of
/// generics (or erroneous code) or if the value can't be represented as bits (e.g. because it
/// contains const generic parameters or pointers).
#[inline]
pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<u128> {
let (scalar, ty) = self.try_to_scalar()?;
let scalar = scalar.try_to_scalar_int().ok()?;
let input = typing_env.with_post_analysis_normalized(tcx).as_query_input(ty);
let size = tcx.layout_of(input).ok()?.size;
Some(scalar.to_bits(size))
self.try_to_value()?.try_to_target_usize(tcx)
}
pub fn is_ct_infer(self) -> bool {

View file

@ -1,11 +1,9 @@
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
use super::ScalarInt;
use crate::mir::interpret::Scalar;
use crate::ty::{self, Ty, TyCtxt};
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq)]
#[derive(HashStable)]
/// This datastructure is used to represent the value of constants used in the type system.
///
/// We explicitly choose a different datastructure from the way values are processed within
@ -18,6 +16,8 @@ use crate::ty::{self, Ty, TyCtxt};
///
/// `ValTree` does not have this problem with representation, as it only contains integers or
/// lists of (nested) `ValTree`.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[derive(HashStable, TyEncodable, TyDecodable)]
pub enum ValTree<'tcx> {
/// integers, `bool`, `char` are represented as scalars.
/// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
@ -79,10 +79,6 @@ impl<'tcx> ValTree<'tcx> {
}
}
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
self.try_to_scalar_int().map(|s| s.to_target_usize(tcx))
}
/// Get the values inside the ValTree as a slice of bytes. This only works for
/// constants with types &str, &[u8], or [u8; _].
pub fn try_to_raw_bytes(self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx [u8]> {
@ -107,3 +103,44 @@ impl<'tcx> ValTree<'tcx> {
)
}
}
/// A type-level constant value.
///
/// Represents a typed, fully evaluated constant.
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
#[derive(HashStable, TyEncodable, TyDecodable, TypeFoldable, TypeVisitable)]
pub struct Value<'tcx> {
pub ty: Ty<'tcx>,
pub valtree: ValTree<'tcx>,
}
impl<'tcx> Value<'tcx> {
/// Attempts to extract the raw bits from the constant.
///
/// Fails if the value can't be represented as bits (e.g. because it is an aggregate).
#[inline]
pub fn try_to_bits(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> Option<u128> {
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<bool> {
self.valtree.try_to_scalar_int()?.try_to_bool().ok()
}
pub fn try_to_target_usize(self, tcx: TyCtxt<'tcx>) -> Option<u64> {
self.valtree.try_to_scalar_int().map(|s| s.to_target_usize(tcx))
}
}
impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> {
fn ty(self) -> Ty<'tcx> {
self.ty
}
fn valtree(self) -> ValTree<'tcx> {
self.valtree
}
}

View file

@ -142,10 +142,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
type ParamConst = ty::ParamConst;
type BoundConst = ty::BoundVar;
type ValueConst = ty::ValTree<'tcx>;
type ValueConst = ty::Value<'tcx>;
type ExprConst = ty::Expr<'tcx>;
type Region = Region<'tcx>;
type ValTree = ty::ValTree<'tcx>;
type Region = Region<'tcx>;
type EarlyParamRegion = ty::EarlyParamRegion;
type LateParamRegion = ty::LateParamRegion;
type BoundRegion = ty::BoundRegion;
@ -1118,15 +1119,18 @@ impl<'tcx> CommonConsts<'tcx> {
};
CommonConsts {
unit: mk_const(ty::ConstKind::Value(types.unit, ty::ValTree::zst())),
true_: mk_const(ty::ConstKind::Value(
types.bool,
ty::ValTree::Leaf(ty::ScalarInt::TRUE),
)),
false_: mk_const(ty::ConstKind::Value(
types.bool,
ty::ValTree::Leaf(ty::ScalarInt::FALSE),
)),
unit: mk_const(ty::ConstKind::Value(ty::Value {
ty: types.unit,
valtree: ty::ValTree::zst(),
})),
true_: mk_const(ty::ConstKind::Value(ty::Value {
ty: types.bool,
valtree: ty::ValTree::Leaf(ty::ScalarInt::TRUE),
})),
false_: mk_const(ty::ConstKind::Value(ty::Value {
ty: types.bool,
valtree: ty::ValTree::Leaf(ty::ScalarInt::FALSE),
})),
}
}
}

View file

@ -381,7 +381,7 @@ impl FlagComputation {
self.add_flags(TypeFlags::HAS_CT_PLACEHOLDER);
self.add_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
}
ty::ConstKind::Value(ty, _) => self.add_ty(ty),
ty::ConstKind::Value(cv) => self.add_ty(cv.ty),
ty::ConstKind::Expr(e) => self.add_args(e.args()),
ty::ConstKind::Error(_) => self.add_flags(TypeFlags::HAS_ERROR),
}

View file

@ -60,7 +60,7 @@ pub use self::closure::{
place_to_string_for_capture,
};
pub use self::consts::{
Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree,
Const, ConstInt, ConstKind, Expr, ExprKind, ScalarInt, UnevaluatedConst, ValTree, Value,
};
pub use self::context::{
CtxtInterners, CurrentGcx, DeducedParamAttrs, Feed, FreeRegionInfo, GlobalCtxt, Lift, TyCtxt,

View file

@ -1484,8 +1484,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
_ => write!(self, "_")?,
},
ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)),
ty::ConstKind::Value(ty, value) => {
return self.pretty_print_const_valtree(value, ty, print_ty);
ty::ConstKind::Value(cv) => {
return self.pretty_print_const_valtree(cv.valtree, cv.ty, print_ty);
}
ty::ConstKind::Bound(debruijn, bound_var) => {
@ -1637,33 +1637,32 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
match ty.kind() {
// Byte strings (&[u8; N])
ty::Ref(_, inner, _) => {
if let ty::Array(elem, len) = inner.kind() {
if let ty::Uint(ty::UintTy::U8) = elem.kind() {
if let ty::ConstKind::Value(_, ty::ValTree::Leaf(int)) = len.kind() {
match self.tcx().try_get_global_alloc(prov.alloc_id()) {
Some(GlobalAlloc::Memory(alloc)) => {
let len = int.to_bits(self.tcx().data_layout.pointer_size);
let range =
AllocRange { start: offset, size: Size::from_bytes(len) };
if let Ok(byte_str) =
alloc.inner().get_bytes_strip_provenance(&self.tcx(), range)
{
p!(pretty_print_byte_str(byte_str))
} else {
p!("<too short allocation>")
}
}
// FIXME: for statics, vtables, and functions, we could in principle print more detail.
Some(GlobalAlloc::Static(def_id)) => {
p!(write("<static({:?})>", def_id))
}
Some(GlobalAlloc::Function { .. }) => p!("<function>"),
Some(GlobalAlloc::VTable(..)) => p!("<vtable>"),
None => p!("<dangling pointer>"),
if let ty::Array(elem, len) = inner.kind()
&& let ty::Uint(ty::UintTy::U8) = elem.kind()
&& let ty::ConstKind::Value(cv) = len.kind()
&& let ty::ValTree::Leaf(int) = cv.valtree
{
match self.tcx().try_get_global_alloc(prov.alloc_id()) {
Some(GlobalAlloc::Memory(alloc)) => {
let len = int.to_bits(self.tcx().data_layout.pointer_size);
let range = AllocRange { start: offset, size: Size::from_bytes(len) };
if let Ok(byte_str) =
alloc.inner().get_bytes_strip_provenance(&self.tcx(), range)
{
p!(pretty_print_byte_str(byte_str))
} else {
p!("<too short allocation>")
}
return Ok(());
}
// FIXME: for statics, vtables, and functions, we could in principle print more detail.
Some(GlobalAlloc::Static(def_id)) => {
p!(write("<static({:?})>", def_id))
}
Some(GlobalAlloc::Function { .. }) => p!("<function>"),
Some(GlobalAlloc::VTable(..)) => p!("<vtable>"),
None => p!("<dangling pointer>"),
}
return Ok(());
}
}
ty::FnPtr(..) => {

View file

@ -162,16 +162,15 @@ impl<'tcx> fmt::Debug for ty::consts::Expr<'tcx> {
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(_, _) = self.kind() {
if let ConstKind::Value(_) = self.kind() {
return ty::tls::with(move |tcx| {
// Somehow trying to lift the valtree results in lifetime errors, so we lift the
// entire constant.
// ValTrees aren't interned, so we lift the entire constant.
let lifted = tcx.lift(*self).unwrap();
let ConstKind::Value(ty, valtree) = lifted.kind() else {
let ConstKind::Value(cv) = lifted.kind() else {
bug!("we checked that this is a valtree")
};
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
cx.pretty_print_const_valtree(valtree, ty, /*print_ty*/ true)?;
cx.pretty_print_const_valtree(cv.valtree, cv.ty, /*print_ty*/ true)?;
f.write_str(&cx.into_buffer())
});
}
@ -589,9 +588,7 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for ty::Const<'tcx> {
}
ConstKind::Placeholder(p) => ConstKind::Placeholder(p.try_fold_with(folder)?),
ConstKind::Unevaluated(uv) => ConstKind::Unevaluated(uv.try_fold_with(folder)?),
ConstKind::Value(t, v) => {
ConstKind::Value(t.try_fold_with(folder)?, v.try_fold_with(folder)?)
}
ConstKind::Value(v) => ConstKind::Value(v.try_fold_with(folder)?),
ConstKind::Error(e) => ConstKind::Error(e.try_fold_with(folder)?),
ConstKind::Expr(e) => ConstKind::Expr(e.try_fold_with(folder)?),
};
@ -610,10 +607,7 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for ty::Const<'tcx> {
}
ConstKind::Placeholder(p) => p.visit_with(visitor),
ConstKind::Unevaluated(uv) => uv.visit_with(visitor),
ConstKind::Value(t, v) => {
try_visit!(t.visit_with(visitor));
v.visit_with(visitor)
}
ConstKind::Value(v) => v.visit_with(visitor),
ConstKind::Error(e) => e.visit_with(visitor),
ConstKind::Expr(e) => e.visit_with(visitor),
}

View file

@ -206,7 +206,7 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
| ty::ConstKind::Bound(..)
| ty::ConstKind::Error(_) => {}
ty::ConstKind::Value(ty, _) => stack.push(ty.into()),
ty::ConstKind::Value(cv) => stack.push(cv.ty.into()),
ty::ConstKind::Expr(expr) => stack.extend(expr.args().iter().rev()),
ty::ConstKind::Unevaluated(ct) => {

View file

@ -46,7 +46,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
match c.kind() {
ty::ConstKind::Unevaluated(uv) => convert.unevaluated_to_pat(uv, ty),
ty::ConstKind::Value(_, val) => convert.valtree_to_pat(val, ty),
ty::ConstKind::Value(cv) => convert.valtree_to_pat(cv.valtree, cv.ty),
_ => span_bug!(span, "Invalid `ConstKind` for `const_to_pat`: {:?}", c),
}
}

View file

@ -522,7 +522,7 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
// FIXME: See comment above -- we could fold the region separately or something.
ty::ConstKind::Bound(_, _)
| ty::ConstKind::Unevaluated(_)
| ty::ConstKind::Value(_, _)
| ty::ConstKind::Value(_)
| ty::ConstKind::Error(_)
| ty::ConstKind::Expr(_) => return c.super_fold_with(self),
};

View file

@ -160,9 +160,7 @@ where
ty::ConstKind::Infer(_) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
ty::ConstKind::Placeholder(_)
| ty::ConstKind::Value(_, _)
| ty::ConstKind::Error(_) => {
ty::ConstKind::Placeholder(_) | ty::ConstKind::Value(_) | ty::ConstKind::Error(_) => {
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// We can freely ICE here as:
@ -199,7 +197,7 @@ where
unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`")
}
ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct),
ty::ConstKind::Value(ty, _) => ty,
ty::ConstKind::Value(cv) => cv.ty(),
ty::ConstKind::Placeholder(placeholder) => {
self.cx().find_const_ty_from_env(goal.param_env, placeholder)
}

View file

@ -103,7 +103,7 @@ fn encode_args<'tcx>(
/// <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling.literal>).
fn encode_const<'tcx>(
tcx: TyCtxt<'tcx>,
c: Const<'tcx>,
ct: Const<'tcx>,
ct_ty: Ty<'tcx>,
dict: &mut FxHashMap<DictKey<'tcx>, usize>,
options: EncodeTyOptions,
@ -111,7 +111,7 @@ fn encode_const<'tcx>(
// L<element-type>[n][<element-value>]E as literal argument
let mut s = String::from('L');
match c.kind() {
match ct.kind() {
// Const parameters
ty::ConstKind::Param(..) => {
// L<element-type>E as literal argument
@ -121,18 +121,18 @@ fn encode_const<'tcx>(
}
// Literal arguments
ty::ConstKind::Value(ct_ty, ..) => {
ty::ConstKind::Value(cv) => {
// L<element-type>[n]<element-value>E as literal argument
// Element type
s.push_str(&encode_ty(tcx, ct_ty, dict, options));
s.push_str(&encode_ty(tcx, cv.ty, dict, options));
// The only allowed types of const values are bool, u8, u16, u32,
// u64, u128, usize i8, i16, i32, i64, i128, isize, and char. The
// bool value false is encoded as 0 and true as 1.
match ct_ty.kind() {
match cv.ty.kind() {
ty::Int(ity) => {
let bits = c
let bits = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in cfi");
let val = Integer::from_int_ty(&tcx, *ity).size().sign_extend(bits) as i128;
@ -142,30 +142,30 @@ fn encode_const<'tcx>(
let _ = write!(s, "{val}");
}
ty::Uint(_) => {
let val = c
let val = cv
.try_to_bits(tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected monomorphic const in cfi");
let _ = write!(s, "{val}");
}
ty::Bool => {
let val = c.try_to_bool().expect("expected monomorphic const in cfi");
let val = cv.try_to_bool().expect("expected monomorphic const in cfi");
let _ = write!(s, "{val}");
}
_ => {
bug!("encode_const: unexpected type `{:?}`", ct_ty);
bug!("encode_const: unexpected type `{:?}`", cv.ty);
}
}
}
_ => {
bug!("encode_const: unexpected kind `{:?}`", c.kind());
bug!("encode_const: unexpected kind `{:?}`", ct.kind());
}
}
// Close the "L..E" pair
s.push('E');
compress(dict, DictKey::Const(c), &mut s);
compress(dict, DictKey::Const(ct), &mut s);
s
}

View file

@ -450,8 +450,9 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
let tcx = tables.tcx;
let ty = ty::Ty::new_static_str(tcx);
let bytes = value.as_bytes();
let val_tree = ty::ValTree::from_raw_bytes(tcx, bytes);
let val = tcx.valtree_to_const_val((ty, val_tree));
let valtree = ty::ValTree::from_raw_bytes(tcx, bytes);
let cv = ty::Value { ty, valtree };
let val = tcx.valtree_to_const_val(cv);
mir::Const::from_value(val, ty).stable(&mut tables)
}

View file

@ -418,23 +418,16 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> {
type T = stable_mir::ty::TyConst;
fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
let kind = match self.kind() {
ty::ConstKind::Value(ty, val) => {
let val = match val {
ty::ValTree::Leaf(scalar) => ty::ValTree::Leaf(scalar),
ty::ValTree::Branch(branch) => {
ty::ValTree::Branch(tables.tcx.lift(branch).unwrap())
}
};
let ty = tables.tcx.lift(ty).unwrap();
let const_val = tables.tcx.valtree_to_const_val((ty, val));
let ct = tables.tcx.lift(*self).unwrap();
let kind = match ct.kind() {
ty::ConstKind::Value(cv) => {
let const_val = tables.tcx.valtree_to_const_val(cv);
if matches!(const_val, mir::ConstValue::ZeroSized) {
stable_mir::ty::TyConstKind::ZSTValue(ty.stable(tables))
stable_mir::ty::TyConstKind::ZSTValue(cv.ty.stable(tables))
} else {
stable_mir::ty::TyConstKind::Value(
ty.stable(tables),
alloc::new_allocation(ty, const_val, tables),
cv.ty.stable(tables),
alloc::new_allocation(cv.ty, const_val, tables),
)
}
}
@ -449,7 +442,7 @@ impl<'tcx> Stable<'tcx> for ty::Const<'tcx> {
ty::ConstKind::Placeholder(_) => unimplemented!(),
ty::ConstKind::Expr(_) => unimplemented!(),
};
let id = tables.intern_ty_const(tables.tcx.lift(*self).unwrap());
let id = tables.intern_ty_const(ct);
stable_mir::ty::TyConst::new(kind, id)
}
}

View file

@ -274,14 +274,15 @@ impl<'tcx> Printer<'tcx> for SymbolPrinter<'tcx> {
fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
// only print integers
match ct.kind() {
ty::ConstKind::Value(ty, ty::ValTree::Leaf(scalar)) if ty.is_integral() => {
ty::ConstKind::Value(cv) if cv.ty.is_integral() => {
// The `pretty_print_const` formatting depends on -Zverbose-internals
// flag, so we cannot reuse it here.
let signed = matches!(ty.kind(), ty::Int(_));
let scalar = cv.valtree.unwrap_leaf();
let signed = matches!(cv.ty.kind(), ty::Int(_));
write!(
self,
"{:#?}",
ty::ConstInt::new(scalar, signed, ty.is_ptr_sized_integral())
ty::ConstInt::new(scalar, signed, cv.ty.is_ptr_sized_integral())
)?;
}
_ => self.write_str("_")?,

View file

@ -590,8 +590,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
fn print_const(&mut self, ct: ty::Const<'tcx>) -> Result<(), PrintError> {
// We only mangle a typed value if the const can be evaluated.
let (ct_ty, valtree) = match ct.kind() {
ty::ConstKind::Value(ty, val) => (ty, val),
let cv = match ct.kind() {
ty::ConstKind::Value(cv) => cv,
// Should only be encountered within the identity-substituted
// impl header of an item nested within an impl item.
@ -619,13 +619,14 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
return Ok(());
}
let ty::Value { ty: ct_ty, valtree } = cv;
let start = self.out.len();
match ct_ty.kind() {
ty::Uint(_) | ty::Int(_) | ty::Bool | ty::Char => {
ct_ty.print(self)?;
let mut bits = ct
let mut bits = cv
.try_to_bits(self.tcx, ty::TypingEnv::fully_monomorphized())
.expect("expected const to be monomorphic");

View file

@ -264,7 +264,7 @@ fn fulfillment_error_for_no_solution<'tcx>(
infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
}
ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(obligation.param_env),
ty::ConstKind::Value(ty, _) => ty,
ty::ConstKind::Value(cv) => cv.ty,
kind => span_bug!(
obligation.cause.span,
"ConstArgHasWrongType failed but we don't know how to compute type for {kind:?}"

View file

@ -35,7 +35,7 @@ pub fn is_const_evaluatable<'tcx>(
ty::ConstKind::Param(_)
| ty::ConstKind::Bound(_, _)
| ty::ConstKind::Placeholder(_)
| ty::ConstKind::Value(_, _)
| ty::ConstKind::Value(_)
| ty::ConstKind::Error(_) => return Ok(()),
ty::ConstKind::Infer(_) => return Err(NotConstEvaluatable::MentionsInfer),
};

View file

@ -479,7 +479,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
ty::ConstKind::Error(_) => {
return ProcessResult::Changed(PendingPredicateObligations::new());
}
ty::ConstKind::Value(ty, _) => ty,
ty::ConstKind::Value(cv) => cv.ty,
ty::ConstKind::Unevaluated(uv) => {
infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
}

View file

@ -962,7 +962,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
return Ok(EvaluatedToAmbig);
}
ty::ConstKind::Error(_) => return Ok(EvaluatedToOk),
ty::ConstKind::Value(ty, _) => ty,
ty::ConstKind::Value(cv) => cv.ty,
ty::ConstKind::Unevaluated(uv) => {
self.tcx().type_of(uv.def).instantiate(self.tcx(), uv.args)
}

View file

@ -128,16 +128,16 @@ mod rustc {
pub fn from_const<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
c: Const<'tcx>,
ct: Const<'tcx>,
) -> Option<Self> {
use rustc_middle::ty::ScalarInt;
use rustc_span::sym;
let Some((cv, ty)) = c.try_to_valtree() else {
let Some(cv) = ct.try_to_value() else {
return None;
};
let adt_def = ty.ty_adt_def()?;
let adt_def = cv.ty.ty_adt_def()?;
assert_eq!(
tcx.require_lang_item(LangItem::TransmuteOpts, None),
@ -147,7 +147,7 @@ mod rustc {
);
let variant = adt_def.non_enum_variant();
let fields = match cv {
let fields = match cv.valtree {
ValTree::Branch(branch) => branch,
_ => {
return Some(Self {

View file

@ -22,16 +22,16 @@ fn destructure_const<'tcx>(
tcx: TyCtxt<'tcx>,
const_: ty::Const<'tcx>,
) -> ty::DestructuredConst<'tcx> {
let ty::ConstKind::Value(ct_ty, valtree) = const_.kind() else {
let ty::ConstKind::Value(cv) = const_.kind() else {
bug!("cannot destructure constant {:?}", const_)
};
let branches = match valtree {
let branches = match cv.valtree {
ty::ValTree::Branch(b) => b,
_ => bug!("cannot destructure constant {:?}", const_),
};
let (fields, variant) = match ct_ty.kind() {
let (fields, variant) = match cv.ty.kind() {
ty::Array(inner_ty, _) | ty::Slice(inner_ty) => {
// construct the consts for the elements of the array/slice
let field_consts = branches

View file

@ -144,13 +144,13 @@ fn univariant_uninterned<'tcx>(
cx.calc.univariant(fields, repr, kind).map_err(|err| map_error(cx, ty, err))
}
fn validate_const_with_value<'tcx>(
fn extract_const_value<'tcx>(
const_: ty::Const<'tcx>,
ty: Ty<'tcx>,
cx: &LayoutCx<'tcx>,
) -> Result<ty::Const<'tcx>, &'tcx LayoutError<'tcx>> {
) -> Result<ty::Value<'tcx>, &'tcx LayoutError<'tcx>> {
match const_.kind() {
ty::ConstKind::Value(..) => Ok(const_),
ty::ConstKind::Value(cv) => Ok(cv),
ty::ConstKind::Error(guar) => {
return Err(error(cx, LayoutError::ReferencesError(guar)));
}
@ -209,13 +209,12 @@ fn layout_of_uncached<'tcx>(
&mut layout.backend_repr
{
if let Some(start) = start {
scalar.valid_range_mut().start =
validate_const_with_value(start, ty, cx)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
scalar.valid_range_mut().start = extract_const_value(start, ty, cx)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
}
if let Some(end) = end {
let mut end = validate_const_with_value(end, ty, cx)?
let mut end = extract_const_value(end, ty, cx)?
.try_to_bits(tcx, cx.typing_env)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;
if !include_end {
@ -348,9 +347,7 @@ fn layout_of_uncached<'tcx>(
// Arrays and slices.
ty::Array(element, count) => {
let count = validate_const_with_value(count, ty, cx)?
.to_valtree()
.0
let count = extract_const_value(count, ty, cx)?
.try_to_target_usize(tcx)
.ok_or_else(|| error(cx, LayoutError::Unknown(ty)))?;

View file

@ -31,7 +31,7 @@ pub enum ConstKind<I: Interner> {
Unevaluated(ty::UnevaluatedConst<I>),
/// Used to hold computed value.
Value(I::Ty, I::ValueConst),
Value(I::ValueConst),
/// A placeholder for a const which could not be computed; this is
/// propagated to avoid useless error messages.
@ -52,7 +52,7 @@ impl<I: Interner> fmt::Debug for ConstKind<I> {
Bound(debruijn, var) => crate::debug_bound_var(f, *debruijn, var),
Placeholder(placeholder) => write!(f, "{placeholder:?}"),
Unevaluated(uv) => write!(f, "{uv:?}"),
Value(ty, valtree) => write!(f, "({valtree:?}: {ty:?})"),
Value(val) => write!(f, "{val:?}"),
Error(_) => write!(f, "{{const error}}"),
Expr(expr) => write!(f, "{expr:?}"),
}

View file

@ -483,8 +483,8 @@ impl<I: Interner, const INSTANTIATE_LHS_WITH_INFER: bool, const INSTANTIATE_RHS_
};
match lhs.kind() {
ty::ConstKind::Value(_, lhs_val) => match rhs.kind() {
ty::ConstKind::Value(_, rhs_val) => lhs_val == rhs_val,
ty::ConstKind::Value(lhs_val) => match rhs.kind() {
ty::ConstKind::Value(rhs_val) => lhs_val.valtree() == rhs_val.valtree(),
_ => false,
},

View file

@ -286,6 +286,11 @@ pub trait Const<I: Interner<Const = Self>>:
}
}
pub trait ValueConst<I: Interner<ValueConst = Self>>: Copy + Debug + Hash + Eq {
fn ty(self) -> I::Ty;
fn valtree(self) -> I::ValTree;
}
pub trait ExprConst<I: Interner<ExprConst = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
fn args(self) -> I::GenericArgs;
}

View file

@ -112,8 +112,9 @@ pub trait Interner:
type PlaceholderConst: PlaceholderLike;
type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
type ValueConst: Copy + Debug + Hash + Eq;
type ValueConst: ValueConst<Self>;
type ExprConst: ExprConst<Self>;
type ValTree: Copy + Debug + Hash + Eq;
// Kinds of regions
type Region: Region<Self>;

View file

@ -606,7 +606,9 @@ pub fn structurally_relate_consts<I: Interner, R: TypeRelation<I>>(
true
}
(ty::ConstKind::Placeholder(p1), ty::ConstKind::Placeholder(p2)) => p1 == p2,
(ty::ConstKind::Value(_, a_val), ty::ConstKind::Value(_, b_val)) => a_val == b_val,
(ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => {
a_val.valtree() == b_val.valtree()
}
// While this is slightly incorrect, it shouldn't matter for `min_const_generics`
// and is the better alternative to waiting until `generic_const_exprs` can

View file

@ -344,10 +344,8 @@ pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
s
}
// array lengths are obviously usize
ty::ConstKind::Value(ty, ty::ValTree::Leaf(scalar))
if *ty.kind() == ty::Uint(ty::UintTy::Usize) =>
{
scalar.to_string()
ty::ConstKind::Value(cv) if *cv.ty.kind() == ty::Uint(ty::UintTy::Usize) => {
cv.valtree.unwrap_leaf().to_string()
}
_ => n.to_string(),
}

View file

@ -56,9 +56,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
&& !item.span.from_expansion()
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
&& let ty::Array(element_type, cst) = ty.kind()
&& let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx
.try_normalize_erasing_regions(cx.typing_env(), *cst).unwrap_or(*cst).try_to_valtree()
&& let element_count = element_count.to_target_usize(cx.tcx)
&& let Some(element_count) = cx.tcx
.try_normalize_erasing_regions(cx.typing_env(), *cst).unwrap_or(*cst).try_to_target_usize(cx.tcx)
&& let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
&& u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size)
{

View file

@ -8,7 +8,7 @@ use clippy_utils::source::snippet;
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, ConstKind};
use rustc_middle::ty;
use rustc_session::impl_lint_pass;
use rustc_span::{Span, sym};
@ -81,8 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
&& let ExprKind::Repeat(_, _) | ExprKind::Array(_) = expr.kind
&& !self.is_from_vec_macro(cx, expr.span)
&& let ty::Array(element_type, cst) = cx.typeck_results().expr_ty(expr).kind()
&& let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind()
&& let element_count = element_count.to_target_usize(cx.tcx)
&& let Some(element_count) = cst.try_to_target_usize(cx.tcx)
&& let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
&& !cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| {
matches!(

View file

@ -640,7 +640,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let (dest, dest_len) = this.project_to_simd(dest)?;
let index =
generic_args[2].expect_const().try_to_valtree().unwrap().0.unwrap_branch();
generic_args[2].expect_const().to_value().valtree.unwrap_branch();
let index_len = index.len();
assert_eq!(left_len, right_len);