Skip to content

Use debug impl in rendering const eval result #15230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions crates/hir-ty/src/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
4 changes: 3 additions & 1 deletion crates/hir-ty/src/mir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
115 changes: 82 additions & 33 deletions crates/hir-ty/src/mir/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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");
Expand All @@ -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],
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 };
Expand Down
23 changes: 21 additions & 2 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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);
}
}
Expand Down