Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/eval.rs')
-rw-r--r--crates/hir-ty/src/mir/eval.rs94
1 files changed, 74 insertions, 20 deletions
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 855dafe2d9..b968f33e81 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -738,6 +738,42 @@ impl<'a, 'db: 'a> Evaluator<'a, 'db> {
self.cached_ptr_size
}
+ fn caller_location_fields(&self, owner: InferBodyId, span: MirSpan) -> (String, u32, u32) {
+ let Some((file_id, text_range)) = self.resolve_mir_span(owner, span) else {
+ return (String::new(), 0, 0);
+ };
+ let source_root = self.db.file_source_root(file_id).source_root_id(self.db);
+ let source_root = self.db.source_root(source_root).source_root(self.db);
+ let path = source_root.path_for_file(&file_id).map(|path| path.to_string());
+ let (line, col) = self.db.line_column(file_id, text_range.start()).unwrap_or((0, 0));
+ (path.unwrap_or_default(), line + 1, col + 1)
+ }
+
+ fn resolve_mir_span(&self, owner: InferBodyId, span: MirSpan) -> Option<(FileId, TextRange)> {
+ let (source_map, self_param_syntax) = match owner {
+ InferBodyId::DefWithBodyId(def) => {
+ let body = &Body::with_source_map(self.db, def).1;
+ (&**body, body.self_param_syntax())
+ }
+ InferBodyId::AnonConstId(def) => {
+ (ExpressionStore::with_source_map(self.db, def.loc(self.db).owner).1, None)
+ }
+ };
+ let span: InFile<SyntaxNodePtr> = match span {
+ MirSpan::ExprId(e) => source_map.expr_syntax(e).ok()?.map(|it| it.into()),
+ MirSpan::PatId(p) => source_map.pat_syntax(p).ok()?.map(|it| it.syntax_node_ptr()),
+ MirSpan::BindingId(b) => source_map
+ .patterns_for_binding(b)
+ .iter()
+ .find_map(|p| source_map.pat_syntax(*p).ok())?
+ .map(|it| it.syntax_node_ptr()),
+ MirSpan::SelfParam => self_param_syntax?.map(|it| it.syntax_node_ptr()),
+ MirSpan::Unknown => return None,
+ };
+ let file_id = span.file_id.original_file(self.db);
+ Some((file_id.file_id(self.db), span.value.text_range()))
+ }
+
fn projected_ty(&self, ty: PlaceTy<'db>, proj: PlaceElem) -> PlaceTy<'db> {
let pair = (ty, proj);
if let Some(r) = self.projected_ty_cache.borrow().get(&pair) {
@@ -3181,26 +3217,33 @@ pub fn render_const_using_debug_impl<'db>(
let Some(debug_fmt_fn) = lang_items.Debug_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 ptr_size = evaluator.ptr_size();
+ // Construct the arguments of `format_args!("{:?}", THE_CONST)` directly in memory and hand
+ // them to `std::fmt::format`.
+ //
+ // `core::fmt::rt::Argument` is a niche-encoded `Placeholder { value: NonNull<()>, formatter }`,
+ // i.e. two words: a pointer to the value, and the type-erased `<T as Debug>::fmt` function.
+ // A non-null `value` is what distinguishes the `Placeholder` variant from `Count`.
+ let argument = evaluator.heap_allocate(ptr_size * 2, ptr_size)?;
+ evaluator.write_memory(argument, &data.addr.to_bytes())?;
let debug_fmt_fn_ptr = evaluator.vtable_map.id(Ty::new_fn_def(
evaluator.interner(),
CallableDefId::FunctionId(debug_fmt_fn).into(),
GenericArgs::new_from_slice(&[ty.into()]),
));
- 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, &a1.to_bytes())?;
- evaluator.write_memory(a3.offset(evaluator.ptr_size()), &[1])?;
- evaluator.write_memory(a3.offset(2 * evaluator.ptr_size()), &a2.to_bytes())?;
- evaluator.write_memory(a3.offset(3 * evaluator.ptr_size()), &[1])?;
+ evaluator.write_memory(argument.offset(ptr_size), &debug_fmt_fn_ptr.to_le_bytes())?;
+ // Since Rust 1.93 `core::fmt::Arguments` is two words wide:
+ // struct Arguments<'a> { template: NonNull<u8>, args: NonNull<Argument<'a>> }
+ // `template` points at a byte-encoded format string; `format_args!("{:?}", x)` encodes to a
+ // single default placeholder (`0xC0`) followed by the end marker (`0x00`). `args` points at
+ // our one-element argument array, and must stay pointer-aligned: `core` uses the low bit of
+ // `args` as a tag (1 = inline `&str` form, 0 = placeholder form), and heap allocations here
+ // are pointer-aligned so the bit is 0 as required.
+ let template = evaluator.heap_allocate(2, 1)?;
+ evaluator.write_memory(template, &[0xC0, 0x00])?;
+ let arguments = evaluator.heap_allocate(ptr_size * 2, ptr_size)?;
+ evaluator.write_memory(arguments, &template.to_bytes())?;
+ evaluator.write_memory(arguments.offset(ptr_size), &argument.to_bytes())?;
let Some(ValueNs::FunctionId(format_fn)) = resolver.resolve_path_in_value_ns_fully(
db,
&hir_def::expr_store::path::Path::from_known_path_with_no_generic(path![std::fmt::format]),
@@ -3210,13 +3253,24 @@ pub fn render_const_using_debug_impl<'db>(
};
let interval = 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(),
+ [IntervalOrOwned::Borrowed(Interval { addr: arguments, size: ptr_size * 2 })].into_iter(),
)?;
let message_string = interval.get(&evaluator)?;
- 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()..]);
+ let words = [
+ from_bytes!(usize, message_string[0..ptr_size]),
+ from_bytes!(usize, message_string[ptr_size..2 * ptr_size]),
+ from_bytes!(usize, message_string[2 * ptr_size..3 * ptr_size]),
+ ];
+ let Some(addr) = words.into_iter().map(Address::from_usize).find(|it| matches!(it, Heap(_)))
+ else {
+ // No heap buffer means the formatted string is empty.
+ return Ok(String::new());
+ };
+ let size = words
+ .into_iter()
+ .filter(|&it| !matches!(Address::from_usize(it), Heap(_)))
+ .min()
+ .unwrap_or(0);
Ok(std::string::String::from_utf8_lossy(evaluator.read_memory(addr, size)?).into_owned())
}