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.rs1332
1 files changed, 841 insertions, 491 deletions
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs
index 9acf9d39e5..d7820de629 100644
--- a/crates/hir-ty/src/mir/eval.rs
+++ b/crates/hir-ty/src/mir/eval.rs
@@ -1,6 +1,6 @@
//! This module provides a MIR interpreter, which is used in const eval.
-use std::{borrow::Cow, collections::HashMap, fmt::Write, iter, ops::Range};
+use std::{borrow::Cow, cell::RefCell, collections::HashMap, fmt::Write, iter, mem, ops::Range};
use base_db::{CrateId, FileId};
use chalk_ir::Mutability;
@@ -8,12 +8,13 @@ use either::Either;
use hir_def::{
builtin_type::BuiltinType,
data::adt::{StructFlags, VariantData},
- lang_item::{lang_attr, LangItem},
+ 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};
@@ -28,7 +29,7 @@ use crate::{
infer::PointerCast,
layout::{Layout, LayoutError, RustcEnumVariantIdx},
mapping::from_chalk,
- method_resolution::{is_dyn_method, lookup_impl_method},
+ method_resolution::{is_dyn_method, lookup_impl_const},
name, static_lifetime,
traits::FnTrait,
utils::{detect_variant_from_bytes, ClosureSubst},
@@ -37,8 +38,8 @@ use crate::{
};
use super::{
- return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, Operand,
- Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp,
+ return_slot, AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError,
+ MirSpan, Operand, Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp,
};
mod shim;
@@ -48,15 +49,15 @@ mod tests;
macro_rules! from_bytes {
($ty:tt, $value:expr) => {
($ty::from_le_bytes(match ($value).try_into() {
- Ok(x) => x,
+ Ok(it) => it,
Err(_) => return Err(MirEvalError::TypeError(stringify!(mismatched size in constructing $ty))),
}))
};
}
macro_rules! not_supported {
- ($x: expr) => {
- return Err(MirEvalError::NotSupported(format!($x)))
+ ($it: expr) => {
+ return Err(MirEvalError::NotSupported(format!($it)))
};
}
@@ -68,8 +69,8 @@ pub struct VTableMap {
impl VTableMap {
fn id(&mut self, ty: Ty) -> usize {
- if let Some(x) = self.ty_to_id.get(&ty) {
- return *x;
+ if let Some(it) = self.ty_to_id.get(&ty) {
+ return *it;
}
let id = self.id_to_ty.len();
self.id_to_ty.push(ty.clone());
@@ -114,11 +115,20 @@ impl TlsData {
}
}
+struct StackFrame {
+ body: Arc<MirBody>,
+ locals: Locals,
+ destination: Option<BasicBlockId>,
+ prev_stack_ptr: usize,
+ span: (MirSpan, DefWithBodyId),
+}
+
pub struct Evaluator<'a> {
db: &'a dyn HirDatabase,
trait_env: Arc<TraitEnvironment>,
stack: Vec<u8>,
heap: Vec<u8>,
+ code_stack: Vec<StackFrame>,
/// Stores the global location of the statics. We const evaluate every static first time we need it
/// and see it's missing, then we add it to this to reuse.
static_locations: FxHashMap<StaticId, Address>,
@@ -127,8 +137,10 @@ pub struct Evaluator<'a> {
/// time of use.
vtable_map: VTableMap,
thread_local_storage: TlsData,
+ random_state: oorandom::Rand64,
stdout: Vec<u8>,
stderr: Vec<u8>,
+ layout_cache: RefCell<FxHashMap<Ty, Arc<Layout>>>,
crate_id: CrateId,
// FIXME: This is a workaround, see the comment on `interpret_mir`
assert_placeholder_ty_is_unused: bool,
@@ -136,6 +148,8 @@ pub struct Evaluator<'a> {
execution_limit: usize,
/// An additional limit on stack depth, to prevent stack overflow
stack_depth_limit: usize,
+ /// Maximum count of bytes that heap and stack can grow
+ memory_limit: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -192,7 +206,7 @@ impl IntervalAndTy {
addr: Address,
ty: Ty,
evaluator: &Evaluator<'_>,
- locals: &Locals<'_>,
+ locals: &Locals,
) -> Result<IntervalAndTy> {
let size = evaluator.size_of_sized(&ty, locals, "type of interval")?;
Ok(IntervalAndTy { interval: Interval { addr, size }, ty })
@@ -226,18 +240,28 @@ impl IntervalOrOwned {
}
}
+#[cfg(target_pointer_width = "64")]
+const STACK_OFFSET: usize = 1 << 60;
+#[cfg(target_pointer_width = "64")]
+const HEAP_OFFSET: usize = 1 << 59;
+
+#[cfg(target_pointer_width = "32")]
+const STACK_OFFSET: usize = 1 << 30;
+#[cfg(target_pointer_width = "32")]
+const HEAP_OFFSET: usize = 1 << 29;
+
impl Address {
- fn from_bytes(x: &[u8]) -> Result<Self> {
- Ok(Address::from_usize(from_bytes!(usize, x)))
+ fn from_bytes(it: &[u8]) -> Result<Self> {
+ Ok(Address::from_usize(from_bytes!(usize, it)))
}
- fn from_usize(x: usize) -> Self {
- if x > usize::MAX / 2 {
- Stack(x - usize::MAX / 2)
- } else if x > usize::MAX / 4 {
- Heap(x - usize::MAX / 4)
+ fn from_usize(it: usize) -> Self {
+ if it > STACK_OFFSET {
+ Stack(it - STACK_OFFSET)
+ } else if it > HEAP_OFFSET {
+ Heap(it - HEAP_OFFSET)
} else {
- Invalid(x)
+ Invalid(it)
}
}
@@ -247,23 +271,23 @@ impl Address {
fn to_usize(&self) -> usize {
let as_num = match self {
- Stack(x) => *x + usize::MAX / 2,
- Heap(x) => *x + usize::MAX / 4,
- Invalid(x) => *x,
+ Stack(it) => *it + STACK_OFFSET,
+ Heap(it) => *it + HEAP_OFFSET,
+ Invalid(it) => *it,
};
as_num
}
fn map(&self, f: impl FnOnce(usize) -> usize) -> Address {
match self {
- Stack(x) => Stack(f(*x)),
- Heap(x) => Heap(f(*x)),
- Invalid(x) => Invalid(f(*x)),
+ Stack(it) => Stack(f(*it)),
+ Heap(it) => Heap(f(*it)),
+ Invalid(it) => Invalid(f(*it)),
}
}
fn offset(&self, offset: usize) -> Address {
- self.map(|x| x + offset)
+ self.map(|it| it + offset)
}
}
@@ -282,7 +306,7 @@ pub enum MirEvalError {
TypeIsUnsized(Ty, &'static str),
NotSupported(String),
InvalidConst(Const),
- InFunction(Either<FunctionId, ClosureId>, Box<MirEvalError>, MirSpan, DefWithBodyId),
+ InFunction(Box<MirEvalError>, Vec<(Either<FunctionId, ClosureId>, MirSpan, DefWithBodyId)>),
ExecutionLimitExceeded,
StackOverflow,
TargetDataLayoutNotAvailable,
@@ -300,40 +324,42 @@ impl MirEvalError {
) -> std::result::Result<(), std::fmt::Error> {
writeln!(f, "Mir eval error:")?;
let mut err = self;
- while let MirEvalError::InFunction(func, e, span, def) = err {
+ while let MirEvalError::InFunction(e, stack) = err {
err = e;
- match func {
- Either::Left(func) => {
- let function_name = db.function_data(*func);
- writeln!(
- f,
- "In function {} ({:?})",
- function_name.name.display(db.upcast()),
- func
- )?;
- }
- Either::Right(clos) => {
- writeln!(f, "In {:?}", clos)?;
+ for (func, span, def) in stack.iter().take(30).rev() {
+ match func {
+ Either::Left(func) => {
+ let function_name = db.function_data(*func);
+ writeln!(
+ f,
+ "In function {} ({:?})",
+ function_name.name.display(db.upcast()),
+ func
+ )?;
+ }
+ Either::Right(clos) => {
+ writeln!(f, "In {:?}", clos)?;
+ }
}
+ let source_map = db.body_with_source_map(*def).1;
+ let span: InFile<SyntaxNodePtr> = match span {
+ MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
+ Ok(s) => s.map(|it| it.into()),
+ Err(_) => continue,
+ },
+ MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
+ Ok(s) => s.map(|it| match it {
+ Either::Left(e) => e.into(),
+ Either::Right(e) => e.into(),
+ }),
+ Err(_) => continue,
+ },
+ MirSpan::Unknown => continue,
+ };
+ let file_id = span.file_id.original_file(db.upcast());
+ let text_range = span.value.text_range();
+ writeln!(f, "{}", span_formatter(file_id, text_range))?;
}
- let source_map = db.body_with_source_map(*def).1;
- let span: InFile<SyntaxNodePtr> = match span {
- MirSpan::ExprId(e) => match source_map.expr_syntax(*e) {
- Ok(s) => s.map(|x| x.into()),
- Err(_) => continue,
- },
- MirSpan::PatId(p) => match source_map.pat_syntax(*p) {
- Ok(s) => s.map(|x| match x {
- Either::Left(e) => e.into(),
- Either::Right(e) => e.into(),
- }),
- Err(_) => continue,
- },
- MirSpan::Unknown => continue,
- };
- let file_id = span.file_id.original_file(db.upcast());
- let text_range = span.value.text_range();
- writeln!(f, "{}", span_formatter(file_id, text_range))?;
}
match err {
MirEvalError::InFunction(..) => unreachable!(),
@@ -413,13 +439,7 @@ impl std::fmt::Debug for MirEvalError {
let data = &arg0.data(Interner);
f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish()
}
- Self::InFunction(func, e, span, _) => {
- let mut e = &**e;
- let mut stack = vec![(*func, *span)];
- while let Self::InFunction(f, next_e, span, _) = e {
- e = &next_e;
- stack.push((*f, *span));
- }
+ Self::InFunction(e, stack) => {
f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish()
}
}
@@ -435,10 +455,10 @@ struct DropFlags {
impl DropFlags {
fn add_place(&mut self, p: Place) {
- if p.iterate_over_parents().any(|x| self.need_drop.contains(&x)) {
+ if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) {
return;
}
- self.need_drop.retain(|x| !p.is_parent(x));
+ self.need_drop.retain(|it| !p.is_parent(it));
self.need_drop.insert(p);
}
@@ -449,15 +469,15 @@ impl DropFlags {
}
#[derive(Debug)]
-struct Locals<'a> {
- ptr: &'a ArenaMap<LocalId, Interval>,
- body: &'a MirBody,
+struct Locals {
+ ptr: ArenaMap<LocalId, Interval>,
+ body: Arc<MirBody>,
drop_flags: DropFlags,
}
pub fn interpret_mir(
db: &dyn HirDatabase,
- body: &MirBody,
+ body: Arc<MirBody>,
// FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now
// they share their body with their parent, so in MIR lowering we have locals of the parent body, which
// might have placeholders. With this argument, we (wrongly) assume that every placeholder type has
@@ -466,19 +486,22 @@ 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 x: Result<Const> = (|| {
- let bytes = evaluator.interpret_mir(&body, None.into_iter())?;
+ 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");
+ }
+ let bytes = evaluator.interpret_mir(body.clone(), None.into_iter())?;
let mut memory_map = evaluator.create_memory_map(
&bytes,
&ty,
- &Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() },
+ &Locals { ptr: ArenaMap::new(), body, drop_flags: DropFlags::default() },
)?;
memory_map.vtable = evaluator.vtable_map.clone();
return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
})();
(
- x,
+ it,
String::from_utf8_lossy(&evaluator.stdout).into_owned(),
String::from_utf8_lossy(&evaluator.stderr).into_owned(),
)
@@ -487,18 +510,20 @@ 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],
+ code_stack: vec![],
vtable_map: VTableMap::default(),
thread_local_storage: TlsData::default(),
static_locations: HashMap::default(),
db,
+ random_state: oorandom::Rand64::new(0),
trait_env,
crate_id,
stdout: vec![],
@@ -506,14 +531,16 @@ impl Evaluator<'_> {
assert_placeholder_ty_is_unused,
stack_depth_limit: 100,
execution_limit: 1000_000,
+ memory_limit: 1000_000_000, // 2GB, 1GB for stack and 1GB for heap
+ layout_cache: RefCell::new(HashMap::default()),
}
}
- fn place_addr(&self, p: &Place, locals: &Locals<'_>) -> Result<Address> {
+ fn place_addr(&self, p: &Place, locals: &Locals) -> Result<Address> {
Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0)
}
- fn place_interval(&self, p: &Place, locals: &Locals<'_>) -> Result<Interval> {
+ fn place_interval(&self, p: &Place, locals: &Locals) -> Result<Interval> {
let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?;
Ok(Interval {
addr: place_addr_and_ty.0,
@@ -527,7 +554,7 @@ impl Evaluator<'_> {
fn ptr_size(&self) -> usize {
match self.db.target_data_layout(self.crate_id) {
- Some(x) => x.pointer_size.bytes_usize(),
+ Some(it) => it.pointer_size.bytes_usize(),
None => 8,
}
}
@@ -535,7 +562,7 @@ impl Evaluator<'_> {
fn place_addr_and_ty_and_metadata<'a>(
&'a self,
p: &Place,
- locals: &'a Locals<'a>,
+ locals: &'a Locals,
) -> Result<(Address, Ty, Option<IntervalOrOwned>)> {
let mut addr = locals.ptr[p.local].addr;
let mut ty: Ty = locals.body.locals[p.local].ty.clone();
@@ -569,8 +596,8 @@ impl Evaluator<'_> {
} else {
None
};
- let x = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?);
- addr = Address::from_usize(x);
+ let it = from_bytes!(usize, self.read_memory(addr, self.ptr_size())?);
+ addr = Address::from_usize(it);
}
ProjectionElem::Index(op) => {
let offset = from_bytes!(
@@ -586,13 +613,13 @@ impl Evaluator<'_> {
let offset = if from_end {
let len = match prev_ty.kind(Interner) {
TyKind::Array(_, c) => match try_const_usize(self.db, c) {
- Some(x) => x as u64,
+ Some(it) => it as u64,
None => {
not_supported!("indexing array with unknown const from end")
}
},
TyKind::Slice(_) => match metadata {
- Some(x) => from_bytes!(u64, x.get(self)?),
+ Some(it) => from_bytes!(u64, it.get(self)?),
None => not_supported!("slice place without metadata"),
},
_ => not_supported!("bad type for const index"),
@@ -612,8 +639,8 @@ impl Evaluator<'_> {
_ => TyKind::Error.intern(Interner),
};
metadata = match metadata {
- Some(x) => {
- let prev_len = from_bytes!(u64, x.get(self)?);
+ Some(it) => {
+ let prev_len = from_bytes!(u64, it.get(self)?);
Some(IntervalOrOwned::Owned(
(prev_len - from - to).to_le_bytes().to_vec(),
))
@@ -636,8 +663,8 @@ impl Evaluator<'_> {
Variants::Single { .. } => &layout,
Variants::Multiple { variants, .. } => {
&variants[match f.parent {
- hir_def::VariantId::EnumVariantId(x) => {
- RustcEnumVariantIdx(x.local_id)
+ hir_def::VariantId::EnumVariantId(it) => {
+ RustcEnumVariantIdx(it.local_id)
}
_ => {
return Err(MirEvalError::TypeError(
@@ -662,9 +689,15 @@ impl Evaluator<'_> {
}
fn layout(&self, ty: &Ty) -> Result<Arc<Layout>> {
- self.db
+ if let Some(x) = self.layout_cache.borrow().get(ty) {
+ return Ok(x.clone());
+ }
+ let r = self
+ .db
.layout_of_ty(ty.clone(), self.crate_id)
- .map_err(|e| MirEvalError::LayoutError(e, ty.clone()))
+ .map_err(|e| MirEvalError::LayoutError(e, ty.clone()))?;
+ self.layout_cache.borrow_mut().insert(ty.clone(), r.clone());
+ Ok(r)
}
fn layout_adt(&self, adt: AdtId, subst: Substitution) -> Result<Arc<Layout>> {
@@ -673,11 +706,11 @@ impl Evaluator<'_> {
})
}
- fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals<'a>) -> Result<Ty> {
+ fn place_ty<'a>(&'a self, p: &Place, locals: &'a Locals) -> Result<Ty> {
Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1)
}
- fn operand_ty(&self, o: &Operand, locals: &Locals<'_>) -> Result<Ty> {
+ fn operand_ty(&self, o: &Operand, locals: &Locals) -> Result<Ty> {
Ok(match o {
Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?,
Operand::Constant(c) => c.data(Interner).ty.clone(),
@@ -688,11 +721,7 @@ impl Evaluator<'_> {
})
}
- fn operand_ty_and_eval(
- &mut self,
- o: &Operand,
- locals: &mut Locals<'_>,
- ) -> Result<IntervalAndTy> {
+ fn operand_ty_and_eval(&mut self, o: &Operand, locals: &mut Locals) -> Result<IntervalAndTy> {
Ok(IntervalAndTy {
interval: self.eval_operand(o, locals)?,
ty: self.operand_ty(o, locals)?,
@@ -701,25 +730,209 @@ impl Evaluator<'_> {
fn interpret_mir(
&mut self,
- body: &MirBody,
- args: impl Iterator<Item = Vec<u8>>,
+ body: Arc<MirBody>,
+ args: impl Iterator<Item = IntervalOrOwned>,
) -> Result<Vec<u8>> {
- if let Some(x) = self.stack_depth_limit.checked_sub(1) {
- self.stack_depth_limit = x;
+ if let Some(it) = self.stack_depth_limit.checked_sub(1) {
+ self.stack_depth_limit = it;
} else {
return Err(MirEvalError::StackOverflow);
}
let mut current_block_idx = body.start_block;
+ let (mut locals, prev_stack_ptr) = self.create_locals_for_body(body.clone(), None)?;
+ self.fill_locals_for_body(&body, &mut locals, args)?;
+ let prev_code_stack = mem::take(&mut self.code_stack);
+ let span = (MirSpan::Unknown, body.owner);
+ self.code_stack.push(StackFrame { body, locals, destination: None, prev_stack_ptr, span });
+ 'stack: loop {
+ let Some(mut my_stack_frame) = self.code_stack.pop() else {
+ not_supported!("missing stack frame");
+ };
+ let e = (|| {
+ let mut locals = &mut my_stack_frame.locals;
+ let body = &*my_stack_frame.body;
+ loop {
+ let current_block = &body.basic_blocks[current_block_idx];
+ if let Some(it) = self.execution_limit.checked_sub(1) {
+ self.execution_limit = it;
+ } else {
+ return Err(MirEvalError::ExecutionLimitExceeded);
+ }
+ for statement in &current_block.statements {
+ match &statement.kind {
+ StatementKind::Assign(l, r) => {
+ let addr = self.place_addr(l, &locals)?;
+ let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?;
+ self.write_memory(addr, &result)?;
+ locals.drop_flags.add_place(l.clone());
+ }
+ StatementKind::Deinit(_) => not_supported!("de-init statement"),
+ StatementKind::StorageLive(_)
+ | StatementKind::StorageDead(_)
+ | StatementKind::Nop => (),
+ }
+ }
+ let Some(terminator) = current_block.terminator.as_ref() else {
+ not_supported!("block without terminator");
+ };
+ match &terminator.kind {
+ TerminatorKind::Goto { target } => {
+ current_block_idx = *target;
+ }
+ TerminatorKind::Call {
+ func,
+ args,
+ destination,
+ target,
+ cleanup: _,
+ from_hir_call: _,
+ } => {
+ let destination_interval = self.place_interval(destination, &locals)?;
+ let fn_ty = self.operand_ty(func, &locals)?;
+ let args = args
+ .iter()
+ .map(|it| self.operand_ty_and_eval(it, &mut locals))
+ .collect::<Result<Vec<_>>>()?;
+ let stack_frame = match &fn_ty.data(Interner).kind {
+ TyKind::Function(_) => {
+ let bytes = self.eval_operand(func, &mut locals)?;
+ self.exec_fn_pointer(
+ bytes,
+ destination_interval,
+ &args,
+ &locals,
+ *target,
+ terminator.span,
+ )?
+ }
+ TyKind::FnDef(def, generic_args) => self.exec_fn_def(
+ *def,
+ generic_args,
+ destination_interval,
+ &args,
+ &locals,
+ *target,
+ terminator.span,
+ )?,
+ it => not_supported!("unknown function type {it:?}"),
+ };
+ locals.drop_flags.add_place(destination.clone());
+ if let Some(stack_frame) = stack_frame {
+ self.code_stack.push(my_stack_frame);
+ current_block_idx = stack_frame.body.start_block;
+ self.code_stack.push(stack_frame);
+ return Ok(None);
+ } else {
+ current_block_idx =
+ target.ok_or(MirEvalError::UndefinedBehavior(
+ "Diverging function returned".to_owned(),
+ ))?;
+ }
+ }
+ TerminatorKind::SwitchInt { discr, targets } => {
+ let val = u128::from_le_bytes(pad16(
+ self.eval_operand(discr, &mut locals)?.get(&self)?,
+ false,
+ ));
+ current_block_idx = targets.target_for_value(val);
+ }
+ TerminatorKind::Return => {
+ break;
+ }
+ TerminatorKind::Unreachable => {
+ return Err(MirEvalError::UndefinedBehavior(
+ "unreachable executed".to_owned(),
+ ));
+ }
+ TerminatorKind::Drop { place, target, unwind: _ } => {
+ self.drop_place(place, &mut locals, terminator.span)?;
+ current_block_idx = *target;
+ }
+ _ => not_supported!("unknown terminator"),
+ }
+ }
+ Ok(Some(my_stack_frame))
+ })();
+ let my_stack_frame = match e {
+ Ok(None) => continue 'stack,
+ Ok(Some(x)) => x,
+ Err(e) => {
+ let my_code_stack = mem::replace(&mut self.code_stack, prev_code_stack);
+ let mut error_stack = vec![];
+ for frame in my_code_stack.into_iter().rev() {
+ if let DefWithBodyId::FunctionId(f) = frame.body.owner {
+ error_stack.push((Either::Left(f), frame.span.0, frame.span.1));
+ }
+ }
+ return Err(MirEvalError::InFunction(Box::new(e), error_stack));
+ }
+ };
+ match my_stack_frame.destination {
+ None => {
+ self.code_stack = prev_code_stack;
+ self.stack_depth_limit += 1;
+ return Ok(my_stack_frame.locals.ptr[return_slot()].get(self)?.to_vec());
+ }
+ Some(bb) => {
+ // We don't support const promotion, so we can't truncate the stack yet.
+ let _ = my_stack_frame.prev_stack_ptr;
+ // self.stack.truncate(my_stack_frame.prev_stack_ptr);
+ current_block_idx = bb;
+ }
+ }
+ }
+ }
+
+ fn fill_locals_for_body(
+ &mut self,
+ body: &MirBody,
+ locals: &mut Locals,
+ args: impl Iterator<Item = IntervalOrOwned>,
+ ) -> Result<()> {
+ let mut remain_args = body.param_locals.len();
+ for ((l, interval), value) in locals.ptr.iter().skip(1).zip(args) {
+ locals.drop_flags.add_place(l.into());
+ match value {
+ IntervalOrOwned::Owned(value) => interval.write_from_bytes(self, &value)?,
+ IntervalOrOwned::Borrowed(value) => interval.write_from_interval(self, value)?,
+ }
+ if remain_args == 0 {
+ return Err(MirEvalError::TypeError("more arguments provided"));
+ }
+ remain_args -= 1;
+ }
+ if remain_args > 0 {
+ return Err(MirEvalError::TypeError("not enough arguments provided"));
+ }
+ Ok(())
+ }
+
+ fn create_locals_for_body(
+ &mut self,
+ body: Arc<MirBody>,
+ destination: Option<Interval>,
+ ) -> Result<(Locals, usize)> {
let mut locals =
- Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() };
+ Locals { ptr: ArenaMap::new(), body: body.clone(), drop_flags: DropFlags::default() };
let (locals_ptr, stack_size) = {
let mut stack_ptr = self.stack.len();
let addr = body
.locals
.iter()
- .map(|(id, x)| {
- let size =
- self.size_of_sized(&x.ty, &locals, "no unsized local in extending stack")?;
+ .map(|(id, it)| {
+ if id == return_slot() {
+ if let Some(destination) = destination {
+ return Ok((id, destination));
+ }
+ }
+ let (size, align) = self.size_align_of_sized(
+ &it.ty,
+ &locals,
+ "no unsized local in extending stack",
+ )?;
+ while stack_ptr % align != 0 {
+ stack_ptr += 1;
+ }
let my_ptr = stack_ptr;
stack_ptr += size;
Ok((id, Interval { addr: Stack(my_ptr), size }))
@@ -728,115 +941,21 @@ impl Evaluator<'_> {
let stack_size = stack_ptr - self.stack.len();
(addr, stack_size)
};
- locals.ptr = &locals_ptr;
- self.stack.extend(iter::repeat(0).take(stack_size));
- let mut remain_args = body.param_locals.len();
- for ((l, interval), value) in locals_ptr.iter().skip(1).zip(args) {
- locals.drop_flags.add_place(l.into());
- interval.write_from_bytes(self, &value)?;
- if remain_args == 0 {
- return Err(MirEvalError::TypeError("more arguments provided"));
- }
- remain_args -= 1;
- }
- if remain_args > 0 {
- return Err(MirEvalError::TypeError("not enough arguments provided"));
- }
- loop {
- let current_block = &body.basic_blocks[current_block_idx];
- if let Some(x) = self.execution_limit.checked_sub(1) {
- self.execution_limit = x;
- } else {
- return Err(MirEvalError::ExecutionLimitExceeded);
- }
- for statement in &current_block.statements {
- match &statement.kind {
- StatementKind::Assign(l, r) => {
- let addr = self.place_addr(l, &locals)?;
- let result = self.eval_rvalue(r, &mut locals)?.to_vec(&self)?;
- self.write_memory(addr, &result)?;
- locals.drop_flags.add_place(l.clone());
- }
- StatementKind::Deinit(_) => not_supported!("de-init statement"),
- StatementKind::StorageLive(_)
- | StatementKind::StorageDead(_)
- | StatementKind::Nop => (),
- }
- }
- let Some(terminator) = current_block.terminator.as_ref() else {
- not_supported!("block without terminator");
- };
- match &terminator.kind {
- TerminatorKind::Goto { target } => {
- current_block_idx = *target;
- }
- TerminatorKind::Call {
- func,
- args,
- destination,
- target,
- cleanup: _,
- from_hir_call: _,
- } => {
- let destination_interval = self.place_interval(destination, &locals)?;
- let fn_ty = self.operand_ty(func, &locals)?;
- let args = args
- .iter()
- .map(|x| self.operand_ty_and_eval(x, &mut locals))
- .collect::<Result<Vec<_>>>()?;
- match &fn_ty.data(Interner).kind {
- TyKind::Function(_) => {
- let bytes = self.eval_operand(func, &mut locals)?;
- self.exec_fn_pointer(
- bytes,
- destination_interval,
- &args,
- &locals,
- terminator.span,
- )?;
- }
- TyKind::FnDef(def, generic_args) => {
- self.exec_fn_def(
- *def,
- generic_args,
- destination_interval,
- &args,
- &locals,
- terminator.span,
- )?;
- }
- x => not_supported!("unknown function type {x:?}"),
- }
- locals.drop_flags.add_place(destination.clone());
- current_block_idx = target.expect("broken mir, function without target");
- }
- TerminatorKind::SwitchInt { discr, targets } => {
- let val = u128::from_le_bytes(pad16(
- self.eval_operand(discr, &mut locals)?.get(&self)?,
- false,
- ));
- current_block_idx = targets.target_for_value(val);
- }
- TerminatorKind::Return => {
- self.stack_depth_limit += 1;
- return Ok(locals.ptr[return_slot()].get(self)?.to_vec());
- }
- TerminatorKind::Unreachable => {
- return Err(MirEvalError::UndefinedBehavior("unreachable executed".to_owned()));
- }
- TerminatorKind::Drop { place, target, unwind: _ } => {
- self.drop_place(place, &mut locals, terminator.span)?;
- current_block_idx = *target;
- }
- _ => not_supported!("unknown terminator"),
- }
+ locals.ptr = locals_ptr;
+ let prev_stack_pointer = self.stack.len();
+ if stack_size > self.memory_limit {
+ return Err(MirEvalError::Panic(format!(
+ "Stack overflow. Tried to grow stack to {stack_size} bytes"
+ )));
}
+ self.stack.extend(iter::repeat(0).take(stack_size));
+ Ok((locals, prev_stack_pointer))
}
- fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result<IntervalOrOwned> {
+ fn eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals) -> Result<IntervalOrOwned> {
use IntervalOrOwned::*;
Ok(match r {
- Rvalue::Use(x) => Borrowed(self.eval_operand(x, locals)?),
+ Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?),
Rvalue::Ref(_, p) => {
let (addr, _, metadata) = self.place_addr_and_ty_and_metadata(p, locals)?;
let mut r = addr.to_bytes();
@@ -881,9 +1000,9 @@ impl Evaluator<'_> {
c[0] = 1 - c[0];
} else {
match op {
- UnOp::Not => c.iter_mut().for_each(|x| *x = !*x),
+ UnOp::Not => c.iter_mut().for_each(|it| *it = !*it),
UnOp::Neg => {
- c.iter_mut().for_each(|x| *x = !*x);
+ c.iter_mut().for_each(|it| *it = !*it);
for k in c.iter_mut() {
let o;
(*k, o) = k.overflowing_add(1);
@@ -948,8 +1067,8 @@ impl Evaluator<'_> {
};
Owned(r.to_le_bytes().into())
}
- x => not_supported!(
- "invalid binop {x:?} on floating point operators"
+ it => not_supported!(
+ "invalid binop {it:?} on floating point operators"
),
}
}
@@ -976,8 +1095,8 @@ impl Evaluator<'_> {
};
Owned(r.to_le_bytes().into())
}
- x => not_supported!(
- "invalid binop {x:?} on floating point operators"
+ it => not_supported!(
+ "invalid binop {it:?} on floating point operators"
),
}
}
@@ -1034,13 +1153,18 @@ impl Evaluator<'_> {
BinOp::Shr => l128.checked_shr(shift_amount),
_ => unreachable!(),
};
+ if shift_amount as usize >= lc.len() * 8 {
+ return Err(MirEvalError::Panic(format!(
+ "Overflow in {op:?}"
+ )));
+ }
if let Some(r) = r {
break 'b r;
}
};
return Err(MirEvalError::Panic(format!("Overflow in {op:?}")));
};
- check_overflow(r)?
+ Owned(r.to_le_bytes()[..lc.len()].to_vec())
}
BinOp::Offset => not_supported!("offset binop"),
}
@@ -1049,64 +1173,15 @@ impl Evaluator<'_> {
Rvalue::Discriminant(p) => {
let ty = self.place_ty(p, locals)?;
let bytes = self.eval_place(p, locals)?.get(&self)?;
- let layout = self.layout(&ty)?;
- let enum_id = 'b: {
- match ty.kind(Interner) {
- TyKind::Adt(e, _) => match e.0 {
- AdtId::EnumId(e) => break 'b e,
- _ => (),
- },
- _ => (),
- }
- return Ok(Owned(0u128.to_le_bytes().to_vec()));
- };
- match &layout.variants {
- Variants::Single { index } => {
- let r = self.const_eval_discriminant(EnumVariantId {
- parent: enum_id,
- local_id: index.0,
- })?;
- Owned(r.to_le_bytes().to_vec())
- }
- Variants::Multiple { tag, tag_encoding, variants, .. } => {
- let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
- not_supported!("missing target data layout");
- };
- let size = tag.size(&*target_data_layout).bytes_usize();
- let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
- match tag_encoding {
- TagEncoding::Direct => {
- let tag = &bytes[offset..offset + size];
- Owned(pad16(tag, false).to_vec())
- }
- TagEncoding::Niche { untagged_variant, niche_start, .. } => {
- let tag = &bytes[offset..offset + size];
- let candidate_tag = i128::from_le_bytes(pad16(tag, false))
- .wrapping_sub(*niche_start as i128)
- as usize;
- let variant = variants
- .iter_enumerated()
- .map(|(x, _)| x)
- .filter(|x| x != untagged_variant)
- .nth(candidate_tag)
- .unwrap_or(*untagged_variant)
- .0;
- let result = self.const_eval_discriminant(EnumVariantId {
- parent: enum_id,
- local_id: variant,
- })?;
- Owned(result.to_le_bytes().to_vec())
- }
- }
- }
- }
+ let result = self.compute_discriminant(ty, bytes)?;
+ Owned(result.to_le_bytes().to_vec())
}
- Rvalue::Repeat(x, len) => {
+ Rvalue::Repeat(it, len) => {
let len = match try_const_usize(self.db, &len) {
- Some(x) => x as usize,
+ Some(it) => it as usize,
None => not_supported!("non evaluatable array len in repeat Rvalue"),
};
- let val = self.eval_operand(x, locals)?.get(self)?;
+ let val = self.eval_operand(it, locals)?.get(self)?;
let size = len * val.len();
Owned(val.iter().copied().cycle().take(size).collect())
}
@@ -1115,20 +1190,20 @@ impl Evaluator<'_> {
let Some((size, align)) = self.size_align_of(ty, locals)? else {
not_supported!("unsized box initialization");
};
- let addr = self.heap_allocate(size, align);
+ let addr = self.heap_allocate(size, align)?;
Owned(addr.to_bytes())
}
Rvalue::CopyForDeref(_) => not_supported!("copy for deref"),
Rvalue::Aggregate(kind, values) => {
let values = values
.iter()
- .map(|x| self.eval_operand(x, locals))
+ .map(|it| self.eval_operand(it, locals))
.collect::<Result<Vec<_>>>()?;
match kind {
AggregateKind::Array(_) => {
let mut r = vec![];
- for x in values {
- let value = x.get(&self)?;
+ for it in values {
+ let value = it.get(&self)?;
r.extend(value);
}
Owned(r)
@@ -1139,11 +1214,12 @@ impl Evaluator<'_> {
layout.size.bytes_usize(),
&layout,
None,
- values.iter().map(|&x| x.into()),
+ values.iter().map(|&it| it.into()),
)?)
}
- AggregateKind::Union(x, f) => {
- let layout = self.layout_adt((*x).into(), Substitution::empty(Interner))?;
+ AggregateKind::Union(it, f) => {
+ let layout =
+ self.layout_adt((*it).into(), Substitution::empty(Interner))?;
let offset = layout
.fields
.offset(u32::from(f.local_id.into_raw()) as usize)
@@ -1153,14 +1229,14 @@ impl Evaluator<'_> {
result[offset..offset + op.len()].copy_from_slice(op);
Owned(result)
}
- AggregateKind::Adt(x, subst) => {
+ AggregateKind::Adt(it, subst) => {
let (size, variant_layout, tag) =
- self.layout_of_variant(*x, subst.clone(), locals)?;
+ self.layout_of_variant(*it, subst.clone(), locals)?;
Owned(self.make_by_layout(
size,
&variant_layout,
tag,
- values.iter().map(|&x| x.into()),
+ values.iter().map(|&it| it.into()),
)?)
}
AggregateKind::Closure(ty) => {
@@ -1169,7 +1245,7 @@ impl Evaluator<'_> {
layout.size.bytes_usize(),
&layout,
None,
- values.iter().map(|&x| x.into()),
+ values.iter().map(|&it| it.into()),
)?)
}
}
@@ -1229,21 +1305,75 @@ impl Evaluator<'_> {
})
}
+ fn compute_discriminant(&self, ty: Ty, bytes: &[u8]) -> Result<i128> {
+ let layout = self.layout(&ty)?;
+ let enum_id = 'b: {
+ match ty.kind(Interner) {
+ TyKind::Adt(e, _) => match e.0 {
+ AdtId::EnumId(e) => break 'b e,
+ _ => (),
+ },
+ _ => (),
+ }
+ return Ok(0);
+ };
+ match &layout.variants {
+ Variants::Single { index } => {
+ let r = self.const_eval_discriminant(EnumVariantId {
+ parent: enum_id,
+ local_id: index.0,
+ })?;
+ Ok(r)
+ }
+ Variants::Multiple { tag, tag_encoding, variants, .. } => {
+ let Some(target_data_layout) = self.db.target_data_layout(self.crate_id) else {
+ not_supported!("missing target data layout");
+ };
+ let size = tag.size(&*target_data_layout).bytes_usize();
+ let offset = layout.fields.offset(0).bytes_usize(); // The only field on enum variants is the tag field
+ match tag_encoding {
+ TagEncoding::Direct => {
+ let tag = &bytes[offset..offset + size];
+ Ok(i128::from_le_bytes(pad16(tag, false)))
+ }
+ TagEncoding::Niche { untagged_variant, niche_start, .. } => {
+ let tag = &bytes[offset..offset + size];
+ let candidate_tag = i128::from_le_bytes(pad16(tag, false))
+ .wrapping_sub(*niche_start as i128)
+ as usize;
+ let variant = variants
+ .iter_enumerated()
+ .map(|(it, _)| it)
+ .filter(|it| it != untagged_variant)
+ .nth(candidate_tag)
+ .unwrap_or(*untagged_variant)
+ .0;
+ let result = self.const_eval_discriminant(EnumVariantId {
+ parent: enum_id,
+ local_id: variant,
+ })?;
+ Ok(result)
+ }
+ }
+ }
+ }
+ }
+
fn coerce_unsized_look_through_fields<T>(
&self,
ty: &Ty,
goal: impl Fn(&TyKind) -> Option<T>,
) -> Result<T> {
let kind = ty.kind(Interner);
- if let Some(x) = goal(kind) {
- return Ok(x);
+ if let Some(it) = goal(kind) {
+ return Ok(it);
}
if let TyKind::Adt(id, subst) = kind {
if let AdtId::StructId(struct_id) = id.0 {
let field_types = self.db.field_types(struct_id.into());
let mut field_types = field_types.iter();
if let Some(ty) =
- field_types.next().map(|x| x.1.clone().substitute(Interner, subst))
+ field_types.next().map(|it| it.1.clone().substitute(Interner, subst))
{
return self.coerce_unsized_look_through_fields(&ty, goal);
}
@@ -1258,66 +1388,99 @@ impl Evaluator<'_> {
current_ty: &Ty,
target_ty: &Ty,
) -> Result<IntervalOrOwned> {
- use IntervalOrOwned::*;
- fn for_ptr(x: &TyKind) -> Option<Ty> {
- match x {
+ fn for_ptr(it: &TyKind) -> Option<Ty> {
+ match it {
TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => Some(ty.clone()),
_ => None,
}
}
- Ok(match self.coerce_unsized_look_through_fields(target_ty, for_ptr)? {
- ty => match &ty.data(Interner).kind {
- TyKind::Slice(_) => {
- match self.coerce_unsized_look_through_fields(current_ty, for_ptr)? {
- ty => match &ty.data(Interner).kind {
- TyKind::Array(_, size) => {
- let len = match try_const_usize(self.db, size) {
- None => not_supported!(
- "unevaluatble len of array in coerce unsized"
- ),
- Some(x) => x as usize,
- };
- let mut r = Vec::with_capacity(16);
- let addr = addr.get(self)?;
- r.extend(addr.iter().copied());
- r.extend(len.to_le_bytes().into_iter());
- Owned(r)
- }
- t => {
- not_supported!("slice unsizing from non array type {t:?}")
- }
- },
- }
+ let target_ty = self.coerce_unsized_look_through_fields(target_ty, for_ptr)?;
+ let current_ty = self.coerce_unsized_look_through_fields(current_ty, for_ptr)?;
+
+ self.unsizing_ptr_from_addr(target_ty, current_ty, addr)
+ }
+
+ /// Adds metadata to the address and create the fat pointer result of the unsizing operation.
+ fn unsizing_ptr_from_addr(
+ &mut self,
+ target_ty: Ty,
+ current_ty: Ty,
+ addr: Interval,
+ ) -> Result<IntervalOrOwned> {
+ use IntervalOrOwned::*;
+ Ok(match &target_ty.data(Interner).kind {
+ TyKind::Slice(_) => match &current_ty.data(Interner).kind {
+ TyKind::Array(_, size) => {
+ let len = match try_const_usize(self.db, size) {
+ None => {
+ not_supported!("unevaluatble len of array in coerce unsized")
+ }
+ Some(it) => it as usize,
+ };
+ let mut r = Vec::with_capacity(16);
+ let addr = addr.get(self)?;
+ r.extend(addr.iter().copied());
+ r.extend(len.to_le_bytes().into_iter());
+ Owned(r)
}
- TyKind::Dyn(_) => match &current_ty.data(Interner).kind {
- TyKind::Raw(_, ty) | TyKind::Ref(_, _, ty) => {
- let vtable = self.vtable_map.id(ty.clone());
- let mut r = Vec::with_capacity(16);
- let addr = addr.get(self)?;
- r.extend(addr.iter().copied());
- r.extend(vtable.to_le_bytes().into_iter());
- Owned(r)
+ t => {
+ not_supported!("slice unsizing from non array type {t:?}")
+ }
+ },
+ TyKind::Dyn(_) => {
+ let vtable = self.vtable_map.id(current_ty.clone());
+ let mut r = Vec::with_capacity(16);
+ let addr = addr.get(self)?;
+ r.extend(addr.iter().copied());
+ r.extend(vtable.to_le_bytes().into_iter());
+ Owned(r)
+ }
+ TyKind::Adt(id, target_subst) => match &current_ty.data(Interner).kind {
+ TyKind::Adt(current_id, current_subst) => {
+ if id != current_id {
+ not_supported!("unsizing struct with different type");
}
- _ => not_supported!("dyn unsizing from non pointers"),
- },
- _ => not_supported!("unknown unsized cast"),
+ let id = match id.0 {
+ AdtId::StructId(s) => s,
+ AdtId::UnionId(_) => not_supported!("unsizing unions"),
+ AdtId::EnumId(_) => not_supported!("unsizing enums"),
+ };
+ let Some((last_field, _)) =
+ self.db.struct_data(id).variant_data.fields().iter().rev().next()
+ else {
+ not_supported!("unsizing struct without field");
+ };
+ let target_last_field = self.db.field_types(id.into())[last_field]
+ .clone()
+ .substitute(Interner, target_subst);
+ let current_last_field = self.db.field_types(id.into())[last_field]
+ .clone()
+ .substitute(Interner, current_subst);
+ return self.unsizing_ptr_from_addr(
+ target_last_field,
+ current_last_field,
+ addr,
+ );
+ }
+ _ => not_supported!("unsizing struct with non adt type"),
},
+ _ => not_supported!("unknown unsized cast"),
})
}
fn layout_of_variant(
&mut self,
- x: VariantId,
+ it: VariantId,
subst: Substitution,
- locals: &Locals<'_>,
+ locals: &Locals,
) -> Result<(usize, Arc<Layout>, Option<(usize, usize, i128)>)> {
- let adt = x.adt_id();
+ let adt = it.adt_id();
if let DefWithBodyId::VariantId(f) = locals.body.owner {
- if let VariantId::EnumVariantId(x) = x {
+ if let VariantId::EnumVariantId(it) = it {
if AdtId::from(f.parent) == adt {
// Computing the exact size of enums require resolving the enum discriminants. In order to prevent loops (and
// infinite sized type errors) we use a dummy layout
- let i = self.const_eval_discriminant(x)?;
+ let i = self.const_eval_discriminant(it)?;
return Ok((16, self.layout(&TyBuilder::unit())?, Some((0, 16, i))));
}
}
@@ -1330,8 +1493,8 @@ impl Evaluator<'_> {
.db
.target_data_layout(self.crate_id)
.ok_or(MirEvalError::TargetDataLayoutNotAvailable)?;
- let enum_variant_id = match x {
- VariantId::EnumVariantId(x) => x,
+ let enum_variant_id = match it {
+ VariantId::EnumVariantId(it) => it,
_ => not_supported!("multi variant layout for non-enums"),
};
let rustc_enum_variant_idx = RustcEnumVariantIdx(enum_variant_id.local_id);
@@ -1345,8 +1508,8 @@ impl Evaluator<'_> {
} else {
discriminant = (variants
.iter_enumerated()
- .filter(|(x, _)| x != untagged_variant)
- .position(|(x, _)| x == rustc_enum_variant_idx)
+ .filter(|(it, _)| it != untagged_variant)
+ .position(|(it, _)| it == rustc_enum_variant_idx)
.unwrap() as i128)
.wrapping_add(*niche_start as i128);
true
@@ -1389,8 +1552,8 @@ impl Evaluator<'_> {
Ok(result)
}
- fn eval_operand(&mut self, x: &Operand, locals: &mut Locals<'_>) -> Result<Interval> {
- Ok(match x {
+ fn eval_operand(&mut self, it: &Operand, locals: &mut Locals) -> Result<Interval> {
+ Ok(match it {
Operand::Copy(p) | Operand::Move(p) => {
locals.drop_flags.remove_place(p);
self.eval_place(p, locals)?
@@ -1399,61 +1562,63 @@ 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> {
- 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| {
- let addr = self.heap_allocate(b.len(), 1); // FIXME: align is wrong
- self.write_memory(addr, b)?;
- Ok(addr.to_usize())
+ 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");
+ };
+ 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> {
+ fn eval_place(&mut self, p: &Place, locals: &Locals) -> Result<Interval> {
let addr = self.place_addr(p, locals)?;
Ok(Interval::new(
addr,
@@ -1466,11 +1631,11 @@ impl Evaluator<'_> {
return Ok(&[]);
}
let (mem, pos) = match addr {
- Stack(x) => (&self.stack, x),
- Heap(x) => (&self.heap, x),
- Invalid(x) => {
+ Stack(it) => (&self.stack, it),
+ Heap(it) => (&self.heap, it),
+ Invalid(it) => {
return Err(MirEvalError::UndefinedBehavior(format!(
- "read invalid memory address {x} with size {size}"
+ "read invalid memory address {it} with size {size}"
)));
}
};
@@ -1478,28 +1643,30 @@ impl Evaluator<'_> {
.ok_or_else(|| MirEvalError::UndefinedBehavior("out of bound memory read".to_string()))
}
- fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> {
- if r.is_empty() {
- return Ok(());
- }
+ fn write_memory_using_ref(&mut self, addr: Address, size: usize) -> Result<&mut [u8]> {
let (mem, pos) = match addr {
- Stack(x) => (&mut self.stack, x),
- Heap(x) => (&mut self.heap, x),
- Invalid(x) => {
+ Stack(it) => (&mut self.stack, it),
+ Heap(it) => (&mut self.heap, it),
+ Invalid(it) => {
return Err(MirEvalError::UndefinedBehavior(format!(
- "write invalid memory address {x} with content {r:?}"
+ "write invalid memory address {it} with size {size}"
)));
}
};
- mem.get_mut(pos..pos + r.len())
- .ok_or_else(|| {
- MirEvalError::UndefinedBehavior("out of bound memory write".to_string())
- })?
- .copy_from_slice(r);
+ Ok(mem.get_mut(pos..pos + size).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior("out of bound memory write".to_string())
+ })?)
+ }
+
+ fn write_memory(&mut self, addr: Address, r: &[u8]) -> Result<()> {
+ if r.is_empty() {
+ return Ok(());
+ }
+ self.write_memory_using_ref(addr, r.len())?.copy_from_slice(r);
Ok(())
}
- fn size_align_of(&self, ty: &Ty, locals: &Locals<'_>) -> Result<Option<(usize, usize)>> {
+ fn size_align_of(&self, ty: &Ty, locals: &Locals) -> Result<Option<(usize, usize)>> {
if let DefWithBodyId::VariantId(f) = locals.body.owner {
if let Some((adt, _)) = ty.as_adt() {
if AdtId::from(f.parent) == adt {
@@ -1523,17 +1690,40 @@ impl Evaluator<'_> {
/// A version of `self.size_of` which returns error if the type is unsized. `what` argument should
/// be something that complete this: `error: type {ty} was unsized. {what} should be sized`
- fn size_of_sized(&self, ty: &Ty, locals: &Locals<'_>, what: &'static str) -> Result<usize> {
+ fn size_of_sized(&self, ty: &Ty, locals: &Locals, what: &'static str) -> Result<usize> {
match self.size_align_of(ty, locals)? {
- Some(x) => Ok(x.0),
+ Some(it) => Ok(it.0),
None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)),
}
}
- fn heap_allocate(&mut self, size: usize, _align: usize) -> Address {
+ /// A version of `self.size_align_of` which returns error if the type is unsized. `what` argument should
+ /// be something that complete this: `error: type {ty} was unsized. {what} should be sized`
+ fn size_align_of_sized(
+ &self,
+ ty: &Ty,
+ locals: &Locals,
+ what: &'static str,
+ ) -> Result<(usize, usize)> {
+ match self.size_align_of(ty, locals)? {
+ Some(it) => Ok(it),
+ None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)),
+ }
+ }
+
+ fn heap_allocate(&mut self, size: usize, align: usize) -> Result<Address> {
+ if !align.is_power_of_two() || align > 10000 {
+ return Err(MirEvalError::UndefinedBehavior(format!("Alignment {align} is invalid")));
+ }
+ while self.heap.len() % align != 0 {
+ self.heap.push(0);
+ }
+ if size.checked_add(self.heap.len()).map_or(true, |x| x > self.memory_limit) {
+ return Err(MirEvalError::Panic(format!("Memory allocation of {size} bytes failed")));
+ }
let pos = self.heap.len();
self.heap.extend(iter::repeat(0).take(size));
- Address::Heap(pos)
+ Ok(Address::Heap(pos))
}
fn detect_fn_trait(&self, def: FunctionId) -> Option<FnTrait> {
@@ -1541,7 +1731,7 @@ impl Evaluator<'_> {
let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else {
return None;
};
- let l = lang_attr(self.db.upcast(), parent)?;
+ let l = self.db.lang_attr(parent.into())?;
match l {
FnOnce => Some(FnTrait::FnOnce),
FnMut => Some(FnTrait::FnMut),
@@ -1550,12 +1740,12 @@ impl Evaluator<'_> {
}
}
- fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals<'_>) -> Result<MemoryMap> {
+ fn create_memory_map(&self, bytes: &[u8], ty: &Ty, locals: &Locals) -> Result<MemoryMap> {
fn rec(
this: &Evaluator<'_>,
bytes: &[u8],
ty: &Ty,
- locals: &Locals<'_>,
+ locals: &Locals,
mm: &mut MemoryMap,
) -> Result<()> {
match ty.kind(Interner) {
@@ -1661,7 +1851,7 @@ impl Evaluator<'_> {
old_vtable: &VTableMap,
addr: Address,
ty: &Ty,
- locals: &Locals<'_>,
+ locals: &Locals,
) -> Result<()> {
// FIXME: support indirect references
let layout = self.layout(ty)?;
@@ -1672,14 +1862,14 @@ impl Evaluator<'_> {
match size {
Some(_) => {
let current = from_bytes!(usize, self.read_memory(addr, my_size)?);
- if let Some(x) = patch_map.get(&current) {
- self.write_memory(addr, &x.to_le_bytes())?;
+ if let Some(it) = patch_map.get(&current) {
+ self.write_memory(addr, &it.to_le_bytes())?;
}
}
None => {
let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?);
- if let Some(x) = patch_map.get(&current) {
- self.write_memory(addr, &x.to_le_bytes())?;
+ if let Some(it) = patch_map.get(&current) {
+ self.write_memory(addr, &it.to_le_bytes())?;
}
}
}
@@ -1735,21 +1925,21 @@ impl Evaluator<'_> {
bytes: Interval,
destination: Interval,
args: &[IntervalAndTy],
- locals: &Locals<'_>,
+ locals: &Locals,
+ target_bb: Option<BasicBlockId>,
span: MirSpan,
- ) -> Result<()> {
+ ) -> Result<Option<StackFrame>> {
let id = from_bytes!(usize, bytes.get(self)?);
let next_ty = self.vtable_map.ty(id)?.clone();
match &next_ty.data(Interner).kind {
TyKind::FnDef(def, generic_args) => {
- self.exec_fn_def(*def, generic_args, destination, args, &locals, span)?;
+ self.exec_fn_def(*def, generic_args, destination, args, &locals, target_bb, span)
}
TyKind::Closure(id, subst) => {
- self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span)?;
+ self.exec_closure(*id, bytes.slice(0..0), subst, destination, args, locals, span)
}
- _ => return Err(MirEvalError::TypeError("function pointer to non function")),
+ _ => Err(MirEvalError::TypeError("function pointer to non function")),
}
- Ok(())
}
fn exec_closure(
@@ -1759,9 +1949,9 @@ impl Evaluator<'_> {
generic_args: &Substitution,
destination: Interval,
args: &[IntervalAndTy],
- locals: &Locals<'_>,
+ locals: &Locals,
span: MirSpan,
- ) -> Result<()> {
+ ) -> Result<Option<StackFrame>> {
let mir_body = self
.db
.monomorphized_mir_body_for_closure(
@@ -1769,7 +1959,7 @@ impl Evaluator<'_> {
generic_args.clone(),
self.trait_env.clone(),
)
- .map_err(|x| MirEvalError::MirLowerErrorForClosure(closure, x))?;
+ .map_err(|it| MirEvalError::MirLowerErrorForClosure(closure, it))?;
let closure_data = if mir_body.locals[mir_body.param_locals[0]].ty.as_reference().is_some()
{
closure_data.addr.to_bytes()
@@ -1777,12 +1967,18 @@ impl Evaluator<'_> {
closure_data.get(self)?.to_owned()
};
let arg_bytes = iter::once(Ok(closure_data))
- .chain(args.iter().map(|x| Ok(x.get(&self)?.to_owned())))
+ .chain(args.iter().map(|it| Ok(it.get(&self)?.to_owned())))
.collect::<Result<Vec<_>>>()?;
- let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| {
- MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner)
- })?;
- destination.write_from_bytes(self, &bytes)
+ let bytes = self
+ .interpret_mir(mir_body, arg_bytes.into_iter().map(IntervalOrOwned::Owned))
+ .map_err(|e| {
+ MirEvalError::InFunction(
+ Box::new(e),
+ vec![(Either::Right(closure), span, locals.body.owner)],
+ )
+ })?;
+ destination.write_from_bytes(self, &bytes)?;
+ Ok(None)
}
fn exec_fn_def(
@@ -1791,18 +1987,34 @@ impl Evaluator<'_> {
generic_args: &Substitution,
destination: Interval,
args: &[IntervalAndTy],
- locals: &Locals<'_>,
+ locals: &Locals,
+ target_bb: Option<BasicBlockId>,
span: MirSpan,
- ) -> Result<()> {
+ ) -> Result<Option<StackFrame>> {
let def: CallableDefId = from_chalk(self.db, def);
let generic_args = generic_args.clone();
match def {
CallableDefId::FunctionId(def) => {
if let Some(_) = self.detect_fn_trait(def) {
- self.exec_fn_trait(&args, destination, locals, span)?;
- return Ok(());
+ return self.exec_fn_trait(
+ def,
+ args,
+ generic_args,
+ locals,
+ destination,
+ target_bb,
+ span,
+ );
}
- self.exec_fn_with_args(def, args, generic_args, locals, destination, span)?;
+ self.exec_fn_with_args(
+ def,
+ args,
+ generic_args,
+ locals,
+ destination,
+ target_bb,
+ span,
+ )
}
CallableDefId::StructId(id) => {
let (size, variant_layout, tag) =
@@ -1811,9 +2023,10 @@ impl Evaluator<'_> {
size,
&variant_layout,
tag,
- args.iter().map(|x| x.interval.into()),
+ args.iter().map(|it| it.interval.into()),
)?;
destination.write_from_bytes(self, &result)?;
+ Ok(None)
}
CallableDefId::EnumVariantId(id) => {
let (size, variant_layout, tag) =
@@ -1822,12 +2035,12 @@ impl Evaluator<'_> {
size,
&variant_layout,
tag,
- args.iter().map(|x| x.interval.into()),
+ args.iter().map(|it| it.interval.into()),
)?;
destination.write_from_bytes(self, &result)?;
+ Ok(None)
}
}
- Ok(())
}
fn exec_fn_with_args(
@@ -1835,10 +2048,11 @@ impl Evaluator<'_> {
def: FunctionId,
args: &[IntervalAndTy],
generic_args: Substitution,
- locals: &Locals<'_>,
+ locals: &Locals,
destination: Interval,
+ target_bb: Option<BasicBlockId>,
span: MirSpan,
- ) -> Result<()> {
+ ) -> Result<Option<StackFrame>> {
if self.detect_and_exec_special_function(
def,
args,
@@ -1847,10 +2061,9 @@ impl Evaluator<'_> {
destination,
span,
)? {
- return Ok(());
+ return Ok(None);
}
- let arg_bytes =
- args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
+ let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval));
if let Some(self_ty_idx) =
is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone())
{
@@ -1858,74 +2071,103 @@ impl Evaluator<'_> {
// `&T`, `&mut T`, `Box<T>`, `Rc<T>`, `Arc<T>`, and `Pin<P>` where `P` is one of possible recievers,
// the vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on
// the type.
+ let first_arg = arg_bytes.clone().next().unwrap();
+ let first_arg = first_arg.get(self)?;
let ty =
- self.vtable_map.ty_of_bytes(&arg_bytes[0][self.ptr_size()..self.ptr_size() * 2])?;
+ self.vtable_map.ty_of_bytes(&first_arg[self.ptr_size()..self.ptr_size() * 2])?;
let mut args_for_target = args.to_vec();
args_for_target[0] = IntervalAndTy {
interval: args_for_target[0].interval.slice(0..self.ptr_size()),
ty: ty.clone(),
};
let ty = GenericArgData::Ty(ty.clone()).intern(Interner);
- let generics_for_target =
- Substitution::from_iter(
- Interner,
- generic_args.iter(Interner).enumerate().map(|(i, x)| {
- if i == self_ty_idx {
- &ty
- } else {
- x
- }
- }),
- );
+ let generics_for_target = Substitution::from_iter(
+ Interner,
+ generic_args.iter(Interner).enumerate().map(|(i, it)| {
+ if i == self_ty_idx {
+ &ty
+ } else {
+ it
+ }
+ }),
+ );
return self.exec_fn_with_args(
def,
&args_for_target,
generics_for_target,
locals,
destination,
+ target_bb,
span,
);
}
let (imp, generic_args) =
- lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args);
- self.exec_looked_up_function(generic_args, locals, imp, arg_bytes, span, destination)
+ self.db.lookup_impl_method(self.trait_env.clone(), def, generic_args);
+ self.exec_looked_up_function(
+ generic_args,
+ locals,
+ imp,
+ arg_bytes,
+ span,
+ destination,
+ target_bb,
+ )
}
fn exec_looked_up_function(
&mut self,
generic_args: Substitution,
- locals: &Locals<'_>,
+ locals: &Locals,
imp: FunctionId,
- arg_bytes: Vec<Vec<u8>>,
+ arg_bytes: impl Iterator<Item = IntervalOrOwned>,
span: MirSpan,
destination: Interval,
- ) -> Result<()> {
+ target_bb: Option<BasicBlockId>,
+ ) -> Result<Option<StackFrame>> {
let def = imp.into();
let mir_body = self
.db
.monomorphized_mir_body(def, generic_args, self.trait_env.clone())
.map_err(|e| {
MirEvalError::InFunction(
- Either::Left(imp),
Box::new(MirEvalError::MirLowerError(imp, e)),
- span,
- locals.body.owner,
+ vec![(Either::Left(imp), span, locals.body.owner)],
)
})?;
- let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| {
- MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner)
- })?;
- destination.write_from_bytes(self, &result)?;
- Ok(())
+ Ok(if let Some(target_bb) = target_bb {
+ let (mut locals, prev_stack_ptr) =
+ self.create_locals_for_body(mir_body.clone(), Some(destination))?;
+ self.fill_locals_for_body(&mir_body, &mut locals, arg_bytes.into_iter())?;
+ let span = (span, locals.body.owner);
+ Some(StackFrame {
+ body: mir_body,
+ locals,
+ destination: Some(target_bb),
+ prev_stack_ptr,
+ span,
+ })
+ } else {
+ let result = self.interpret_mir(mir_body, arg_bytes).map_err(|e| {
+ MirEvalError::InFunction(
+ Box::new(e),
+ vec![(Either::Left(imp), span, locals.body.owner)],
+ )
+ })?;
+ destination.write_from_bytes(self, &result)?;
+ None
+ })
}
fn exec_fn_trait(
&mut self,
+ def: FunctionId,
args: &[IntervalAndTy],
+ generic_args: Substitution,
+ locals: &Locals,
destination: Interval,
- locals: &Locals<'_>,
+ target_bb: Option<BasicBlockId>,
span: MirSpan,
- ) -> Result<()> {
+ ) -> Result<Option<StackFrame>> {
let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
let mut func_ty = func.ty.clone();
let mut func_data = func.interval;
@@ -1942,13 +2184,28 @@ impl Evaluator<'_> {
}
match &func_ty.data(Interner).kind {
TyKind::FnDef(def, subst) => {
- self.exec_fn_def(*def, subst, destination, &args[1..], locals, span)?;
+ return self.exec_fn_def(
+ *def,
+ subst,
+ destination,
+ &args[1..],
+ locals,
+ target_bb,
+ span,
+ );
}
TyKind::Function(_) => {
- self.exec_fn_pointer(func_data, destination, &args[1..], locals, span)?;
+ return self.exec_fn_pointer(
+ func_data,
+ destination,
+ &args[1..],
+ locals,
+ target_bb,
+ span,
+ );
}
TyKind::Closure(closure, subst) => {
- self.exec_closure(
+ return self.exec_closure(
*closure,
func_data,
&Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()),
@@ -1956,14 +2213,45 @@ impl Evaluator<'_> {
&args[1..],
locals,
span,
- )?;
+ );
+ }
+ _ => {
+ // try to execute the manual impl of `FnTrait` for structs (nightly feature used in std)
+ let arg0 = func;
+ let args = &args[1..];
+ let arg1 = {
+ let ty = TyKind::Tuple(
+ args.len(),
+ Substitution::from_iter(Interner, args.iter().map(|it| it.ty.clone())),
+ )
+ .intern(Interner);
+ let layout = self.layout(&ty)?;
+ let result = self.make_by_layout(
+ layout.size.bytes_usize(),
+ &layout,
+ None,
+ args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval)),
+ )?;
+ // FIXME: there is some leak here
+ let size = layout.size.bytes_usize();
+ let addr = self.heap_allocate(size, layout.align.abi.bytes() as usize)?;
+ self.write_memory(addr, &result)?;
+ IntervalAndTy { interval: Interval { addr, size }, ty }
+ };
+ return self.exec_fn_with_args(
+ def,
+ &[arg0.clone(), arg1],
+ generic_args,
+ locals,
+ destination,
+ target_bb,
+ span,
+ );
}
- x => not_supported!("Call FnTrait methods with type {x:?}"),
}
- Ok(())
}
- fn eval_static(&mut self, st: StaticId, locals: &Locals<'_>) -> Result<Address> {
+ fn eval_static(&mut self, st: StaticId, locals: &Locals) -> Result<Address> {
if let Some(o) = self.static_locations.get(&st) {
return Ok(*o);
};
@@ -1975,21 +2263,16 @@ 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 {
not_supported!("unsized extern static");
};
- let addr = self.heap_allocate(size, align);
+ let addr = self.heap_allocate(size, align)?;
Interval::new(addr, size)
};
- let addr = self.heap_allocate(self.ptr_size(), self.ptr_size());
+ let addr = self.heap_allocate(self.ptr_size(), self.ptr_size())?;
self.write_memory(addr, &result.addr.to_bytes())?;
self.static_locations.insert(st, addr);
Ok(addr)
@@ -2011,13 +2294,13 @@ impl Evaluator<'_> {
}
}
- fn drop_place(&mut self, place: &Place, locals: &mut Locals<'_>, span: MirSpan) -> Result<()> {
+ fn drop_place(&mut self, place: &Place, locals: &mut Locals, span: MirSpan) -> Result<()> {
let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?;
if !locals.drop_flags.remove_place(place) {
return Ok(());
}
let metadata = match metadata {
- Some(x) => x.get(self)?.to_vec(),
+ Some(it) => it.get(self)?.to_vec(),
None => vec![],
};
self.run_drop_glue_deep(ty, locals, addr, &metadata, span)
@@ -2026,7 +2309,7 @@ impl Evaluator<'_> {
fn run_drop_glue_deep(
&mut self,
ty: Ty,
- locals: &Locals<'_>,
+ locals: &Locals,
addr: Address,
_metadata: &[u8],
span: MirSpan,
@@ -2039,8 +2322,7 @@ impl Evaluator<'_> {
// we can ignore drop in them.
return Ok(());
};
- let (impl_drop_candidate, subst) = lookup_impl_method(
- self.db,
+ let (impl_drop_candidate, subst) = self.db.lookup_impl_method(
self.trait_env.clone(),
drop_fn,
Substitution::from1(Interner, ty.clone()),
@@ -2050,9 +2332,10 @@ impl Evaluator<'_> {
subst,
locals,
impl_drop_candidate,
- vec![addr.to_bytes()],
+ [IntervalOrOwned::Owned(addr.to_bytes())].into_iter(),
span,
Interval { addr: Address::Invalid(0), size: 0 },
+ None,
)?;
}
match ty.kind(Interner) {
@@ -2121,10 +2404,77 @@ impl Evaluator<'_> {
}
}
-pub fn pad16(x: &[u8], is_signed: bool) -> [u8; 16] {
- let is_negative = is_signed && x.last().unwrap_or(&0) > &128;
+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 };
- x.iter()
+ it.iter()
.copied()
.chain(iter::repeat(fill_with))
.take(16)