Refactor to unify with method resolution

This commit is contained in:
Florian Diebold 2019-10-31 19:28:33 +01:00
parent c7cedea270
commit 1173c3dab5
7 changed files with 117 additions and 141 deletions

View file

@ -1053,4 +1053,13 @@ impl AssocItem {
AssocItem::TypeAlias(t) => t.module(db),
}
}
pub fn container(self, db: &impl DefDatabase) -> Container {
match self {
AssocItem::Function(f) => f.container(db),
AssocItem::Const(c) => c.container(db),
AssocItem::TypeAlias(t) => t.container(db),
}
.expect("AssocItem without container")
}
}

View file

@ -77,9 +77,10 @@ impl GenericParams {
let parent = match def {
GenericDef::Function(it) => it.container(db).map(GenericDef::from),
GenericDef::TypeAlias(it) => it.container(db).map(GenericDef::from),
GenericDef::Const(it) => it.container(db).map(GenericDef::from),
GenericDef::EnumVariant(it) => Some(it.parent_enum(db).into()),
GenericDef::Adt(_) | GenericDef::Trait(_) => None,
GenericDef::ImplBlock(_) | GenericDef::Const(_) => None,
GenericDef::ImplBlock(_) => None,
};
let mut generics = GenericParams {
def,

View file

@ -28,8 +28,8 @@ use crate::{
ids::LocationCtx,
resolve::{ScopeDef, TypeNs, ValueNs},
ty::method_resolution::implements_trait,
Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId, MacroDef, Module,
Name, Path, Resolver, Static, Struct, Ty,
AssocItem, Const, DefWithBody, Either, Enum, FromSource, Function, HasBody, HirFileId,
MacroDef, Module, Name, Path, Resolver, Static, Struct, Ty,
};
fn try_get_resolver_for_node(
@ -327,7 +327,7 @@ impl SourceAnalyzer {
db: &impl HirDatabase,
ty: Ty,
name: Option<&Name>,
callback: impl FnMut(&Ty, Function) -> Option<T>,
callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
) -> Option<T> {
// There should be no inference vars in types passed here
// FIXME check that?
@ -337,6 +337,7 @@ impl SourceAnalyzer {
db,
&self.resolver,
name,
crate::ty::method_resolution::LookupMode::MethodCall,
callback,
)
}

View file

@ -6,11 +6,9 @@ use super::{ExprOrPatId, InferenceContext, TraitRef};
use crate::{
db::HirDatabase,
resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
ty::{lower, traits::TraitEnvironment, Canonical},
ty::{Substs, Ty, TypableDef, TypeWalk},
AssocItem, HasGenericParams, Name, Namespace, Path, Trait,
ty::{method_resolution, Substs, Ty, TypableDef, TypeWalk},
AssocItem, Container, HasGenericParams, Name, Namespace, Path,
};
use std::sync::Arc;
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
pub(super) fn infer_path(
@ -184,91 +182,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
return None;
}
self.find_inherent_assoc_candidate(ty.clone(), name, id)
.or_else(|| self.find_trait_assoc_candidate(ty.clone(), name, id))
}
fn find_inherent_assoc_candidate(
&mut self,
ty: Ty,
name: &Name,
id: ExprOrPatId,
) -> Option<(ValueNs, Option<Substs>)> {
let krate = self.resolver.krate()?;
// Find impl
let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item {
AssocItem::Function(func) => {
if *name == func.name(self.db) {
Some(AssocItem::Function(func))
} else {
None
}
}
AssocItem::Const(konst) => {
if konst.name(self.db).map_or(false, |n| n == *name) {
Some(AssocItem::Const(konst))
} else {
None
}
}
AssocItem::TypeAlias(_) => None,
})?;
let def = match item {
AssocItem::Function(f) => ValueNs::Function(f),
AssocItem::Const(c) => ValueNs::Const(c),
AssocItem::TypeAlias(_) => unreachable!(),
};
let substs = self.find_self_types(&def, ty);
self.write_assoc_resolution(id, item);
Some((def, substs))
}
fn find_trait_assoc_candidate(
&mut self,
ty: Ty,
name: &Name,
id: ExprOrPatId,
) -> Option<(ValueNs, Option<Substs>)> {
let krate = self.resolver.krate()?;
let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone());
let env = lower::trait_env(self.db, &self.resolver);
// if we have `T: Trait` in the param env, the trait doesn't need to be in scope
let traits_from_env = env
.trait_predicates_for_self_ty(&ty)
.map(|tr| tr.trait_)
.flat_map(|t| t.all_super_traits(self.db));
let traits = traits_from_env.chain(self.resolver.traits_in_scope(self.db));
'traits: for t in traits {
let data = t.trait_data(self.db);
let mut known_implemented = false;
for item in data.items() {
if let AssocItem::Function(f) = *item {
if f.name(self.db) == *name {
if !known_implemented {
let goal = generic_implements_goal(
self.db,
env.clone(),
t,
canonical_ty.value.clone(),
);
if self.db.trait_solve(krate, goal).is_none() {
continue 'traits;
}
}
known_implemented = true;
method_resolution::iterate_method_candidates(
&canonical_ty.value,
self.db,
&self.resolver.clone(),
Some(name),
method_resolution::LookupMode::Path,
move |_ty, item| {
let def = match item {
AssocItem::Function(f) => ValueNs::Function(f),
AssocItem::Const(c) => ValueNs::Const(c),
AssocItem::TypeAlias(_) => unreachable!(),
};
match item.container(self.db) {
Container::ImplBlock(_) => {
let substs = self.find_self_types(&def, ty.clone());
self.write_assoc_resolution(id, item);
Some((def, substs))
}
Container::Trait(t) => {
// we're picking this method
let trait_substs = Substs::build_for_def(self.db, t)
.push(ty.clone())
.fill(std::iter::repeat_with(|| self.new_type_var()))
.build();
let substs = Substs::build_for_def(self.db, f)
let substs = Substs::build_for_def(self.db, item)
.use_parent_substs(&trait_substs)
.fill_with_params()
.build();
@ -277,14 +218,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
substs: trait_substs,
}));
self.write_assoc_resolution(id, *item);
return Some((ValueNs::Function(f), Some(substs)));
self.write_assoc_resolution(id, item);
Some((def, Some(substs)))
}
}
}
}
None
},
)
}
fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> {
@ -317,23 +256,3 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
}
}
// TODO remove duplication
/// This creates Substs for a trait with the given Self type and type variables
/// for all other parameters, to query Chalk with it.
fn generic_implements_goal(
db: &impl HirDatabase,
env: Arc<TraitEnvironment>,
trait_: Trait,
self_ty: Canonical<Ty>,
) -> Canonical<super::InEnvironment<super::Obligation>> {
let num_vars = self_ty.num_vars;
let substs = super::Substs::build_for_def(db, trait_)
.push(self_ty.value)
.fill_with_bound_vars(num_vars as u32)
.build();
let num_vars = substs.len() - 1 + self_ty.num_vars;
let trait_ref = TraitRef { trait_, substs };
let obligation = super::Obligation::Trait(trait_ref);
Canonical { num_vars, value: super::InEnvironment::new(env, obligation) }
}

View file

@ -166,7 +166,19 @@ pub(crate) fn lookup_method(
name: &Name,
resolver: &Resolver,
) -> Option<(Ty, Function)> {
iterate_method_candidates(ty, db, resolver, Some(name), |ty, f| Some((ty.clone(), f)))
iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| {
if let AssocItem::Function(f) = f {
Some((ty.clone(), f))
} else {
None
}
})
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub(crate) enum LookupMode {
MethodCall,
Path,
}
// This would be nicer if it just returned an iterator, but that runs into
@ -176,7 +188,8 @@ pub(crate) fn iterate_method_candidates<T>(
db: &impl HirDatabase,
resolver: &Resolver,
name: Option<&Name>,
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
mode: LookupMode,
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
) -> Option<T> {
// For method calls, rust first does any number of autoderef, and then one
// autoref (i.e. when the method takes &self or &mut self). We just ignore
@ -188,13 +201,15 @@ pub(crate) fn iterate_method_candidates<T>(
// rustc does an autoderef and then autoref again).
let krate = resolver.krate()?;
// TODO no autoderef in LookupMode::Path
for derefed_ty in autoderef::autoderef(db, resolver, ty.clone()) {
if let Some(result) = iterate_inherent_methods(&derefed_ty, db, name, krate, &mut callback)
if let Some(result) =
iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback)
{
return Some(result);
}
if let Some(result) =
iterate_trait_method_candidates(&derefed_ty, db, resolver, name, &mut callback)
iterate_trait_method_candidates(&derefed_ty, db, resolver, name, mode, &mut callback)
{
return Some(result);
}
@ -207,7 +222,8 @@ fn iterate_trait_method_candidates<T>(
db: &impl HirDatabase,
resolver: &Resolver,
name: Option<&Name>,
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
mode: LookupMode,
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
) -> Option<T> {
let krate = resolver.krate()?;
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
@ -231,21 +247,35 @@ fn iterate_trait_method_candidates<T>(
// trait, but if we find out it doesn't, we'll skip the rest of the
// iteration
let mut known_implemented = inherently_implemented;
for item in data.items() {
if let AssocItem::Function(m) = *item {
let data = m.data(db);
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
if !known_implemented {
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
if db.trait_solve(krate, goal).is_none() {
continue 'traits;
}
}
known_implemented = true;
if let Some(result) = callback(&ty.value, m) {
return Some(result);
for &item in data.items() {
// TODO unify with the impl case
match item {
AssocItem::Function(m) => {
let data = m.data(db);
if !name.map_or(true, |name| data.name() == name)
|| (!data.has_self_param() && mode != LookupMode::Path)
{
continue;
}
}
AssocItem::Const(c) => {
if !name.map_or(true, |name| Some(name) == c.name(db).as_ref())
|| (mode != LookupMode::Path)
{
continue;
}
}
_ => {}
};
if !known_implemented {
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
if db.trait_solve(krate, goal).is_none() {
continue 'traits;
}
}
known_implemented = true;
if let Some(result) = callback(&ty.value, item) {
return Some(result);
}
}
}
@ -256,21 +286,35 @@ fn iterate_inherent_methods<T>(
ty: &Canonical<Ty>,
db: &impl HirDatabase,
name: Option<&Name>,
mode: LookupMode,
krate: Crate,
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
mut callback: impl FnMut(&Ty, AssocItem) -> Option<T>,
) -> Option<T> {
for krate in def_crates(db, krate, &ty.value)? {
let impls = db.impls_in_crate(krate);
for impl_block in impls.lookup_impl_blocks(&ty.value) {
for item in impl_block.items(db) {
if let AssocItem::Function(f) = item {
let data = f.data(db);
if name.map_or(true, |name| data.name() == name) && data.has_self_param() {
if let Some(result) = callback(&ty.value, f) {
return Some(result);
match item {
AssocItem::Function(f) => {
let data = f.data(db);
if !name.map_or(true, |name| data.name() == name)
|| (!data.has_self_param() && mode != LookupMode::Path)
{
continue;
}
}
AssocItem::Const(c) => {
if !name.map_or(true, |name| Some(name) == c.name(db).as_ref())
|| (mode != LookupMode::Path)
{
continue;
}
}
_ => {}
}
if let Some(result) = callback(&ty.value, item) {
return Some(result);
}
}
}

View file

@ -1841,8 +1841,8 @@ fn test() {
[243; 254) 'Struct::FOO': u32
[264; 265) 'y': u32
[268; 277) 'Enum::BAR': u32
[287; 288) 'z': {unknown}
[291; 304) 'TraitTest::ID': {unknown}
[287; 288) 'z': u32
[291; 304) 'TraitTest::ID': u32
"###
);
}

View file

@ -58,10 +58,12 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty)
fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty) {
let mut seen_methods = FxHashSet::default();
ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, func| {
let data = func.data(ctx.db);
if data.has_self_param() && seen_methods.insert(data.name().clone()) {
acc.add_function(ctx, func);
ctx.analyzer.iterate_method_candidates(ctx.db, receiver, None, |_ty, item| {
if let hir::AssocItem::Function(func) = item {
let data = func.data(ctx.db);
if data.has_self_param() && seen_methods.insert(data.name().clone()) {
acc.add_function(ctx, func);
}
}
None::<()>
});