Auto merge of #15228 - HKalbasi:mir, r=HKalbasi

Implement recursion in mir interpreter without recursion

This enables interpreting functions with deep stack + profiling. I also applied some changes to make it faster based on the profiling result.
This commit is contained in:
bors 2023-07-07 11:40:47 +00:00
commit c5ca8165e1
15 changed files with 432 additions and 264 deletions

View File

@ -18,7 +18,7 @@ use crate::{
generics::GenericParams, generics::GenericParams,
import_map::ImportMap, import_map::ImportMap,
item_tree::{AttrOwner, ItemTree}, item_tree::{AttrOwner, ItemTree},
lang_item::{LangItem, LangItemTarget, LangItems}, lang_item::{self, LangItem, LangItemTarget, LangItems},
nameres::{diagnostics::DefDiagnostic, DefMap}, nameres::{diagnostics::DefDiagnostic, DefMap},
visibility::{self, Visibility}, visibility::{self, Visibility},
AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId, 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)] #[salsa::invoke(AttrsWithOwner::attrs_query)]
fn attrs(&self, def: AttrDefId) -> Attrs; fn attrs(&self, def: AttrDefId) -> Attrs;
#[salsa::invoke(lang_item::lang_attr_query)]
fn lang_attr(&self, def: AttrDefId) -> Option<LangItem>;
#[salsa::transparent] #[salsa::transparent]
#[salsa::invoke(AttrsWithOwner::attrs_with_owner)] #[salsa::invoke(AttrsWithOwner::attrs_with_owner)]
fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner; fn attrs_with_owner(&self, def: AttrDefId) -> AttrsWithOwner;

View File

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

View File

@ -11,7 +11,7 @@ use chalk_solve::rust_ir::{self, OpaqueTyDatumBound, WellKnownTrait};
use base_db::CrateId; use base_db::CrateId;
use hir_def::{ use hir_def::{
hir::Movability, hir::Movability,
lang_item::{lang_attr, LangItem, LangItemTarget}, lang_item::{LangItem, LangItemTarget},
AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId, AssocItemId, BlockId, GenericDefId, HasModule, ItemContainerId, Lookup, TypeAliasId,
}; };
use hir_expand::name::name; 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 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 associated_ty_ids = trait_data.associated_types().map(to_assoc_type_id).collect();
let trait_datum_bound = rust_ir::TraitDatumBound { where_clauses }; 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 { let trait_datum = TraitDatum {
id: trait_id, id: trait_id,
binders: make_binders(db, &generic_params, trait_datum_bound), 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())?, 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) Ok(c)
} }
@ -241,7 +241,7 @@ pub(crate) fn const_eval_static_query(
Substitution::empty(Interner), Substitution::empty(Interner),
db.trait_environment_for_body(def.into()), 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) Ok(c)
} }
@ -268,7 +268,7 @@ pub(crate) fn const_eval_discriminant_variant(
Substitution::empty(Interner), Substitution::empty(Interner),
db.trait_environment_for_body(def), 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; let c = try_const_usize(db, &c).unwrap() as i128;
Ok(c) Ok(c)
} }
@ -293,7 +293,7 @@ pub(crate) fn eval_to_const(
} }
let infer = ctx.clone().resolve_all(); 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(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; return result;
} }
} }

View File

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

View File

@ -110,6 +110,14 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
#[salsa::invoke(crate::layout::target_data_layout_query)] #[salsa::invoke(crate::layout::target_data_layout_query)]
fn target_data_layout(&self, krate: CrateId) -> Option<Arc<TargetDataLayout>>; 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)] #[salsa::invoke(crate::lower::callable_item_sig)]
fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig; fn callable_item_signature(&self, def: CallableDefId) -> PolyFnSig;

View File

@ -23,7 +23,7 @@ use hir_def::{
generics::{ generics::{
TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget, TypeOrConstParamData, TypeParamProvenance, WherePredicate, WherePredicateTypeTarget,
}, },
lang_item::{lang_attr, LangItem}, lang_item::LangItem,
nameres::MacroSubNs, nameres::MacroSubNs,
path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments}, path::{GenericArg, GenericArgs, ModPath, Path, PathKind, PathSegment, PathSegments},
resolver::{HasResolver, Resolver, TypeNs}, resolver::{HasResolver, Resolver, TypeNs},
@ -1012,7 +1012,7 @@ impl<'a> TyLoweringContext<'a> {
// (So ideally, we'd only ignore `~const Drop` here) // (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 // - `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. // 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) { if matches!(lang, LangItem::Drop | LangItem::Destruct) {
return false; 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`. /// 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. /// 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, db: &dyn HirDatabase,
env: Arc<TraitEnvironment>, env: Arc<TraitEnvironment>,
func: FunctionId, func: FunctionId,

View File

@ -1,6 +1,6 @@
//! This module provides a MIR interpreter, which is used in const eval. //! 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 base_db::{CrateId, FileId};
use chalk_ir::Mutability; use chalk_ir::Mutability;
@ -8,7 +8,7 @@ use either::Either;
use hir_def::{ use hir_def::{
builtin_type::BuiltinType, builtin_type::BuiltinType,
data::adt::{StructFlags, VariantData}, data::adt::{StructFlags, VariantData},
lang_item::{lang_attr, LangItem}, lang_item::LangItem,
layout::{TagEncoding, Variants}, layout::{TagEncoding, Variants},
AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId,
VariantId, VariantId,
@ -28,7 +28,7 @@ use crate::{
infer::PointerCast, infer::PointerCast,
layout::{Layout, LayoutError, RustcEnumVariantIdx}, layout::{Layout, LayoutError, RustcEnumVariantIdx},
mapping::from_chalk, mapping::from_chalk,
method_resolution::{is_dyn_method, lookup_impl_method}, method_resolution::is_dyn_method,
name, static_lifetime, name, static_lifetime,
traits::FnTrait, traits::FnTrait,
utils::{detect_variant_from_bytes, ClosureSubst}, utils::{detect_variant_from_bytes, ClosureSubst},
@ -37,8 +37,8 @@ use crate::{
}; };
use super::{ use super::{
return_slot, AggregateKind, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, Operand, return_slot, AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError,
Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp, MirSpan, Operand, Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, UnOp,
}; };
mod shim; 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> { pub struct Evaluator<'a> {
db: &'a dyn HirDatabase, db: &'a dyn HirDatabase,
trait_env: Arc<TraitEnvironment>, trait_env: Arc<TraitEnvironment>,
stack: Vec<u8>, stack: Vec<u8>,
heap: 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 /// 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. /// and see it's missing, then we add it to this to reuse.
static_locations: FxHashMap<StaticId, Address>, static_locations: FxHashMap<StaticId, Address>,
@ -129,6 +138,7 @@ pub struct Evaluator<'a> {
thread_local_storage: TlsData, thread_local_storage: TlsData,
stdout: Vec<u8>, stdout: Vec<u8>,
stderr: Vec<u8>, stderr: Vec<u8>,
layout_cache: RefCell<FxHashMap<Ty, Arc<Layout>>>,
crate_id: CrateId, crate_id: CrateId,
// FIXME: This is a workaround, see the comment on `interpret_mir` // FIXME: This is a workaround, see the comment on `interpret_mir`
assert_placeholder_ty_is_unused: bool, assert_placeholder_ty_is_unused: bool,
@ -192,7 +202,7 @@ impl IntervalAndTy {
addr: Address, addr: Address,
ty: Ty, ty: Ty,
evaluator: &Evaluator<'_>, evaluator: &Evaluator<'_>,
locals: &Locals<'_>, locals: &Locals,
) -> Result<IntervalAndTy> { ) -> Result<IntervalAndTy> {
let size = evaluator.size_of_sized(&ty, locals, "type of interval")?; let size = evaluator.size_of_sized(&ty, locals, "type of interval")?;
Ok(IntervalAndTy { interval: Interval { addr, size }, ty }) Ok(IntervalAndTy { interval: Interval { addr, size }, ty })
@ -292,7 +302,7 @@ pub enum MirEvalError {
TypeIsUnsized(Ty, &'static str), TypeIsUnsized(Ty, &'static str),
NotSupported(String), NotSupported(String),
InvalidConst(Const), InvalidConst(Const),
InFunction(Either<FunctionId, ClosureId>, Box<MirEvalError>, MirSpan, DefWithBodyId), InFunction(Box<MirEvalError>, Vec<(Either<FunctionId, ClosureId>, MirSpan, DefWithBodyId)>),
ExecutionLimitExceeded, ExecutionLimitExceeded,
StackOverflow, StackOverflow,
TargetDataLayoutNotAvailable, TargetDataLayoutNotAvailable,
@ -310,8 +320,9 @@ impl MirEvalError {
) -> std::result::Result<(), std::fmt::Error> { ) -> std::result::Result<(), std::fmt::Error> {
writeln!(f, "Mir eval error:")?; writeln!(f, "Mir eval error:")?;
let mut err = self; let mut err = self;
while let MirEvalError::InFunction(func, e, span, def) = err { while let MirEvalError::InFunction(e, stack) = err {
err = e; err = e;
for (func, span, def) in stack.iter().take(30).rev() {
match func { match func {
Either::Left(func) => { Either::Left(func) => {
let function_name = db.function_data(*func); let function_name = db.function_data(*func);
@ -345,6 +356,7 @@ impl MirEvalError {
let text_range = span.value.text_range(); let text_range = span.value.text_range();
writeln!(f, "{}", span_formatter(file_id, text_range))?; writeln!(f, "{}", span_formatter(file_id, text_range))?;
} }
}
match err { match err {
MirEvalError::InFunction(..) => unreachable!(), MirEvalError::InFunction(..) => unreachable!(),
MirEvalError::LayoutError(err, ty) => { MirEvalError::LayoutError(err, ty) => {
@ -423,13 +435,7 @@ impl std::fmt::Debug for MirEvalError {
let data = &arg0.data(Interner); let data = &arg0.data(Interner);
f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish() f.debug_struct("InvalidConst").field("ty", &data.ty).field("value", &arg0).finish()
} }
Self::InFunction(func, e, span, _) => { Self::InFunction(e, stack) => {
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));
}
f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish() f.debug_struct("WithStack").field("error", e).field("stack", &stack).finish()
} }
} }
@ -459,15 +465,15 @@ impl DropFlags {
} }
#[derive(Debug)] #[derive(Debug)]
struct Locals<'a> { struct Locals {
ptr: &'a ArenaMap<LocalId, Interval>, ptr: ArenaMap<LocalId, Interval>,
body: &'a MirBody, body: Arc<MirBody>,
drop_flags: DropFlags, drop_flags: DropFlags,
} }
pub fn interpret_mir( pub fn interpret_mir(
db: &dyn HirDatabase, db: &dyn HirDatabase,
body: &MirBody, body: Arc<MirBody>,
// FIXME: This is workaround. Ideally, const generics should have a separate body (issue #7434), but now // 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 // 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 // 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, assert_placeholder_ty_is_unused: bool,
) -> (Result<Const>, String, String) { ) -> (Result<Const>, String, String) {
let ty = body.locals[return_slot()].ty.clone(); 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> = (|| { let it: Result<Const> = (|| {
if evaluator.ptr_size() != std::mem::size_of::<usize>() { if evaluator.ptr_size() != std::mem::size_of::<usize>() {
not_supported!("targets with different pointer size from host"); 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( let mut memory_map = evaluator.create_memory_map(
&bytes, &bytes,
&ty, &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(); memory_map.vtable = evaluator.vtable_map.clone();
return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty)); return Ok(intern_const_scalar(ConstScalar::Bytes(bytes, memory_map), ty));
@ -508,6 +514,7 @@ impl Evaluator<'_> {
Evaluator { Evaluator {
stack: vec![0], stack: vec![0],
heap: vec![0], heap: vec![0],
code_stack: vec![],
vtable_map: VTableMap::default(), vtable_map: VTableMap::default(),
thread_local_storage: TlsData::default(), thread_local_storage: TlsData::default(),
static_locations: HashMap::default(), static_locations: HashMap::default(),
@ -519,14 +526,15 @@ impl Evaluator<'_> {
assert_placeholder_ty_is_unused, assert_placeholder_ty_is_unused,
stack_depth_limit: 100, stack_depth_limit: 100,
execution_limit: 1000_000, 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) 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)?; let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?;
Ok(Interval { Ok(Interval {
addr: place_addr_and_ty.0, addr: place_addr_and_ty.0,
@ -548,7 +556,7 @@ impl Evaluator<'_> {
fn place_addr_and_ty_and_metadata<'a>( fn place_addr_and_ty_and_metadata<'a>(
&'a self, &'a self,
p: &Place, p: &Place,
locals: &'a Locals<'a>, locals: &'a Locals,
) -> Result<(Address, Ty, Option<IntervalOrOwned>)> { ) -> Result<(Address, Ty, Option<IntervalOrOwned>)> {
let mut addr = locals.ptr[p.local].addr; let mut addr = locals.ptr[p.local].addr;
let mut ty: Ty = locals.body.locals[p.local].ty.clone(); 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>> { 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) .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>> { 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) 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 { Ok(match o {
Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?, Operand::Copy(p) | Operand::Move(p) => self.place_ty(p, locals)?,
Operand::Constant(c) => c.data(Interner).ty.clone(), Operand::Constant(c) => c.data(Interner).ty.clone(),
@ -701,11 +715,7 @@ impl Evaluator<'_> {
}) })
} }
fn operand_ty_and_eval( fn operand_ty_and_eval(&mut self, o: &Operand, locals: &mut Locals) -> Result<IntervalAndTy> {
&mut self,
o: &Operand,
locals: &mut Locals<'_>,
) -> Result<IntervalAndTy> {
Ok(IntervalAndTy { Ok(IntervalAndTy {
interval: self.eval_operand(o, locals)?, interval: self.eval_operand(o, locals)?,
ty: self.operand_ty(o, locals)?, ty: self.operand_ty(o, locals)?,
@ -714,8 +724,8 @@ impl Evaluator<'_> {
fn interpret_mir( fn interpret_mir(
&mut self, &mut self,
body: &MirBody, body: Arc<MirBody>,
args: impl Iterator<Item = Vec<u8>>, args: impl Iterator<Item = IntervalOrOwned>,
) -> Result<Vec<u8>> { ) -> Result<Vec<u8>> {
if let Some(it) = self.stack_depth_limit.checked_sub(1) { if let Some(it) = self.stack_depth_limit.checked_sub(1) {
self.stack_depth_limit = it; self.stack_depth_limit = it;
@ -723,44 +733,18 @@ impl Evaluator<'_> {
return Err(MirEvalError::StackOverflow); return Err(MirEvalError::StackOverflow);
} }
let mut current_block_idx = body.start_block; let mut current_block_idx = body.start_block;
let mut locals = let (mut locals, prev_stack_ptr) = self.create_locals_for_body(body.clone(), None)?;
Locals { ptr: &ArenaMap::new(), body: &body, drop_flags: DropFlags::default() }; self.fill_locals_for_body(&body, &mut locals, args)?;
let (locals_ptr, stack_size) = { let prev_code_stack = mem::take(&mut self.code_stack);
let mut stack_ptr = self.stack.len(); let span = (MirSpan::Unknown, body.owner);
let addr = body self.code_stack.push(StackFrame { body, locals, destination: None, prev_stack_ptr, span });
.locals 'stack: loop {
.iter() let Some(mut my_stack_frame) = self.code_stack.pop() else {
.map(|(id, it)| { not_supported!("missing stack frame");
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 e = (|| {
self.stack.extend(iter::repeat(0).take(stack_size)); let mut locals = &mut my_stack_frame.locals;
let mut remain_args = body.param_locals.len(); let body = &*my_stack_frame.body;
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 { loop {
let current_block = &body.basic_blocks[current_block_idx]; let current_block = &body.basic_blocks[current_block_idx];
if let Some(it) = self.execution_limit.checked_sub(1) { if let Some(it) = self.execution_limit.checked_sub(1) {
@ -803,7 +787,7 @@ impl Evaluator<'_> {
.iter() .iter()
.map(|it| self.operand_ty_and_eval(it, &mut locals)) .map(|it| self.operand_ty_and_eval(it, &mut locals))
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
match &fn_ty.data(Interner).kind { let stack_frame = match &fn_ty.data(Interner).kind {
TyKind::Function(_) => { TyKind::Function(_) => {
let bytes = self.eval_operand(func, &mut locals)?; let bytes = self.eval_operand(func, &mut locals)?;
self.exec_fn_pointer( self.exec_fn_pointer(
@ -811,23 +795,33 @@ impl Evaluator<'_> {
destination_interval, destination_interval,
&args, &args,
&locals, &locals,
*target,
terminator.span, terminator.span,
)?; )?
} }
TyKind::FnDef(def, generic_args) => { TyKind::FnDef(def, generic_args) => self.exec_fn_def(
self.exec_fn_def(
*def, *def,
generic_args, generic_args,
destination_interval, destination_interval,
&args, &args,
&locals, &locals,
*target,
terminator.span, terminator.span,
)?; )?,
}
it => not_supported!("unknown function type {it:?}"), it => not_supported!("unknown function type {it:?}"),
} };
locals.drop_flags.add_place(destination.clone()); 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 } => { TerminatorKind::SwitchInt { discr, targets } => {
let val = u128::from_le_bytes(pad16( let val = u128::from_le_bytes(pad16(
@ -837,11 +831,12 @@ impl Evaluator<'_> {
current_block_idx = targets.target_for_value(val); current_block_idx = targets.target_for_value(val);
} }
TerminatorKind::Return => { TerminatorKind::Return => {
self.stack_depth_limit += 1; break;
return Ok(locals.ptr[return_slot()].get(self)?.to_vec());
} }
TerminatorKind::Unreachable => { TerminatorKind::Unreachable => {
return Err(MirEvalError::UndefinedBehavior("unreachable executed".to_owned())); return Err(MirEvalError::UndefinedBehavior(
"unreachable executed".to_owned(),
));
} }
TerminatorKind::Drop { place, target, unwind: _ } => { TerminatorKind::Drop { place, target, unwind: _ } => {
self.drop_place(place, &mut locals, terminator.span)?; self.drop_place(place, &mut locals, terminator.span)?;
@ -850,9 +845,103 @@ impl Evaluator<'_> {
_ => not_supported!("unknown terminator"), _ => 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::*; use IntervalOrOwned::*;
Ok(match r { Ok(match r {
Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?), Rvalue::Use(it) => Borrowed(self.eval_operand(it, locals)?),
@ -1372,7 +1461,7 @@ impl Evaluator<'_> {
&mut self, &mut self,
it: VariantId, it: VariantId,
subst: Substitution, subst: Substitution,
locals: &Locals<'_>, locals: &Locals,
) -> Result<(usize, Arc<Layout>, Option<(usize, usize, i128)>)> { ) -> Result<(usize, Arc<Layout>, Option<(usize, usize, i128)>)> {
let adt = it.adt_id(); let adt = it.adt_id();
if let DefWithBodyId::VariantId(f) = locals.body.owner { if let DefWithBodyId::VariantId(f) = locals.body.owner {
@ -1452,7 +1541,7 @@ impl Evaluator<'_> {
Ok(result) 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 { Ok(match it {
Operand::Copy(p) | Operand::Move(p) => { Operand::Copy(p) | Operand::Move(p) => {
locals.drop_flags.remove_place(p); locals.drop_flags.remove_place(p);
@ -1482,7 +1571,7 @@ impl Evaluator<'_> {
&mut self, &mut self,
c: &chalk_ir::ConcreteConst<Interner>, c: &chalk_ir::ConcreteConst<Interner>,
ty: &Ty, ty: &Ty,
locals: &Locals<'_>, locals: &Locals,
konst: &chalk_ir::Const<Interner>, konst: &chalk_ir::Const<Interner>,
) -> Result<Interval> { ) -> Result<Interval> {
Ok(match &c.interned { 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)?; let addr = self.place_addr(p, locals)?;
Ok(Interval::new( Ok(Interval::new(
addr, addr,
@ -1562,7 +1651,7 @@ impl Evaluator<'_> {
Ok(()) 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 DefWithBodyId::VariantId(f) = locals.body.owner {
if let Some((adt, _)) = ty.as_adt() { if let Some((adt, _)) = ty.as_adt() {
if AdtId::from(f.parent) == 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 /// 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` /// 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)? { match self.size_align_of(ty, locals)? {
Some(it) => Ok(it.0), Some(it) => Ok(it.0),
None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)), None => Err(MirEvalError::TypeIsUnsized(ty.clone(), what)),
@ -1598,7 +1687,7 @@ impl Evaluator<'_> {
fn size_align_of_sized( fn size_align_of_sized(
&self, &self,
ty: &Ty, ty: &Ty,
locals: &Locals<'_>, locals: &Locals,
what: &'static str, what: &'static str,
) -> Result<(usize, usize)> { ) -> Result<(usize, usize)> {
match self.size_align_of(ty, locals)? { 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 { let ItemContainerId::TraitId(parent) = self.db.lookup_intern_function(def).container else {
return None; return None;
}; };
let l = lang_attr(self.db.upcast(), parent)?; let l = self.db.lang_attr(parent.into())?;
match l { match l {
FnOnce => Some(FnTrait::FnOnce), FnOnce => Some(FnTrait::FnOnce),
FnMut => Some(FnTrait::FnMut), 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( fn rec(
this: &Evaluator<'_>, this: &Evaluator<'_>,
bytes: &[u8], bytes: &[u8],
ty: &Ty, ty: &Ty,
locals: &Locals<'_>, locals: &Locals,
mm: &mut MemoryMap, mm: &mut MemoryMap,
) -> Result<()> { ) -> Result<()> {
match ty.kind(Interner) { match ty.kind(Interner) {
@ -1741,7 +1830,7 @@ impl Evaluator<'_> {
old_vtable: &VTableMap, old_vtable: &VTableMap,
addr: Address, addr: Address,
ty: &Ty, ty: &Ty,
locals: &Locals<'_>, locals: &Locals,
) -> Result<()> { ) -> Result<()> {
// FIXME: support indirect references // FIXME: support indirect references
let layout = self.layout(ty)?; let layout = self.layout(ty)?;
@ -1815,21 +1904,21 @@ impl Evaluator<'_> {
bytes: Interval, bytes: Interval,
destination: Interval, destination: Interval,
args: &[IntervalAndTy], args: &[IntervalAndTy],
locals: &Locals<'_>, locals: &Locals,
target_bb: Option<BasicBlockId>,
span: MirSpan, span: MirSpan,
) -> Result<()> { ) -> Result<Option<StackFrame>> {
let id = from_bytes!(usize, bytes.get(self)?); let id = from_bytes!(usize, bytes.get(self)?);
let next_ty = self.vtable_map.ty(id)?.clone(); let next_ty = self.vtable_map.ty(id)?.clone();
match &next_ty.data(Interner).kind { match &next_ty.data(Interner).kind {
TyKind::FnDef(def, generic_args) => { 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) => { 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( fn exec_closure(
@ -1839,9 +1928,9 @@ impl Evaluator<'_> {
generic_args: &Substitution, generic_args: &Substitution,
destination: Interval, destination: Interval,
args: &[IntervalAndTy], args: &[IntervalAndTy],
locals: &Locals<'_>, locals: &Locals,
span: MirSpan, span: MirSpan,
) -> Result<()> { ) -> Result<Option<StackFrame>> {
let mir_body = self let mir_body = self
.db .db
.monomorphized_mir_body_for_closure( .monomorphized_mir_body_for_closure(
@ -1859,10 +1948,16 @@ impl Evaluator<'_> {
let arg_bytes = iter::once(Ok(closure_data)) let arg_bytes = iter::once(Ok(closure_data))
.chain(args.iter().map(|it| Ok(it.get(&self)?.to_owned()))) .chain(args.iter().map(|it| Ok(it.get(&self)?.to_owned())))
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
let bytes = self.interpret_mir(&mir_body, arg_bytes.into_iter()).map_err(|e| { let bytes = self
MirEvalError::InFunction(Either::Right(closure), Box::new(e), span, locals.body.owner) .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( fn exec_fn_def(
@ -1871,18 +1966,34 @@ impl Evaluator<'_> {
generic_args: &Substitution, generic_args: &Substitution,
destination: Interval, destination: Interval,
args: &[IntervalAndTy], args: &[IntervalAndTy],
locals: &Locals<'_>, locals: &Locals,
target_bb: Option<BasicBlockId>,
span: MirSpan, span: MirSpan,
) -> Result<()> { ) -> Result<Option<StackFrame>> {
let def: CallableDefId = from_chalk(self.db, def); let def: CallableDefId = from_chalk(self.db, def);
let generic_args = generic_args.clone(); let generic_args = generic_args.clone();
match def { match def {
CallableDefId::FunctionId(def) => { CallableDefId::FunctionId(def) => {
if let Some(_) = self.detect_fn_trait(def) { if let Some(_) = self.detect_fn_trait(def) {
self.exec_fn_trait(def, args, generic_args, locals, destination, span)?; return self.exec_fn_trait(
return Ok(()); 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) => { CallableDefId::StructId(id) => {
let (size, variant_layout, tag) = let (size, variant_layout, tag) =
@ -1894,6 +2005,7 @@ impl Evaluator<'_> {
args.iter().map(|it| it.interval.into()), args.iter().map(|it| it.interval.into()),
)?; )?;
destination.write_from_bytes(self, &result)?; destination.write_from_bytes(self, &result)?;
Ok(None)
} }
CallableDefId::EnumVariantId(id) => { CallableDefId::EnumVariantId(id) => {
let (size, variant_layout, tag) = let (size, variant_layout, tag) =
@ -1905,9 +2017,9 @@ impl Evaluator<'_> {
args.iter().map(|it| it.interval.into()), args.iter().map(|it| it.interval.into()),
)?; )?;
destination.write_from_bytes(self, &result)?; destination.write_from_bytes(self, &result)?;
Ok(None)
} }
} }
Ok(())
} }
fn exec_fn_with_args( fn exec_fn_with_args(
@ -1915,10 +2027,11 @@ impl Evaluator<'_> {
def: FunctionId, def: FunctionId,
args: &[IntervalAndTy], args: &[IntervalAndTy],
generic_args: Substitution, generic_args: Substitution,
locals: &Locals<'_>, locals: &Locals,
destination: Interval, destination: Interval,
target_bb: Option<BasicBlockId>,
span: MirSpan, span: MirSpan,
) -> Result<()> { ) -> Result<Option<StackFrame>> {
if self.detect_and_exec_special_function( if self.detect_and_exec_special_function(
def, def,
args, args,
@ -1927,10 +2040,9 @@ impl Evaluator<'_> {
destination, destination,
span, span,
)? { )? {
return Ok(()); return Ok(None);
} }
let arg_bytes = let arg_bytes = args.iter().map(|it| IntervalOrOwned::Borrowed(it.interval));
args.iter().map(|it| Ok(it.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
if let Some(self_ty_idx) = if let Some(self_ty_idx) =
is_dyn_method(self.db, self.trait_env.clone(), def, generic_args.clone()) 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, // `&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 vtable is exactly in the `[ptr_size..2*ptr_size]` bytes. So we can use it without branching on
// the type. // the type.
let first_arg = arg_bytes.clone().next().unwrap();
let first_arg = first_arg.get(self)?;
let ty = 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(); let mut args_for_target = args.to_vec();
args_for_target[0] = IntervalAndTy { args_for_target[0] = IntervalAndTy {
interval: args_for_target[0].interval.slice(0..self.ptr_size()), interval: args_for_target[0].interval.slice(0..self.ptr_size()),
@ -1962,40 +2076,65 @@ impl Evaluator<'_> {
generics_for_target, generics_for_target,
locals, locals,
destination, destination,
target_bb,
span, span,
); );
} }
let (imp, generic_args) = let (imp, generic_args) =
lookup_impl_method(self.db, self.trait_env.clone(), def, generic_args); 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) self.exec_looked_up_function(
generic_args,
locals,
imp,
arg_bytes,
span,
destination,
target_bb,
)
} }
fn exec_looked_up_function( fn exec_looked_up_function(
&mut self, &mut self,
generic_args: Substitution, generic_args: Substitution,
locals: &Locals<'_>, locals: &Locals,
imp: FunctionId, imp: FunctionId,
arg_bytes: Vec<Vec<u8>>, arg_bytes: impl Iterator<Item = IntervalOrOwned>,
span: MirSpan, span: MirSpan,
destination: Interval, destination: Interval,
) -> Result<()> { target_bb: Option<BasicBlockId>,
) -> Result<Option<StackFrame>> {
let def = imp.into(); let def = imp.into();
let mir_body = self let mir_body = self
.db .db
.monomorphized_mir_body(def, generic_args, self.trait_env.clone()) .monomorphized_mir_body(def, generic_args, self.trait_env.clone())
.map_err(|e| { .map_err(|e| {
MirEvalError::InFunction( MirEvalError::InFunction(
Either::Left(imp),
Box::new(MirEvalError::MirLowerError(imp, e)), Box::new(MirEvalError::MirLowerError(imp, e)),
span, vec![(Either::Left(imp), span, locals.body.owner)],
locals.body.owner,
) )
})?; })?;
let result = self.interpret_mir(&mir_body, arg_bytes.iter().cloned()).map_err(|e| { Ok(if let Some(target_bb) = target_bb {
MirEvalError::InFunction(Either::Left(imp), Box::new(e), span, locals.body.owner) 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)?; destination.write_from_bytes(self, &result)?;
Ok(()) None
})
} }
fn exec_fn_trait( fn exec_fn_trait(
@ -2003,10 +2142,11 @@ impl Evaluator<'_> {
def: FunctionId, def: FunctionId,
args: &[IntervalAndTy], args: &[IntervalAndTy],
generic_args: Substitution, generic_args: Substitution,
locals: &Locals<'_>, locals: &Locals,
destination: Interval, destination: Interval,
target_bb: Option<BasicBlockId>,
span: MirSpan, span: MirSpan,
) -> Result<()> { ) -> Result<Option<StackFrame>> {
let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?; let func = args.get(0).ok_or(MirEvalError::TypeError("fn trait with no arg"))?;
let mut func_ty = func.ty.clone(); let mut func_ty = func.ty.clone();
let mut func_data = func.interval; let mut func_data = func.interval;
@ -2023,13 +2163,28 @@ impl Evaluator<'_> {
} }
match &func_ty.data(Interner).kind { match &func_ty.data(Interner).kind {
TyKind::FnDef(def, subst) => { 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(_) => { 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) => { TyKind::Closure(closure, subst) => {
self.exec_closure( return self.exec_closure(
*closure, *closure,
func_data, func_data,
&Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()), &Substitution::from_iter(Interner, ClosureSubst(subst).parent_subst()),
@ -2037,7 +2192,7 @@ impl Evaluator<'_> {
&args[1..], &args[1..],
locals, locals,
span, span,
)?; );
} }
_ => { _ => {
// try to execute the manual impl of `FnTrait` for structs (nightly feature used in std) // try to execute the manual impl of `FnTrait` for structs (nightly feature used in std)
@ -2068,14 +2223,14 @@ impl Evaluator<'_> {
generic_args, generic_args,
locals, locals,
destination, destination,
target_bb,
span, 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) { if let Some(o) = self.static_locations.get(&st) {
return Ok(*o); 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)?; let (addr, ty, metadata) = self.place_addr_and_ty_and_metadata(place, locals)?;
if !locals.drop_flags.remove_place(place) { if !locals.drop_flags.remove_place(place) {
return Ok(()); return Ok(());
@ -2138,7 +2293,7 @@ impl Evaluator<'_> {
fn run_drop_glue_deep( fn run_drop_glue_deep(
&mut self, &mut self,
ty: Ty, ty: Ty,
locals: &Locals<'_>, locals: &Locals,
addr: Address, addr: Address,
_metadata: &[u8], _metadata: &[u8],
span: MirSpan, span: MirSpan,
@ -2151,8 +2306,7 @@ impl Evaluator<'_> {
// we can ignore drop in them. // we can ignore drop in them.
return Ok(()); return Ok(());
}; };
let (impl_drop_candidate, subst) = lookup_impl_method( let (impl_drop_candidate, subst) = self.db.lookup_impl_method(
self.db,
self.trait_env.clone(), self.trait_env.clone(),
drop_fn, drop_fn,
Substitution::from1(Interner, ty.clone()), Substitution::from1(Interner, ty.clone()),
@ -2162,9 +2316,10 @@ impl Evaluator<'_> {
subst, subst,
locals, locals,
impl_drop_candidate, impl_drop_candidate,
vec![addr.to_bytes()], [IntervalOrOwned::Owned(addr.to_bytes())].into_iter(),
span, span,
Interval { addr: Address::Invalid(0), size: 0 }, Interval { addr: Address::Invalid(0), size: 0 },
None,
)?; )?;
} }
match ty.kind(Interner) { match ty.kind(Interner) {

View File

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

View File

@ -50,7 +50,7 @@ impl Evaluator<'_> {
args: &[IntervalAndTy], args: &[IntervalAndTy],
_generic_args: &Substitution, _generic_args: &Substitution,
destination: Interval, destination: Interval,
_locals: &Locals<'_>, _locals: &Locals,
_span: MirSpan, _span: MirSpan,
) -> Result<()> { ) -> Result<()> {
match name { 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()), db.trait_environment(func_id.into()),
) )
.map_err(|e| MirEvalError::MirLowerError(func_id.into(), e))?; .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?; result?;
Ok((stdout, stderr)) Ok((stdout, stderr))
} }

View File

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

View File

@ -1986,7 +1986,7 @@ impl Function {
return r; 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 { let mut text = match result {
Ok(_) => "pass".to_string(), Ok(_) => "pass".to_string(),
Err(e) => { Err(e) => {

View File

@ -832,7 +832,7 @@ impl SourceAnalyzer {
None => return func, None => return func,
}; };
let env = db.trait_environment_for_body(owner); 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( fn resolve_impl_const_or_trait_def(