Implement recursion in mir interpreter without recursion
This commit is contained in:
parent
3a1054fc1c
commit
4a444e768c
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -50,7 +50,7 @@ impl Evaluator<'_> {
|
||||
args: &[IntervalAndTy],
|
||||
_generic_args: &Substitution,
|
||||
destination: Interval,
|
||||
_locals: &Locals<'_>,
|
||||
_locals: &Locals,
|
||||
_span: MirSpan,
|
||||
) -> Result<()> {
|
||||
match name {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) => {
|
||||
|
@ -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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user