Auto merge of #15230 - HKalbasi:mir, r=HKalbasi

Use debug impl in rendering const eval result

fix #15188
This commit is contained in:
bors 2023-07-07 21:38:14 +00:00
commit db0add1ce9
4 changed files with 106 additions and 58 deletions

View File

@ -446,28 +446,6 @@ impl HirDisplay for Const {
}
}
pub struct HexifiedConst(pub Const);
impl HirDisplay for HexifiedConst {
fn hir_fmt(&self, f: &mut HirFormatter<'_>) -> Result<(), HirDisplayError> {
let data = &self.0.data(Interner);
if let TyKind::Scalar(s) = data.ty.kind(Interner) {
if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) {
if let ConstValue::Concrete(c) = &data.value {
if let ConstScalar::Bytes(b, m) = &c.interned {
let value = u128::from_le_bytes(pad16(b, false));
if value >= 10 {
render_const_scalar(f, &b, m, &data.ty)?;
return write!(f, " ({:#X})", value);
}
}
}
}
}
self.0.hir_fmt(f)
}
}
fn render_const_scalar(
f: &mut HirFormatter<'_>,
b: &[u8],

View File

@ -22,7 +22,9 @@ mod pretty;
mod monomorphization;
pub use borrowck::{borrowck_query, BorrowckResult, MutabilityReason};
pub use eval::{interpret_mir, pad16, Evaluator, MirEvalError, VTableMap};
pub use eval::{
interpret_mir, pad16, render_const_using_debug_impl, Evaluator, MirEvalError, VTableMap,
};
pub use lower::{
lower_to_mir, mir_body_for_closure_query, mir_body_query, mir_body_recover, MirLowerError,
};

View File

@ -10,10 +10,11 @@ use hir_def::{
data::adt::{StructFlags, VariantData},
lang_item::LangItem,
layout::{TagEncoding, Variants},
AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId,
VariantId,
resolver::{HasResolver, TypeNs, ValueNs},
AdtId, ConstId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup,
StaticId, VariantId,
};
use hir_expand::InFile;
use hir_expand::{mod_path::ModPath, InFile};
use intern::Interned;
use la_arena::ArenaMap;
use rustc_hash::{FxHashMap, FxHashSet};
@ -482,7 +483,7 @@ pub fn interpret_mir(
assert_placeholder_ty_is_unused: bool,
) -> (Result<Const>, String, String) {
let ty = body.locals[return_slot()].ty.clone();
let mut evaluator = Evaluator::new(db, &body, assert_placeholder_ty_is_unused);
let mut evaluator = Evaluator::new(db, body.owner, assert_placeholder_ty_is_unused);
let it: Result<Const> = (|| {
if evaluator.ptr_size() != std::mem::size_of::<usize>() {
not_supported!("targets with different pointer size from host");
@ -506,11 +507,11 @@ pub fn interpret_mir(
impl Evaluator<'_> {
pub fn new<'a>(
db: &'a dyn HirDatabase,
body: &MirBody,
owner: DefWithBodyId,
assert_placeholder_ty_is_unused: bool,
) -> Evaluator<'a> {
let crate_id = body.owner.module(db.upcast()).krate();
let trait_env = db.trait_environment_for_body(body.owner);
let crate_id = owner.module(db.upcast()).krate();
let trait_env = db.trait_environment_for_body(owner);
Evaluator {
stack: vec![0],
heap: vec![0],
@ -1551,29 +1552,15 @@ impl Evaluator<'_> {
let addr = self.eval_static(*st, locals)?;
Interval::new(addr, self.ptr_size())
}
Operand::Constant(konst) => {
let data = &konst.data(Interner);
match &data.value {
chalk_ir::ConstValue::BoundVar(_) => not_supported!("bound var constant"),
chalk_ir::ConstValue::InferenceVar(_) => {
not_supported!("inference var constant")
}
chalk_ir::ConstValue::Placeholder(_) => not_supported!("placeholder constant"),
chalk_ir::ConstValue::Concrete(c) => {
self.allocate_const_in_heap(c, &data.ty, locals, konst)?
}
}
}
Operand::Constant(konst) => self.allocate_const_in_heap(locals, konst)?,
})
}
fn allocate_const_in_heap(
&mut self,
c: &chalk_ir::ConcreteConst<Interner>,
ty: &Ty,
locals: &Locals,
konst: &chalk_ir::Const<Interner>,
) -> Result<Interval> {
fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result<Interval> {
let ty = &konst.data(Interner).ty;
let chalk_ir::ConstValue::Concrete(c) = &konst.data(Interner).value else {
not_supported!("evaluating non concrete constant");
};
Ok(match &c.interned {
ConstScalar::Bytes(v, memory_map) => {
let mut v: Cow<'_, [u8]> = Cow::Borrowed(v);
@ -2242,12 +2229,7 @@ impl Evaluator<'_> {
Box::new(e),
)
})?;
let data = &konst.data(Interner);
if let chalk_ir::ConstValue::Concrete(c) = &data.value {
self.allocate_const_in_heap(&c, &data.ty, locals, &konst)?
} else {
not_supported!("unevaluatable static");
}
self.allocate_const_in_heap(locals, &konst)?
} else {
let ty = &self.db.infer(st.into())[self.db.body(st.into()).body_expr];
let Some((size, align)) = self.size_align_of(&ty, locals)? else {
@ -2388,6 +2370,73 @@ impl Evaluator<'_> {
}
}
pub fn render_const_using_debug_impl(
db: &dyn HirDatabase,
owner: ConstId,
c: &Const,
) -> Result<String> {
let mut evaluator = Evaluator::new(db, owner.into(), false);
let locals = &Locals {
ptr: ArenaMap::new(),
body: db
.mir_body(owner.into())
.map_err(|_| MirEvalError::NotSupported("unreachable".to_string()))?,
drop_flags: DropFlags::default(),
};
let data = evaluator.allocate_const_in_heap(locals, c)?;
let resolver = owner.resolver(db.upcast());
let Some(TypeNs::TraitId(debug_trait)) = resolver.resolve_path_in_type_ns_fully(
db.upcast(),
&hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments(
hir_expand::mod_path::PathKind::Abs,
[name![core], name![fmt], name![Debug]].into_iter(),
)),
) else {
not_supported!("core::fmt::Debug not found");
};
let Some(debug_fmt_fn) = db.trait_data(debug_trait).method_by_name(&name![fmt]) else {
not_supported!("core::fmt::Debug::fmt not found");
};
// a1 = &[""]
let a1 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size());
// a2 = &[::core::fmt::ArgumentV1::new(&(THE_CONST), ::core::fmt::Debug::fmt)]
// FIXME: we should call the said function, but since its name is going to break in the next rustc version
// and its ABI doesn't break yet, we put it in memory manually.
let a2 = evaluator.heap_allocate(evaluator.ptr_size() * 2, evaluator.ptr_size());
evaluator.write_memory(a2, &data.addr.to_bytes())?;
let debug_fmt_fn_ptr = evaluator.vtable_map.id(TyKind::FnDef(
db.intern_callable_def(debug_fmt_fn.into()).into(),
Substitution::from1(Interner, c.data(Interner).ty.clone()),
)
.intern(Interner));
evaluator.write_memory(a2.offset(evaluator.ptr_size()), &debug_fmt_fn_ptr.to_le_bytes())?;
// a3 = ::core::fmt::Arguments::new_v1(a1, a2)
// FIXME: similarly, we should call function here, not directly working with memory.
let a3 = evaluator.heap_allocate(evaluator.ptr_size() * 6, evaluator.ptr_size());
evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a1.to_bytes())?;
evaluator.write_memory(a3.offset(3 * evaluator.ptr_size()), &[1])?;
evaluator.write_memory(a3.offset(4 * evaluator.ptr_size()), &a2.to_bytes())?;
evaluator.write_memory(a3.offset(5 * evaluator.ptr_size()), &[1])?;
let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully(
db.upcast(),
&hir_def::path::Path::from_known_path_with_no_generic(ModPath::from_segments(
hir_expand::mod_path::PathKind::Abs,
[name![std], name![fmt], name![format]].into_iter(),
)),
) else {
not_supported!("std::fmt::format not found");
};
let message_string = evaluator.interpret_mir(
db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?,
[IntervalOrOwned::Borrowed(Interval { addr: a3, size: evaluator.ptr_size() * 6 })]
.into_iter(),
)?;
let addr =
Address::from_bytes(&message_string[evaluator.ptr_size()..2 * evaluator.ptr_size()])?;
let size = from_bytes!(usize, message_string[2 * evaluator.ptr_size()..]);
Ok(std::string::String::from_utf8_lossy(evaluator.read_memory(addr, size)?).into_owned())
}
pub fn pad16(it: &[u8], is_signed: bool) -> [u8; 16] {
let is_negative = is_signed && it.last().unwrap_or(&0) > &127;
let fill_with = if is_negative { 255 } else { 0 };

View File

@ -62,7 +62,6 @@ use hir_ty::{
all_super_traits, autoderef,
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
diagnostics::BodyValidationDiagnostic,
display::HexifiedConst,
layout::{Layout as TyLayout, RustcEnumVariantIdx, TagEncoding},
method_resolution::{self, TyFingerprint},
mir::{self, interpret_mir},
@ -2156,7 +2155,27 @@ impl Const {
pub fn render_eval(self, db: &dyn HirDatabase) -> Result<String, ConstEvalError> {
let c = db.const_eval(self.id.into(), Substitution::empty(Interner))?;
let r = format!("{}", HexifiedConst(c).display(db));
let data = &c.data(Interner);
if let TyKind::Scalar(s) = data.ty.kind(Interner) {
if matches!(s, Scalar::Int(_) | Scalar::Uint(_)) {
if let hir_ty::ConstValue::Concrete(c) = &data.value {
if let hir_ty::ConstScalar::Bytes(b, _) = &c.interned {
let value = u128::from_le_bytes(mir::pad16(b, false));
let value_signed =
i128::from_le_bytes(mir::pad16(b, matches!(s, Scalar::Int(_))));
if value >= 10 {
return Ok(format!("{} ({:#X})", value_signed, value));
} else {
return Ok(format!("{}", value_signed));
}
}
}
}
}
if let Ok(s) = mir::render_const_using_debug_impl(db, self.id, &c) {
return Ok(s);
}
let r = format!("{}", c.display(db));
return Ok(r);
}
}