mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 01:17:27 +00:00
Implement recursion in mir interpreter without recursion
This commit is contained in:
parent
3a1054fc1c
commit
4a444e768c
15 changed files with 432 additions and 264 deletions
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) => {
|
||||||
|
|
|
@ -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(
|
||||||
|
|
Loading…
Reference in a new issue