about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-ty/src/display.rs22
-rw-r--r--crates/hir-ty/src/mir.rs4
-rw-r--r--crates/hir-ty/src/mir/eval.rs115
-rw-r--r--crates/hir/src/lib.rs23
4 files changed, 106 insertions, 58 deletions
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index b87047718d0..8cffdef289e 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -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],
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs
index 2345bab0bb4..5e92b350a3c 100644
--- a/crates/hir-ty/src/mir.rs
+++ b/crates/hir-ty/src/mir.rs
@@ -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,
 };
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index c76d066232c..010ebcbd068 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -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 };
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 337cfbfb625..44f901fb3a3 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -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);
     }
 }