Unnamed repository; edit this file 'description' to name the repository.
implement type_name intrinsic
hkalbasi 2023-07-14
parent 274e830 · commit 5208bf8
-rw-r--r--crates/base-db/src/fixture.rs2
-rw-r--r--crates/hir-ty/src/consteval/tests.rs64
-rw-r--r--crates/hir-ty/src/consteval/tests/intrinsics.rs30
-rw-r--r--crates/hir-ty/src/mir/eval.rs66
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs20
-rw-r--r--crates/hir-ty/src/mir/monomorphization.rs26
-rw-r--r--crates/ide/src/interpret_function.rs6
7 files changed, 158 insertions, 56 deletions
diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs
index 6bfdf402c4..323ee4260e 100644
--- a/crates/base-db/src/fixture.rs
+++ b/crates/base-db/src/fixture.rs
@@ -26,7 +26,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static {
let fixture = ChangeFixture::parse(ra_fixture);
let mut db = Self::default();
fixture.change.apply(&mut db);
- assert_eq!(fixture.files.len(), 1);
+ assert_eq!(fixture.files.len(), 1, "Multiple file found in the fixture");
(db, fixture.files[0])
}
diff --git a/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs
index 4ca1d8a239..db5a43e067 100644
--- a/crates/hir-ty/src/consteval/tests.rs
+++ b/crates/hir-ty/src/consteval/tests.rs
@@ -4,7 +4,7 @@ use hir_def::db::DefDatabase;
use crate::{
consteval::try_const_usize, db::HirDatabase, mir::pad16, test_db::TestDB, Const, ConstScalar,
- Interner,
+ Interner, MemoryMap,
};
use super::{
@@ -36,7 +36,7 @@ fn check_fail(ra_fixture: &str, error: impl FnOnce(ConstEvalError) -> bool) {
#[track_caller]
fn check_number(ra_fixture: &str, answer: i128) {
- check_answer(ra_fixture, |b| {
+ check_answer(ra_fixture, |b, _| {
assert_eq!(
b,
&answer.to_le_bytes()[0..b.len()],
@@ -47,8 +47,26 @@ fn check_number(ra_fixture: &str, answer: i128) {
}
#[track_caller]
-fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8])) {
- let (db, file_id) = TestDB::with_single_file(ra_fixture);
+fn check_str(ra_fixture: &str, answer: &str) {
+ check_answer(ra_fixture, |b, mm| {
+ let addr = usize::from_le_bytes(b[0..b.len() / 2].try_into().unwrap());
+ let size = usize::from_le_bytes(b[b.len() / 2..].try_into().unwrap());
+ let Some(bytes) = mm.get(addr, size) else {
+ panic!("string data missed in the memory map");
+ };
+ assert_eq!(
+ bytes,
+ answer.as_bytes(),
+ "Bytes differ. In string form: actual = {}, expected = {answer}",
+ String::from_utf8_lossy(bytes)
+ );
+ });
+}
+
+#[track_caller]
+fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
+ let (db, file_ids) = TestDB::with_many_files(ra_fixture);
+ let file_id = *file_ids.last().unwrap();
let r = match eval_goal(&db, file_id) {
Ok(t) => t,
Err(e) => {
@@ -58,8 +76,8 @@ fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8])) {
};
match &r.data(Interner).value {
chalk_ir::ConstValue::Concrete(c) => match &c.interned {
- ConstScalar::Bytes(b, _) => {
- check(b);
+ ConstScalar::Bytes(b, mm) => {
+ check(b, mm);
}
x => panic!("Expected number but found {:?}", x),
},
@@ -224,7 +242,7 @@ const GOAL: usize = {
transmute(&x)
}
"#,
- |b| assert_eq!(b[0] % 8, 0),
+ |b, _| assert_eq!(b[0] % 8, 0),
);
check_answer(
r#"
@@ -233,7 +251,7 @@ use core::mem::transmute;
static X: i64 = 12;
const GOAL: usize = transmute(&X);
"#,
- |b| assert_eq!(b[0] % 8, 0),
+ |b, _| assert_eq!(b[0] % 8, 0),
);
}
@@ -2068,6 +2086,17 @@ fn array_and_index() {
}
#[test]
+fn string() {
+ check_str(
+ r#"
+ //- minicore: coerce_unsized, index, slice
+ const GOAL: &str = "hello";
+ "#,
+ "hello",
+ );
+}
+
+#[test]
fn byte_string() {
check_number(
r#"
@@ -2445,6 +2474,25 @@ fn const_trait_assoc() {
);
check_number(
r#"
+ //- /a/lib.rs crate:a
+ pub trait ToConst {
+ const VAL: usize;
+ }
+ pub const fn to_const<T: ToConst>() -> usize {
+ T::VAL
+ }
+ //- /main.rs crate:main deps:a
+ use a::{ToConst, to_const};
+ struct U0;
+ impl ToConst for U0 {
+ const VAL: usize = 5;
+ }
+ const GOAL: usize = to_const::<U0>();
+ "#,
+ 5,
+ );
+ check_number(
+ r#"
struct S<T>(*mut T);
trait MySized: Sized {
diff --git a/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs
index 30e87661ab..9253e31d77 100644
--- a/crates/hir-ty/src/consteval/tests/intrinsics.rs
+++ b/crates/hir-ty/src/consteval/tests/intrinsics.rs
@@ -150,6 +150,36 @@ fn min_align_of_val() {
}
#[test]
+fn type_name() {
+ check_str(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn type_name<T: ?Sized>() -> &'static str;
+ }
+
+ const GOAL: &str = type_name::<i32>();
+ "#,
+ "i32",
+ );
+ check_str(
+ r#"
+ extern "rust-intrinsic" {
+ pub fn type_name<T: ?Sized>() -> &'static str;
+ }
+
+ mod mod1 {
+ pub mod mod2 {
+ pub struct Ty;
+ }
+ }
+
+ const GOAL: &str = type_name::<mod1::mod2::Ty>();
+ "#,
+ "mod1::mod2::Ty",
+ );
+}
+
+#[test]
fn transmute() {
check_number(
r#"
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 47c9354768..d7820de629 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -29,7 +29,7 @@ use crate::{
infer::PointerCast,
layout::{Layout, LayoutError, RustcEnumVariantIdx},
mapping::from_chalk,
- method_resolution::is_dyn_method,
+ method_resolution::{is_dyn_method, lookup_impl_const},
name, static_lifetime,
traits::FnTrait,
utils::{detect_variant_from_bytes, ClosureSubst},
@@ -1571,35 +1571,51 @@ impl Evaluator<'_> {
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);
- let patch_map = memory_map.transform_addresses(|b, align| {
- let addr = self.heap_allocate(b.len(), align)?;
- self.write_memory(addr, b)?;
- Ok(addr.to_usize())
+ let result_owner;
+ let (v, memory_map) = match &c.interned {
+ ConstScalar::Bytes(v, mm) => (v, mm),
+ ConstScalar::UnevaluatedConst(const_id, subst) => 'b: {
+ let mut const_id = *const_id;
+ let mut subst = subst.clone();
+ if let hir_def::GeneralConstId::ConstId(c) = const_id {
+ let (c, s) = lookup_impl_const(self.db, self.trait_env.clone(), c, subst);
+ const_id = hir_def::GeneralConstId::ConstId(c);
+ subst = s;
+ }
+ result_owner = self.db.const_eval(const_id.into(), subst).map_err(|e| {
+ let name = const_id.name(self.db.upcast());
+ MirEvalError::ConstEvalError(name, Box::new(e))
})?;
- let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1));
- if size != v.len() {
- // Handle self enum
- if size == 16 && v.len() < 16 {
- v = Cow::Owned(pad16(&v, false).to_vec());
- } else if size < 16 && v.len() == 16 {
- v = Cow::Owned(v[0..size].to_vec());
- } else {
- return Err(MirEvalError::InvalidConst(konst.clone()));
+ if let chalk_ir::ConstValue::Concrete(c) = &result_owner.data(Interner).value {
+ if let ConstScalar::Bytes(v, mm) = &c.interned {
+ break 'b (v, mm);
}
}
- let addr = self.heap_allocate(size, align)?;
- self.write_memory(addr, &v)?;
- self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?;
- Interval::new(addr, size)
- }
- ConstScalar::UnevaluatedConst(..) => {
- not_supported!("unevaluated const present in monomorphized mir");
+ not_supported!("unevaluatable constant");
}
ConstScalar::Unknown => not_supported!("evaluating unknown const"),
- })
+ };
+ let mut v: Cow<'_, [u8]> = Cow::Borrowed(v);
+ let patch_map = memory_map.transform_addresses(|b, align| {
+ let addr = self.heap_allocate(b.len(), align)?;
+ self.write_memory(addr, b)?;
+ Ok(addr.to_usize())
+ })?;
+ let (size, align) = self.size_align_of(ty, locals)?.unwrap_or((v.len(), 1));
+ if size != v.len() {
+ // Handle self enum
+ if size == 16 && v.len() < 16 {
+ v = Cow::Owned(pad16(&v, false).to_vec());
+ } else if size < 16 && v.len() == 16 {
+ v = Cow::Owned(v[0..size].to_vec());
+ } else {
+ return Err(MirEvalError::InvalidConst(konst.clone()));
+ }
+ }
+ let addr = self.heap_allocate(size, align)?;
+ self.write_memory(addr, &v)?;
+ self.patch_addresses(&patch_map, &memory_map.vtable, addr, ty, locals)?;
+ Ok(Interval::new(addr, size))
}
fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result<Interval> {
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index cedc453803..28a5e50a56 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -602,6 +602,26 @@ impl Evaluator<'_> {
destination.write_from_bytes(self, &align.to_le_bytes())
}
}
+ "type_name" => {
+ let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
+ else {
+ return Err(MirEvalError::TypeError("type_name generic arg is not provided"));
+ };
+ let Ok(ty_name) = ty.display_source_code(
+ self.db,
+ locals.body.owner.module(self.db.upcast()),
+ true,
+ ) else {
+ not_supported!("fail in generating type_name using source code display");
+ };
+ let len = ty_name.len();
+ let addr = self.heap_allocate(len, 1)?;
+ self.write_memory(addr, ty_name.as_bytes())?;
+ destination.slice(0..self.ptr_size()).write_from_bytes(self, &addr.to_bytes())?;
+ destination
+ .slice(self.ptr_size()..2 * self.ptr_size())
+ .write_from_bytes(self, &len.to_le_bytes())
+ }
"needs_drop" => {
let Some(ty) = generic_args.as_slice(Interner).get(0).and_then(|it| it.ty(Interner))
else {
diff --git a/crates/hir-ty/src/mir/monomorphization.rs b/crates/hir-ty/src/mir/monomorphization.rs
index e4574c77e2..c565228d91 100644
--- a/crates/hir-ty/src/mir/monomorphization.rs
+++ b/crates/hir-ty/src/mir/monomorphization.rs
@@ -13,15 +13,14 @@ use chalk_ir::{
fold::{FallibleTypeFolder, TypeFoldable, TypeSuperFoldable},
ConstData, DebruijnIndex,
};
-use hir_def::{DefWithBodyId, GeneralConstId};
+use hir_def::DefWithBodyId;
use triomphe::Arc;
use crate::{
- consteval::unknown_const,
+ consteval::{intern_const_scalar, unknown_const},
db::HirDatabase,
from_placeholder_idx,
infer::normalize,
- method_resolution::lookup_impl_const,
utils::{generics, Generics},
ClosureId, Const, Interner, ProjectionTy, Substitution, TraitEnvironment, Ty, TyKind,
};
@@ -193,25 +192,12 @@ impl Filler<'_> {
| chalk_ir::ConstValue::Placeholder(_) => {}
chalk_ir::ConstValue::Concrete(cc) => match &cc.interned {
crate::ConstScalar::UnevaluatedConst(const_id, subst) => {
- let mut const_id = *const_id;
let mut subst = subst.clone();
self.fill_subst(&mut subst)?;
- if let GeneralConstId::ConstId(c) = const_id {
- let (c, s) = lookup_impl_const(
- self.db,
- self.db.trait_environment_for_body(self.owner),
- c,
- subst,
- );
- const_id = GeneralConstId::ConstId(c);
- subst = s;
- }
- let result =
- self.db.const_eval(const_id.into(), subst).map_err(|e| {
- let name = const_id.name(self.db.upcast());
- MirLowerError::ConstEvalError(name, Box::new(e))
- })?;
- *c = result;
+ *c = intern_const_scalar(
+ crate::ConstScalar::UnevaluatedConst(*const_id, subst),
+ c.data(Interner).ty.clone(),
+ );
}
crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (),
},
diff --git a/crates/ide/src/interpret_function.rs b/crates/ide/src/interpret_function.rs
index cbcbb4b09d..d06ffd5357 100644
--- a/crates/ide/src/interpret_function.rs
+++ b/crates/ide/src/interpret_function.rs
@@ -34,13 +34,15 @@ fn find_and_interpret(db: &RootDatabase, position: FilePosition) -> Option<Strin
_ => return None,
};
let span_formatter = |file_id, text_range: TextRange| {
- let line_col = db.line_index(file_id).line_col(text_range.start());
let path = &db
.source_root(db.file_source_root(file_id))
.path_for_file(&file_id)
.map(|x| x.to_string());
let path = path.as_deref().unwrap_or("<unknown file>");
- format!("file://{path}#{}:{}", line_col.line + 1, line_col.col)
+ match db.line_index(file_id).try_line_col(text_range.start()) {
+ Some(line_col) => format!("file://{path}#{}:{}", line_col.line + 1, line_col.col),
+ None => format!("file://{path} range {:?}", text_range),
+ }
};
Some(def.eval(db, span_formatter))
}