Implement recursion in mir interpreter without recursion

This commit is contained in:
hkalbasi 2023-07-07 15:07:29 +03:30
parent 3a1054fc1c
commit 4a444e768c
15 changed files with 432 additions and 264 deletions

View File

@ -18,7 +18,7 @@ use crate::{
generics::GenericParams,
import_map::ImportMap,
item_tree::{AttrOwner, ItemTree},
lang_item::{LangItem, LangItemTarget, LangItems},
lang_item::{self, LangItem, LangItemTarget, LangItems},
nameres::{diagnostics::DefDiagnostic, DefMap},
visibility::{self, Visibility},
AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId,
@ -204,6 +204,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
#[salsa::invoke(AttrsWithOwner::attrs_query)]
fn attrs(&self, def: AttrDefId) -> Attrs;
#[salsa::invoke(lang_item::lang_attr_query)]
fn lang_attr(&self, def: AttrDefId) -> Option<LangItem>;
#[salsa::transparent]
#[salsa::invoke(AttrsWithOwner::attrs_with_owner)]
fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner;

View File

@ -180,15 +180,15 @@ impl LangItems {
T: Into<AttrDefId> + Copy,
{
let _p = profile::span("collect_lang_item");
if let Some(lang_item) = lang_attr(db, item) {
if let Some(lang_item) = db.lang_attr(item.into()) {
self.items.entry(lang_item).or_insert_with(|| constructor(item));
}
}
}
pub fn lang_attr(db: &dyn DefDatabase, item: impl Into<AttrDefId> + Copy) -> Option<LangItem> {
let attrs = db.attrs(item.into());
attrs.by_key("lang").string_value().cloned().and_then(|it| LangItem::from_str(&it))
pub(crate) fn lang_attr_query(db: &dyn DefDatabase, item: AttrDefId) -> Option<LangItem> {
let attrs = db.attrs(item);
attrs.by_key("lang").string_value().and_then(|it| LangItem::from_str(&it))
}
pub enum GenericRequirement {

View File

@ -11,7 +11,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
use base_db::CrateId;
use hir_def::{
hir::Movability,
lang_item::{lang_attr, LangItem, LangItemTarget},
lang_item::{LangItem, LangItemTarget},
AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
};
use hir_expand::name::name;
@ -565,7 +565,7 @@ pub(crate) fn trait_datum_query(
let where_clauses = convert_where_clauses(db, trait_.into(), &bound_vars);
let associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect();
let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses };
let well_known = lang_attr(db.upcast(), trait_).and_then(well_known_trait_from_lang_item);
let well_known = db.lang_attr(trait_.into()).and_then(well_known_trait_from_lang_item);
let trait_datum = TraitDatum {
id: trait_id,
binders: make_binders(db, &generic_params, trait_datum_bound),

View File

@ -228,7 +228,7 @@ pub(crate) fn const_eval_query(
}
GeneralConstId::InTypeConstId(c) => db.mir_body(c.into())?,
};
let c = interpret_mir(db, &body, false).0?;
let c = interpret_mir(db, body, false).0?;
Ok(c)
}
@ -241,7 +241,7 @@ pub(crate) fn const_eval_static_query(
Substitution::empty(Interner),
db.trait_environment_for_body(def.into()),
)?;
let c = interpret_mir(db, &body, false).0?;
let c = interpret_mir(db, body, false).0?;
Ok(c)
}
@ -268,7 +268,7 @@ pub(crate) fn const_eval_discriminant_variant(
Substitution::empty(Interner),
db.trait_environment_for_body(def),
)?;
let c = interpret_mir(db, &mir_body, false).0?;
let c = interpret_mir(db, mir_body, false).0?;
let c = try_const_usize(db, &c).unwrap() as i128;
Ok(c)
}
@ -293,7 +293,7 @@ pub(crate) fn eval_to_const(
}
let infer = ctx.clone().resolve_all();
if let Ok(mir_body) = lower_to_mir(ctx.db, ctx.owner, &ctx.body, &infer, expr) {
if let Ok(result) = interpret_mir(db, &mir_body, true).0 {
if let Ok(result) = interpret_mir(db, Arc::new(mir_body), true).0 {
return result;
}
}

View File

@ -16,7 +16,7 @@ mod intrinsics;
fn simplify(e: ConstEvalError) -> ConstEvalError {
match e {
ConstEvalError::MirEvalError(MirEvalError::InFunction(_, e, _, _)) => {
ConstEvalError::MirEvalError(MirEvalError::InFunction(e, _)) => {
simplify(ConstEvalError::MirEvalError(*e))
}
_ => e,
@ -2471,7 +2471,7 @@ fn exec_limits() {
}
const GOAL: i32 = f(0);
"#,
|e| e == ConstEvalError::MirEvalError(MirEvalError::StackOverflow),
|e| e == ConstEvalError::MirEvalError(MirEvalError::ExecutionLimitExceeded),
);
// Reasonable code should still work
check_number(

View File

@ -110,6 +110,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::layout::target_data_layout_query)]
fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>;
#[salsa::invoke(crate::method_resolution::lookup_impl_method_query)]
fn lookup_impl_method(
&self,
env: Arc<crate::TraitEnvironment>,
func: FunctionId,
fn_subst: Substitution,
) -> (FunctionId, Substitution);
#[salsa::invoke(crate::lower::callable_item_sig)]
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;

View File

@ -23,7 +23,7 @@ use hir_def::{
generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
},
lang_item::{lang_attr, LangItem},
lang_item::LangItem,
nameres::MacroSubNs,
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, Resolver, TypeNs},
@ -1012,7 +1012,7 @@ impl<'a> TyLoweringContext<'a> {
// (So ideally, we'd only ignore `~const Drop` here)
// - `Destruct` impls are built-in in 1.62 (current nightly as of 08-04-2022), so until
// the builtin impls are supported by Chalk, we ignore them here.
if let Some(lang) = lang_attr(self.db.upcast(), tr.hir_trait_id()) {
if let Some(lang) = self.db.lang_attr(tr.hir_trait_id().into()) {
if matches!(lang, LangItem::Drop | LangItem::Destruct) {
return false;
}

View File

@ -682,7 +682,7 @@ pub fn is_dyn_method(
/// Looks up the impl method that actually runs for the trait method `func`.
///
/// Returns `func` if it's not a method defined in a trait or the lookup failed.
pub fn lookup_impl_method(
pub(crate) fn lookup_impl_method_query(
db: &dyn HirDatabase,
env: Arc<TraitEnvironment>,
func: FunctionId,

View File

@ -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,7 +8,7 @@ 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,
@ -28,7 +28,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,
name, static_lifetime,
traits::FnTrait,
utils::{detect_variant_from_bytes, ClosureSubst},
@ -37,8 +37,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;
@ -114,11 +114,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>,
@ -129,6 +138,7 @@ pub struct Evaluator<'a> {
thread_local_storage: TlsData,
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,
@ -192,7 +202,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 })
@ -292,7 +302,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,
@ -310,8 +320,9 @@ 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;
for (func, span, def) in stack.iter().take(30).rev() {
match func {
Either::Left(func) => {
let function_name = db.function_data(*func);
@ -345,6 +356,7 @@ impl MirEvalError {
let text_range = span.value.text_range();
writeln!(f, "{}", span_formatter(file_id, text_range))?;
}
}
match err {
MirEvalError::InFunction(..) => unreachable!(),
MirEvalError::LayoutError(err, ty) => {
@ -423,13 +435,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()
}
}
@ -459,15 +465,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
@ -476,16 +482,16 @@ 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, 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, None.into_iter())?;
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));
@ -508,6 +514,7 @@ impl Evaluator<'_> {
Evaluator {
stack: vec![0],
heap: vec![0],
code_stack: vec![],
vtable_map: VTableMap::default(),
thread_local_storage: TlsData::default(),
static_locations: HashMap::default(),
@ -519,14 +526,15 @@ impl Evaluator<'_> {
assert_placeholder_ty_is_unused,
stack_depth_limit: 100,
execution_limit: 1000_000,
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,
@ -548,7 +556,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();
@ -675,9 +683,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>> {
@ -686,11 +700,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(),
@ -701,11 +715,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)?,
@ -714,8 +724,8 @@ 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(it) = self.stack_depth_limit.checked_sub(1) {
self.stack_depth_limit = it;
@ -723,44 +733,18 @@ impl Evaluator<'_> {
return Err(MirEvalError::StackOverflow);
}
let mut current_block_idx = body.start_block;
let mut locals =
Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() };
let (locals_ptr, stack_size) = {
let mut stack_ptr = self.stack.len();
let addr = body
.locals
.iter()
.map(|(id, it)| {
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 }))
})
.collect::<Result<ArenaMap<LocalId, _>>>()?;
let stack_size = stack_ptr - self.stack.len();
(addr, stack_size)
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");
};
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"));
}
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) {
@ -803,7 +787,7 @@ impl Evaluator<'_> {
.iter()
.map(|it| self.operand_ty_and_eval(it, &mut locals))
.collect::<Result<Vec<_>>>()?;
match &fn_ty.data(Interner).kind {
let stack_frame = match &fn_ty.data(Interner).kind {
TyKind::Function(_) => {
let bytes = self.eval_operand(func, &mut locals)?;
self.exec_fn_pointer(
@ -811,23 +795,33 @@ impl Evaluator<'_> {
destination_interval,
&args,
&locals,
*target,
terminator.span,
)?;
)?
}
TyKind::FnDef(def, generic_args) => {
self.exec_fn_def(
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());
current_block_idx = target.expect("broken mir, function without target");
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(
@ -837,11 +831,12 @@ impl Evaluator<'_> {
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());
break;
}
TerminatorKind::Unreachable => {
return Err(MirEvalError::UndefinedBehavior("unreachable executed".to_owned()));
return Err(MirEvalError::UndefinedBehavior(
"unreachable executed".to_owned(),
));
}
TerminatorKind::Drop { place, target, unwind: _ } => {
self.drop_place(place, &mut locals, terminator.span)?;
@ -850,9 +845,103 @@ impl Evaluator<'_> {
_ => 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 eval_rvalue(&mut self, r: &Rvalue, locals: &mut Locals<'_>) -> Result<IntervalOrOwned> {
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.clone(), drop_flags: DropFlags::default() };
let (locals_ptr, stack_size) = {
let mut stack_ptr = self.stack.len();
let addr = body
.locals
.iter()
.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 }))
})
.collect::<Result<ArenaMap<LocalId, _>>>()?;
let stack_size = stack_ptr - self.stack.len();
(addr, stack_size)
};
locals.ptr = locals_ptr;
let prev_stack_pointer = self.stack.len();
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> {
use IntervalOrOwned::*;
Ok(match r {
Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?),
@ -1372,7 +1461,7 @@ impl Evaluator<'_> {
&mut self,
it: VariantId,
subst: Substitution,
locals: &Locals<'_>,
locals: &Locals,
) -> Result<(usize, Arc<Layout>, Option<(usize, usize, i128)>)> {
let adt = it.adt_id();
if let DefWithBodyId::VariantId(f) = locals.body.owner {
@ -1452,7 +1541,7 @@ impl Evaluator<'_> {
Ok(result)
}
fn eval_operand(&mut self, it: &Operand, locals: &mut Locals<'_>) -> Result<Interval> {
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);
@ -1482,7 +1571,7 @@ impl Evaluator<'_> {
&mut self,
c: &chalk_ir::ConcreteConst<Interner>,
ty: &Ty,
locals: &Locals<'_>,
locals: &Locals,
konst: &chalk_ir::Const<Interner>,
) -> Result<Interval> {
Ok(match &c.interned {
@ -1516,7 +1605,7 @@ impl Evaluator<'_> {
})
}
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,
@ -1562,7 +1651,7 @@ impl Evaluator<'_> {
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 {
@ -1586,7 +1675,7 @@ 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(it) => Ok(it.0),
None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)),
@ -1598,7 +1687,7 @@ impl Evaluator<'_> {
fn size_align_of_sized(
&self,
ty: &Ty,
locals: &Locals<'_>,
locals: &Locals,
what: &'static str,
) -> Result<(usize, usize)> {
match self.size_align_of(ty, locals)? {
@ -1621,7 +1710,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),
@ -1630,12 +1719,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) {
@ -1741,7 +1830,7 @@ impl Evaluator<'_> {
old_vtable: &VTableMap,
addr: Address,
ty: &Ty,
locals: &Locals<'_>,
locals: &Locals,
) -> Result<()> {
// FIXME: support indirect references
let layout = self.layout(ty)?;
@ -1815,21 +1904,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(
@ -1839,9 +1928,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(
@ -1859,10 +1948,16 @@ impl Evaluator<'_> {
let arg_bytes = iter::once(Ok(closure_data))
.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)
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)
destination.write_from_bytes(self, &bytes)?;
Ok(None)
}
fn exec_fn_def(
@ -1871,18 +1966,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(def, args, generic_args, locals, destination, 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) =
@ -1894,6 +2005,7 @@ impl Evaluator<'_> {
args.iter().map(|it| it.interval.into()),
)?;
destination.write_from_bytes(self, &result)?;
Ok(None)
}
CallableDefId::EnumVariantId(id) => {
let (size, variant_layout, tag) =
@ -1905,9 +2017,9 @@ impl Evaluator<'_> {
args.iter().map(|it| it.interval.into()),
)?;
destination.write_from_bytes(self, &result)?;
Ok(None)
}
}
Ok(())
}
fn exec_fn_with_args(
@ -1915,10 +2027,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,
@ -1927,10 +2040,9 @@ impl Evaluator<'_> {
destination,
span,
)? {
return Ok(());
return Ok(None);
}
let arg_bytes =
args.iter().map(|it| Ok(it.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())
{
@ -1938,8 +2050,10 @@ 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()),
@ -1962,40 +2076,65 @@ impl Evaluator<'_> {
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)
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)?;
Ok(())
None
})
}
fn exec_fn_trait(
@ -2003,10 +2142,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>> {
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;
@ -2023,13 +2163,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()),
@ -2037,7 +2192,7 @@ impl Evaluator<'_> {
&args[1..],
locals,
span,
)?;
);
}
_ => {
// try to execute the manual impl of `FnTrait` for structs (nightly feature used in std)
@ -2068,14 +2223,14 @@ impl Evaluator<'_> {
generic_args,
locals,
destination,
target_bb,
span,
);
}
}
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);
};
@ -2123,7 +2278,7 @@ 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(());
@ -2138,7 +2293,7 @@ impl Evaluator<'_> {
fn run_drop_glue_deep(
&mut self,
ty: Ty,
locals: &Locals<'_>,
locals: &Locals,
addr: Address,
_metadata: &[u8],
span: MirSpan,
@ -2151,8 +2306,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()),
@ -2162,9 +2316,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) {

View File

@ -32,7 +32,7 @@ impl Evaluator<'_> {
def: FunctionId,
args: &[IntervalAndTy],
generic_args: &Substitution,
locals: &Locals<'_>,
locals: &Locals,
destination: Interval,
span: MirSpan,
) -> Result<bool> {
@ -168,7 +168,7 @@ impl Evaluator<'_> {
fn detect_lang_function(&self, def: FunctionId) -> Option<LangItem> {
use LangItem::*;
let candidate = lang_attr(self.db.upcast(), def)?;
let candidate = self.db.lang_attr(def.into())?;
// We want to execute these functions with special logic
if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
return Some(candidate);
@ -181,7 +181,7 @@ impl Evaluator<'_> {
it: LangItem,
generic_args: &Substitution,
args: &[Vec<u8>],
locals: &Locals<'_>,
locals: &Locals,
span: MirSpan,
) -> Result<Vec<u8>> {
use LangItem::*;
@ -201,7 +201,7 @@ impl Evaluator<'_> {
not_supported!("std::fmt::format not found");
};
let hir_def::resolver::ValueNs::FunctionId(format_fn) = format_fn else { not_supported!("std::fmt::format is not a function") };
let message_string = self.interpret_mir(&*self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.cloned())?;
let message_string = self.interpret_mir(self.db.mir_body(format_fn.into()).map_err(|e| MirEvalError::MirLowerError(format_fn, e))?, args.map(|x| IntervalOrOwned::Owned(x.clone())))?;
let addr = Address::from_bytes(&message_string[self.ptr_size()..2 * self.ptr_size()])?;
let size = from_bytes!(usize, message_string[2 * self.ptr_size()..]);
Ok(std::string::String::from_utf8_lossy(self.read_memory(addr, size)?).into_owned())
@ -245,7 +245,7 @@ impl Evaluator<'_> {
args: &[IntervalAndTy],
_generic_args: &Substitution,
destination: Interval,
locals: &Locals<'_>,
locals: &Locals,
_span: MirSpan,
) -> Result<()> {
match as_str {
@ -353,7 +353,7 @@ impl Evaluator<'_> {
args: &[IntervalAndTy],
generic_args: &Substitution,
destination: Interval,
locals: &Locals<'_>,
locals: &Locals,
span: MirSpan,
) -> Result<()> {
if let Some(name) = name.strip_prefix("simd_") {
@ -368,7 +368,7 @@ impl Evaluator<'_> {
args: &[IntervalAndTy],
generic_args: &Substitution,
destination: Interval,
locals: &Locals<'_>,
locals: &Locals,
span: MirSpan,
) -> Result<()> {
if let Some(name) = name.strip_prefix("atomic_") {
@ -873,15 +873,17 @@ impl Evaluator<'_> {
.as_trait()
.and_then(|it| self.db.trait_data(it).method_by_name(&name![call_once]))
{
return self.exec_fn_trait(
self.exec_fn_trait(
def,
&args,
// FIXME: wrong for manual impls of `FnOnce`
Substitution::empty(Interner),
locals,
destination,
None,
span,
);
)?;
return Ok(());
}
}
not_supported!("FnOnce was not available for executing const_eval_select");
@ -894,7 +896,7 @@ impl Evaluator<'_> {
&mut self,
ty: &Ty,
metadata: Interval,
locals: &Locals<'_>,
locals: &Locals,
) -> Result<(usize, usize)> {
Ok(match ty.kind(Interner) {
TyKind::Str => (from_bytes!(usize, metadata.get(self)?), 1),
@ -948,7 +950,7 @@ impl Evaluator<'_> {
args: &[IntervalAndTy],
generic_args: &Substitution,
destination: Interval,
locals: &Locals<'_>,
locals: &Locals,
_span: MirSpan,
) -> Result<()> {
// We are a single threaded runtime with no UB checking and no optimization, so

View File

@ -50,7 +50,7 @@ impl Evaluator<'_> {
args: &[IntervalAndTy],
_generic_args: &Substitution,
destination: Interval,
_locals: &Locals<'_>,
_locals: &Locals,
_span: MirSpan,
) -> Result<()> {
match name {

View File

@ -30,7 +30,7 @@ fn eval_main(db: &TestDB, file_id: FileId) -> Result<(String, String), MirEvalEr
db.trait_environment(func_id.into()),
)
.map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?;
let (result, stdout, stderr) = interpret_mir(db, &body, false);
let (result, stdout, stderr) = interpret_mir(db, body, false);
result?;
Ok((stdout, stderr))
}

View File

@ -1,7 +1,7 @@
//! MIR lowering for places
use super::*;
use hir_def::{lang_item::lang_attr, FunctionId};
use hir_def::FunctionId;
use hir_expand::name;
macro_rules! not_supported {
@ -162,7 +162,7 @@ impl MirLowerCtx<'_> {
let is_builtin = match self.expr_ty_without_adjust(*expr).kind(Interner) {
TyKind::Ref(..) | TyKind::Raw(..) => true,
TyKind::Adt(id, _) => {
if let Some(lang_item) = lang_attr(self.db.upcast(), id.0) {
if let Some(lang_item) = self.db.lang_attr(id.0.into()) {
lang_item == LangItem::OwnedBox
} else {
false

View File

@ -1986,7 +1986,7 @@ impl Function {
return r;
}
};
let (result, stdout, stderr) = interpret_mir(db, &body, false);
let (result, stdout, stderr) = interpret_mir(db, body, false);
let mut text = match result {
Ok(_) => "pass".to_string(),
Err(e) => {

View File

@ -832,7 +832,7 @@ impl SourceAnalyzer {
None => return func,
};
let env = db.trait_environment_for_body(owner);
method_resolution::lookup_impl_method(db, env, func, substs).0
db.lookup_impl_method(env, func, substs).0
}
fn resolve_impl_const_or_trait_def(