Implement recursion in mir interpreter without recursion

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

View file

@ -18,7 +18,7 @@ use crate::{
generics::GenericParams, 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,

File diff suppressed because it is too large Load diff

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(