implement type_name intrinsic
This commit is contained in:
parent
274e8301c1
commit
5208bf8f55
@ -26,7 +26,7 @@ fn with_single_file(ra_fixture: &str) -> (Self, FileId) {
|
||||
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])
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
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 @@ fn alignment() {
|
||||
transmute(&x)
|
||||
}
|
||||
"#,
|
||||
|b| assert_eq!(b[0] % 8, 0),
|
||||
|b, _| assert_eq!(b[0] % 8, 0),
|
||||
);
|
||||
check_answer(
|
||||
r#"
|
||||
@ -233,7 +251,7 @@ fn alignment() {
|
||||
static X: i64 = 12;
|
||||
const GOAL: usize = transmute(&X);
|
||||
"#,
|
||||
|b| assert_eq!(b[0] % 8, 0),
|
||||
|b, _| assert_eq!(b[0] % 8, 0),
|
||||
);
|
||||
}
|
||||
|
||||
@ -2067,6 +2085,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(
|
||||
@ -2443,6 +2472,25 @@ impl ToConst for i32 {
|
||||
"#,
|
||||
32,
|
||||
);
|
||||
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);
|
||||
|
@ -149,6 +149,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(
|
||||
|
@ -29,7 +29,7 @@
|
||||
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,8 +1571,30 @@ fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result<I
|
||||
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 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))
|
||||
})?;
|
||||
if let chalk_ir::ConstValue::Concrete(c) = &result_owner.data(Interner).value {
|
||||
if let ConstScalar::Bytes(v, mm) = &c.interned {
|
||||
break 'b (v, mm);
|
||||
}
|
||||
}
|
||||
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)?;
|
||||
@ -1593,13 +1615,7 @@ fn allocate_const_in_heap(&mut self, locals: &Locals, konst: &Const) -> Result<I
|
||||
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");
|
||||
}
|
||||
ConstScalar::Unknown => not_supported!("evaluating unknown const"),
|
||||
})
|
||||
Ok(Interval::new(addr, size))
|
||||
}
|
||||
|
||||
fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result<Interval> {
|
||||
|
@ -602,6 +602,26 @@ fn exec_intrinsic(
|
||||
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 {
|
||||
|
@ -13,15 +13,14 @@
|
||||
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 @@ fn fill_operand(&mut self, op: &mut Operand) -> Result<(), MirLowerError> {
|
||||
| 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,
|
||||
*c = intern_const_scalar(
|
||||
crate::ConstScalar::UnevaluatedConst(*const_id, subst),
|
||||
c.data(Interner).ty.clone(),
|
||||
);
|
||||
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;
|
||||
}
|
||||
crate::ConstScalar::Bytes(_, _) | crate::ConstScalar::Unknown => (),
|
||||
},
|
||||
|
@ -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))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user