1
Fork 0

Rollup merge of #136430 - FedericoBruzzone:follow-up-136180, r=oli-obk

Use the type-level constant value `ty::Value` where needed

**Follow-up to #136180**

### Summary

This PR refactors functions to accept a single type-level constant value `ty::Value` instead of separate `ty::ValTree` and `ty::Ty` parameters:

- `valtree_to_const_value`: now takes `ty::Value`
- `pretty_print_const_valtree`: now takes `ty::Value`
- Uses `pretty_print_const_valtree` for formatting valtrees  when `visit_const_operand`
- Moves `try_to_raw_bytes` from `ty::Valtree` to `ty::Value`

---

r? ``@lukas-code`` ``@oli-obk``
This commit is contained in:
Matthias Krüger 2025-02-03 21:11:35 +01:00 committed by GitHub
commit 7e0118cdd2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 79 additions and 78 deletions

View file

@ -273,59 +273,63 @@ pub(crate) fn eval_to_valtree<'tcx>(
/// Converts a `ValTree` to a `ConstValue`, which is needed after mir /// Converts a `ValTree` to a `ConstValue`, which is needed after mir
/// construction has finished. /// construction has finished.
// FIXME(valtrees): Merge `valtree_to_const_value` and `valtree_into_mplace` into one function // FIXME(valtrees): Merge `valtree_to_const_value` and `valtree_into_mplace` into one function
// FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
#[instrument(skip(tcx), level = "debug", ret)] #[instrument(skip(tcx), level = "debug", ret)]
pub fn valtree_to_const_value<'tcx>( pub fn valtree_to_const_value<'tcx>(
tcx: TyCtxt<'tcx>, tcx: TyCtxt<'tcx>,
typing_env: ty::TypingEnv<'tcx>, typing_env: ty::TypingEnv<'tcx>,
ty: Ty<'tcx>, cv: ty::Value<'tcx>,
valtree: ty::ValTree<'tcx>,
) -> mir::ConstValue<'tcx> { ) -> mir::ConstValue<'tcx> {
// Basic idea: We directly construct `Scalar` values from trivial `ValTree`s // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s
// (those for constants with type bool, int, uint, float or char). // (those for constants with type bool, int, uint, float or char).
// For all other types we create an `MPlace` and fill that by walking // For all other types we create an `MPlace` and fill that by walking
// the `ValTree` and using `place_projection` and `place_field` to // the `ValTree` and using `place_projection` and `place_field` to
// create inner `MPlace`s which are filled recursively. // create inner `MPlace`s which are filled recursively.
// FIXME Does this need an example? // FIXME: Does this need an example?
match *ty.kind() { match *cv.ty.kind() {
ty::FnDef(..) => { ty::FnDef(..) => {
assert!(valtree.unwrap_branch().is_empty()); assert!(cv.valtree.unwrap_branch().is_empty());
mir::ConstValue::ZeroSized mir::ConstValue::ZeroSized
} }
ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char | ty::RawPtr(_, _) => { ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char | ty::RawPtr(_, _) => {
match valtree { match cv.valtree {
ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)), ty::ValTree::Leaf(scalar_int) => mir::ConstValue::Scalar(Scalar::Int(scalar_int)),
ty::ValTree::Branch(_) => bug!( ty::ValTree::Branch(_) => bug!(
"ValTrees for Bool, Int, Uint, Float, Char or RawPtr should have the form ValTree::Leaf" "ValTrees for Bool, Int, Uint, Float, Char or RawPtr should have the form ValTree::Leaf"
), ),
} }
} }
ty::Pat(ty, _) => valtree_to_const_value(tcx, typing_env, ty, valtree), ty::Pat(ty, _) => {
let cv = ty::Value { valtree: cv.valtree, ty };
valtree_to_const_value(tcx, typing_env, cv)
}
ty::Ref(_, inner_ty, _) => { ty::Ref(_, inner_ty, _) => {
let mut ecx = let mut ecx =
mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, typing_env, CanAccessMutGlobal::No); mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, typing_env, CanAccessMutGlobal::No);
let imm = valtree_to_ref(&mut ecx, valtree, inner_ty); let imm = valtree_to_ref(&mut ecx, cv.valtree, inner_ty);
let imm = let imm = ImmTy::from_immediate(
ImmTy::from_immediate(imm, tcx.layout_of(typing_env.as_query_input(ty)).unwrap()); imm,
tcx.layout_of(typing_env.as_query_input(cv.ty)).unwrap(),
);
op_to_const(&ecx, &imm.into(), /* for diagnostics */ false) op_to_const(&ecx, &imm.into(), /* for diagnostics */ false)
} }
ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => { ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
let layout = tcx.layout_of(typing_env.as_query_input(ty)).unwrap(); let layout = tcx.layout_of(typing_env.as_query_input(cv.ty)).unwrap();
if layout.is_zst() { if layout.is_zst() {
// Fast path to avoid some allocations. // Fast path to avoid some allocations.
return mir::ConstValue::ZeroSized; return mir::ConstValue::ZeroSized;
} }
if layout.backend_repr.is_scalar() if layout.backend_repr.is_scalar()
&& (matches!(ty.kind(), ty::Tuple(_)) && (matches!(cv.ty.kind(), ty::Tuple(_))
|| matches!(ty.kind(), ty::Adt(def, _) if def.is_struct())) || matches!(cv.ty.kind(), ty::Adt(def, _) if def.is_struct()))
{ {
// A Scalar tuple/struct; we can avoid creating an allocation. // A Scalar tuple/struct; we can avoid creating an allocation.
let branches = valtree.unwrap_branch(); let branches = cv.valtree.unwrap_branch();
// Find the non-ZST field. (There can be aligned ZST!) // Find the non-ZST field. (There can be aligned ZST!)
for (i, &inner_valtree) in branches.iter().enumerate() { for (i, &inner_valtree) in branches.iter().enumerate() {
let field = layout.field(&LayoutCx::new(tcx, typing_env), i); let field = layout.field(&LayoutCx::new(tcx, typing_env), i);
if !field.is_zst() { if !field.is_zst() {
return valtree_to_const_value(tcx, typing_env, field.ty, inner_valtree); let cv = ty::Value { valtree: inner_valtree, ty: field.ty };
return valtree_to_const_value(tcx, typing_env, cv);
} }
} }
bug!("could not find non-ZST field during in {layout:#?}"); bug!("could not find non-ZST field during in {layout:#?}");
@ -335,9 +339,9 @@ pub fn valtree_to_const_value<'tcx>(
mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, typing_env, CanAccessMutGlobal::No); mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, typing_env, CanAccessMutGlobal::No);
// Need to create a place for this valtree. // Need to create a place for this valtree.
let place = create_valtree_place(&mut ecx, layout, valtree); let place = create_valtree_place(&mut ecx, layout, cv.valtree);
valtree_into_mplace(&mut ecx, &place, valtree); valtree_into_mplace(&mut ecx, &place, cv.valtree);
dump_place(&ecx, &place); dump_place(&ecx, &place);
intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap(); intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
@ -362,7 +366,7 @@ pub fn valtree_to_const_value<'tcx>(
| ty::Slice(_) | ty::Slice(_)
| ty::Dynamic(..) | ty::Dynamic(..)
| ty::UnsafeBinder(_) => { | ty::UnsafeBinder(_) => {
bug!("no ValTree should have been created for type {:?}", ty.kind()) bug!("no ValTree should have been created for type {:?}", cv.ty.kind())
} }
} }
} }

View file

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

View file

@ -12,6 +12,7 @@ use rustc_middle::mir::interpret::{
use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*; use rustc_middle::mir::*;
use tracing::trace; use tracing::trace;
use ty::print::PrettyPrinter;
use super::graphviz::write_mir_fn_graphviz; use super::graphviz::write_mir_fn_graphviz;
use crate::mir::interpret::ConstAllocation; use crate::mir::interpret::ConstAllocation;
@ -1439,10 +1440,10 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
}) })
}; };
// FIXME: call pretty_print_const_valtree? let fmt_valtree = |cv: &ty::Value<'tcx>| {
let fmt_valtree = |valtree: &ty::ValTree<'tcx>| match valtree { let mut cx = FmtPrinter::new(self.tcx, Namespace::ValueNS);
ty::ValTree::Leaf(leaf) => format!("Leaf({leaf:?})"), cx.pretty_print_const_valtree(*cv, /*print_ty*/ true).unwrap();
ty::ValTree::Branch(_) => "Branch(..)".to_string(), cx.into_buffer()
}; };
let val = match const_ { let val = match const_ {
@ -1452,7 +1453,7 @@ impl<'tcx> Visitor<'tcx> for ExtraComments<'tcx> {
format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,) format!("ty::Unevaluated({}, {:?})", self.tcx.def_path_str(uv.def), uv.args,)
} }
ty::ConstKind::Value(cv) => { ty::ConstKind::Value(cv) => {
format!("ty::Valtree({})", fmt_valtree(&cv.valtree)) format!("ty::Valtree({})", fmt_valtree(&cv))
} }
// No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`. // No `ty::` prefix since we also use this to represent errors from `mir::Unevaluated`.
ty::ConstKind::Error(_) => "Error".to_string(), ty::ConstKind::Error(_) => "Error".to_string(),

View file

@ -78,30 +78,6 @@ impl<'tcx> ValTree<'tcx> {
Self::Branch(_) => None, Self::Branch(_) => None,
} }
} }
/// 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]> {
match ty.kind() {
ty::Ref(_, inner_ty, _) => match inner_ty.kind() {
// `&str` can be interpreted as raw bytes
ty::Str => {}
// `&[u8]` can be interpreted as raw bytes
ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {}
// other `&_` can't be interpreted as raw bytes
_ => return None,
},
// `[u8; N]` can be interpreted as raw bytes
ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {}
// Otherwise, type cannot be interpreted as raw bytes
_ => return None,
}
Some(
tcx.arena
.alloc_from_iter(self.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().to_u8())),
)
}
} }
/// A type-level constant value. /// A type-level constant value.
@ -143,6 +119,29 @@ impl<'tcx> Value<'tcx> {
} }
self.valtree.try_to_scalar_int().map(|s| s.to_target_usize(tcx)) self.valtree.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>) -> Option<&'tcx [u8]> {
match self.ty.kind() {
ty::Ref(_, inner_ty, _) => match inner_ty.kind() {
// `&str` can be interpreted as raw bytes
ty::Str => {}
// `&[u8]` can be interpreted as raw bytes
ty::Slice(slice_ty) if *slice_ty == tcx.types.u8 => {}
// other `&_` can't be interpreted as raw bytes
_ => return None,
},
// `[u8; N]` can be interpreted as raw bytes
ty::Array(array_ty, _) if *array_ty == tcx.types.u8 => {}
// Otherwise, type cannot be interpreted as raw bytes
_ => return None,
}
Some(tcx.arena.alloc_from_iter(
self.valtree.unwrap_branch().into_iter().map(|v| v.unwrap_leaf().to_u8()),
))
}
} }
impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> { impl<'tcx> rustc_type_ir::inherent::ValueConst<TyCtxt<'tcx>> for Value<'tcx> {

View file

@ -1487,7 +1487,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
}, },
ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)), ty::ConstKind::Param(ParamConst { name, .. }) => p!(write("{}", name)),
ty::ConstKind::Value(cv) => { ty::ConstKind::Value(cv) => {
return self.pretty_print_const_valtree(cv.valtree, cv.ty, print_ty); return self.pretty_print_const_valtree(cv, print_ty);
} }
ty::ConstKind::Bound(debruijn, bound_var) => { ty::ConstKind::Bound(debruijn, bound_var) => {
@ -1787,48 +1787,47 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
Ok(()) Ok(())
} }
// FIXME(valtrees): Accept `ty::Value` instead of `Ty` and `ty::ValTree` separately.
fn pretty_print_const_valtree( fn pretty_print_const_valtree(
&mut self, &mut self,
valtree: ty::ValTree<'tcx>, cv: ty::Value<'tcx>,
ty: Ty<'tcx>,
print_ty: bool, print_ty: bool,
) -> Result<(), PrintError> { ) -> Result<(), PrintError> {
define_scoped_cx!(self); define_scoped_cx!(self);
if self.should_print_verbose() { if self.should_print_verbose() {
p!(write("ValTree({:?}: ", valtree), print(ty), ")"); p!(write("ValTree({:?}: ", cv.valtree), print(cv.ty), ")");
return Ok(()); return Ok(());
} }
let u8_type = self.tcx().types.u8; let u8_type = self.tcx().types.u8;
match (valtree, ty.kind()) { match (cv.valtree, cv.ty.kind()) {
(ty::ValTree::Branch(_), ty::Ref(_, inner_ty, _)) => match inner_ty.kind() { (ty::ValTree::Branch(_), ty::Ref(_, inner_ty, _)) => match inner_ty.kind() {
ty::Slice(t) if *t == u8_type => { ty::Slice(t) if *t == u8_type => {
let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| {
bug!( bug!(
"expected to convert valtree {:?} to raw bytes for type {:?}", "expected to convert valtree {:?} to raw bytes for type {:?}",
valtree, cv.valtree,
t t
) )
}); });
return self.pretty_print_byte_str(bytes); return self.pretty_print_byte_str(bytes);
} }
ty::Str => { ty::Str => {
let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| {
bug!("expected to convert valtree to raw bytes for type {:?}", ty) bug!("expected to convert valtree to raw bytes for type {:?}", cv.ty)
}); });
p!(write("{:?}", String::from_utf8_lossy(bytes))); p!(write("{:?}", String::from_utf8_lossy(bytes)));
return Ok(()); return Ok(());
} }
_ => { _ => {
let cv = ty::Value { valtree: cv.valtree, ty: *inner_ty };
p!("&"); p!("&");
p!(pretty_print_const_valtree(valtree, *inner_ty, print_ty)); p!(pretty_print_const_valtree(cv, print_ty));
return Ok(()); return Ok(());
} }
}, },
(ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => { (ty::ValTree::Branch(_), ty::Array(t, _)) if *t == u8_type => {
let bytes = valtree.try_to_raw_bytes(self.tcx(), ty).unwrap_or_else(|| { let bytes = cv.try_to_raw_bytes(self.tcx()).unwrap_or_else(|| {
bug!("expected to convert valtree to raw bytes for type {:?}", t) bug!("expected to convert valtree to raw bytes for type {:?}", t)
}); });
p!("*"); p!("*");
@ -1837,10 +1836,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
} }
// Aggregates, printed as array/tuple/struct/variant construction syntax. // Aggregates, printed as array/tuple/struct/variant construction syntax.
(ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => { (ty::ValTree::Branch(_), ty::Array(..) | ty::Tuple(..) | ty::Adt(..)) => {
let contents = let contents = self.tcx().destructure_const(ty::Const::new_value(
self.tcx().destructure_const(ty::Const::new_value(self.tcx(), valtree, ty)); self.tcx(),
cv.valtree,
cv.ty,
));
let fields = contents.fields.iter().copied(); let fields = contents.fields.iter().copied();
match *ty.kind() { match *cv.ty.kind() {
ty::Array(..) => { ty::Array(..) => {
p!("[", comma_sep(fields), "]"); p!("[", comma_sep(fields), "]");
} }
@ -1857,7 +1859,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
write!(this, "unreachable()")?; write!(this, "unreachable()")?;
Ok(()) Ok(())
}, },
|this| this.print_type(ty), |this| this.print_type(cv.ty),
": ", ": ",
)?; )?;
} }
@ -1894,7 +1896,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
return self.pretty_print_const_scalar_int(leaf, *inner_ty, print_ty); return self.pretty_print_const_scalar_int(leaf, *inner_ty, print_ty);
} }
(ty::ValTree::Leaf(leaf), _) => { (ty::ValTree::Leaf(leaf), _) => {
return self.pretty_print_const_scalar_int(leaf, ty, print_ty); return self.pretty_print_const_scalar_int(leaf, cv.ty, print_ty);
} }
// FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading // FIXME(oli-obk): also pretty print arrays and other aggregate constants by reading
// their fields instead of just dumping the memory. // their fields instead of just dumping the memory.
@ -1902,13 +1904,13 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
} }
// fallback // fallback
if valtree == ty::ValTree::zst() { if cv.valtree == ty::ValTree::zst() {
p!(write("<ZST>")); p!(write("<ZST>"));
} else { } else {
p!(write("{:?}", valtree)); p!(write("{:?}", cv.valtree));
} }
if print_ty { if print_ty {
p!(": ", print(ty)); p!(": ", print(cv.ty));
} }
Ok(()) Ok(())
} }

View file

@ -170,7 +170,7 @@ impl<'tcx> fmt::Debug for ty::Const<'tcx> {
bug!("we checked that this is a valtree") bug!("we checked that this is a valtree")
}; };
let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS); let mut cx = FmtPrinter::new(tcx, Namespace::ValueNS);
cx.pretty_print_const_valtree(cv.valtree, cv.ty, /*print_ty*/ true)?; cx.pretty_print_const_valtree(cv, /*print_ty*/ true)?;
f.write_str(&cx.into_buffer()) f.write_str(&cx.into_buffer())
}); });
} }

View file

@ -649,7 +649,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
// HACK(jaic1): hide the `str` type behind a reference // HACK(jaic1): hide the `str` type behind a reference
// for the following transformation from valtree to raw bytes // for the following transformation from valtree to raw bytes
let ref_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, ct_ty); let ref_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, ct_ty);
let slice = valtree.try_to_raw_bytes(tcx, ref_ty).unwrap_or_else(|| { let cv = ty::Value { ty: ref_ty, valtree };
let slice = cv.try_to_raw_bytes(tcx).unwrap_or_else(|| {
bug!("expected to get raw bytes from valtree {:?} for type {:}", valtree, ct_ty) bug!("expected to get raw bytes from valtree {:?} for type {:}", valtree, ct_ty)
}); });
let s = std::str::from_utf8(slice).expect("non utf8 str from MIR interpreter"); let s = std::str::from_utf8(slice).expect("non utf8 str from MIR interpreter");