Change order of calls to get method candidate order correct

This commit is contained in:
Florian Diebold 2019-11-02 15:18:26 +01:00
parent 3376c08052
commit cbf262a1bc
2 changed files with 116 additions and 40 deletions

View file

@ -176,7 +176,6 @@ pub fn iterate_method_candidates<T>(
mode: LookupMode, mode: LookupMode,
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
) -> Option<T> { ) -> Option<T> {
let krate = resolver.krate()?;
match mode { match mode {
LookupMode::MethodCall => { LookupMode::MethodCall => {
// For method calls, rust first does any number of autoderef, and then one // For method calls, rust first does any number of autoderef, and then one
@ -189,57 +188,125 @@ pub fn iterate_method_candidates<T>(
// rustc does an autoderef and then autoref again). // rustc does an autoderef and then autoref again).
let environment = TraitEnvironment::lower(db, resolver); let environment = TraitEnvironment::lower(db, resolver);
let ty = InEnvironment { value: ty.clone(), environment }; let ty = InEnvironment { value: ty.clone(), environment };
for derefed_ty in autoderef::autoderef(db, resolver.krate(), ty) { let krate = resolver.krate()?;
if let Some(result) =
iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback) // We have to be careful about the order of operations here.
{ // Consider the case where we're resolving `x.clone()` where `x:
return Some(result); // &Vec<_>`. This resolves to the clone method with self type
} // `Vec<_>`, *not* `&_`. I.e. we need to consider methods where the
if let Some(result) = iterate_trait_method_candidates( // receiver type exactly matches before cases where we have to do
&derefed_ty, // autoref. But in the autoderef steps, the `&_` self type comes up
// *before* the `Vec<_>` self type.
//
// On the other hand, we don't want to just pick any by-value method
// before any by-autoref method; it's just that we need to consider
// the methods by autoderef order of *receiver types*, not *self
// types*.
let deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty.clone()).collect();
for i in 0..deref_chain.len() {
if let Some(result) = iterate_method_candidates_autoref(
&deref_chain[i..],
db, db,
resolver, resolver,
name, name,
mode,
&mut callback, &mut callback,
) { ) {
return Some(result); return Some(result);
} }
} }
None
} }
LookupMode::Path => { LookupMode::Path => {
// No autoderef for path lookups // No autoderef for path lookups
if let Some(result) = iterate_method_candidates_inner(&ty, db, resolver, name, None, &mut callback)
iterate_inherent_methods(&ty, db, name, mode, krate.into(), &mut callback) }
{ }
}
fn iterate_method_candidates_autoref<T>(
deref_chain: &[Canonical<Ty>],
db: &impl HirDatabase,
resolver: &Resolver,
name: Option<&Name>,
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
) -> Option<T> {
if let Some(result) = iterate_method_candidates_by_receiver(&deref_chain[0], &deref_chain[1..], db, resolver, name, &mut callback) {
return Some(result); return Some(result);
} }
if let Some(result) = let refed = Canonical {
iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback) num_vars: deref_chain[0].num_vars,
{ value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()),
};
if let Some(result) = iterate_method_candidates_by_receiver(&refed, deref_chain, db, resolver, name, &mut callback) {
return Some(result); return Some(result);
} }
let ref_muted = Canonical {
num_vars: deref_chain[0].num_vars,
value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()),
};
if let Some(result) = iterate_method_candidates_by_receiver(&ref_muted, deref_chain, db, resolver, name, &mut callback) {
return Some(result);
}
None
}
fn iterate_method_candidates_by_receiver<T>(
receiver_ty: &Canonical<Ty>,
deref_chain: &[Canonical<Ty>],
db: &impl HirDatabase,
resolver: &Resolver,
name: Option<&Name>,
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
) -> Option<T> {
// TODO: do we need to do the whole loop for inherents before traits?
// We're looking for methods with *receiver* type receiver_ty. These could
// be found in any of the derefs of receiver_ty, so we have to go through
// that.
for self_ty in std::iter::once(receiver_ty).chain(deref_chain) {
if let Some(result) = iterate_method_candidates_inner(self_ty, db, resolver, name, Some(receiver_ty), &mut callback) {
return Some(result);
} }
} }
None None
} }
fn iterate_trait_method_candidates<T>( fn iterate_method_candidates_inner<T>(
ty: &Canonical<Ty>, self_ty: &Canonical<Ty>,
db: &impl HirDatabase, db: &impl HirDatabase,
resolver: &Resolver, resolver: &Resolver,
name: Option<&Name>, name: Option<&Name>,
mode: LookupMode, receiver_ty: Option<&Canonical<Ty>>,
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
) -> Option<T> {
let krate = resolver.krate()?;
if let Some(result) = iterate_inherent_methods(self_ty, db, name, receiver_ty, krate, &mut callback) {
return Some(result);
}
if let Some(result) =
iterate_trait_method_candidates(self_ty, db, resolver, name, receiver_ty, &mut callback)
{
return Some(result);
}
None
}
fn iterate_trait_method_candidates<T>(
self_ty: &Canonical<Ty>,
db: &impl HirDatabase,
resolver: &Resolver,
name: Option<&Name>,
receiver_ty: Option<&Canonical<Ty>>,
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
) -> Option<T> { ) -> Option<T> {
let krate = resolver.krate()?; let krate = resolver.krate()?;
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
let env = TraitEnvironment::lower(db, resolver); let env = TraitEnvironment::lower(db, resolver);
// if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope
let inherent_trait = ty.value.inherent_trait().into_iter(); let inherent_trait = self_ty.value.inherent_trait().into_iter();
// if we have `T: Trait` in the param env, the trait doesn't need to be in scope // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
let traits_from_env = env let traits_from_env = env
.trait_predicates_for_self_ty(&ty.value) .trait_predicates_for_self_ty(&self_ty.value)
.map(|tr| tr.trait_) .map(|tr| tr.trait_)
.flat_map(|t| all_super_traits(db, t)); .flat_map(|t| all_super_traits(db, t));
let traits = let traits =
@ -252,17 +319,17 @@ fn iterate_trait_method_candidates<T>(
// iteration // iteration
let mut known_implemented = false; let mut known_implemented = false;
for (_name, item) in data.items.iter() { for (_name, item) in data.items.iter() {
if !is_valid_candidate(db, name, mode, (*item).into()) { if !is_valid_candidate(db, name, receiver_ty, (*item).into(), self_ty) {
continue; continue;
} }
if !known_implemented { if !known_implemented {
let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone());
if db.trait_solve(krate.into(), goal).is_none() { if db.trait_solve(krate.into(), goal).is_none() {
continue 'traits; continue 'traits;
} }
} }
known_implemented = true; known_implemented = true;
if let Some(result) = callback(&ty.value, (*item).into()) { if let Some(result) = callback(&self_ty.value, (*item).into()) {
return Some(result); return Some(result);
} }
} }
@ -271,22 +338,22 @@ fn iterate_trait_method_candidates<T>(
} }
fn iterate_inherent_methods<T>( fn iterate_inherent_methods<T>(
ty: &Canonical<Ty>, self_ty: &Canonical<Ty>,
db: &impl HirDatabase, db: &impl HirDatabase,
name: Option<&Name>, name: Option<&Name>,
mode: LookupMode, receiver_ty: Option<&Canonical<Ty>>,
krate: CrateId, krate: CrateId,
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
) -> Option<T> { ) -> Option<T> {
for krate in ty.value.def_crates(db, krate)? { for krate in self_ty.value.def_crates(db, krate)? {
let impls = db.impls_in_crate(krate); let impls = db.impls_in_crate(krate);
for impl_block in impls.lookup_impl_blocks(&ty.value) { for impl_block in impls.lookup_impl_blocks(&self_ty.value) {
for &item in db.impl_data(impl_block).items.iter() { for &item in db.impl_data(impl_block).items.iter() {
if !is_valid_candidate(db, name, mode, item) { if !is_valid_candidate(db, name, receiver_ty, item, self_ty) {
continue; continue;
} }
if let Some(result) = callback(&ty.value, item.into()) { if let Some(result) = callback(&self_ty.value, item) {
return Some(result); return Some(result);
} }
} }
@ -298,18 +365,29 @@ fn iterate_inherent_methods<T>(
fn is_valid_candidate( fn is_valid_candidate(
db: &impl HirDatabase, db: &impl HirDatabase,
name: Option<&Name>, name: Option<&Name>,
mode: LookupMode, receiver_ty: Option<&Canonical<Ty>>,
item: AssocItemId, item: AssocItemId,
self_ty: &Canonical<Ty>,
) -> bool { ) -> bool {
match item { match item {
AssocItemId::FunctionId(m) => { AssocItemId::FunctionId(m) => {
let data = db.function_data(m); let data = db.function_data(m);
name.map_or(true, |name| &data.name == name) if let Some(name) = name {
&& (data.has_self_param || mode == LookupMode::Path) if &data.name != name {
return false;
}
}
if let Some(receiver_ty) = receiver_ty {
if !data.has_self_param {
return false;
}
// TODO compare receiver ty
}
true
} }
AssocItemId::ConstId(c) => { AssocItemId::ConstId(c) => {
let data = db.const_data(c); let data = db.const_data(c);
name.map_or(true, |name| data.name.as_ref() == Some(name)) && (mode == LookupMode::Path) name.map_or(true, |name| data.name.as_ref() == Some(name)) && receiver_ty.is_none()
} }
_ => false, _ => false,
} }

View file

@ -3433,7 +3433,6 @@ pub fn baz() -> usize { 31usize }
assert_eq!("(i32, usize)", type_at_pos(&db, pos)); assert_eq!("(i32, usize)", type_at_pos(&db, pos));
} }
#[ignore]
#[test] #[test]
fn method_resolution_trait_before_autoref() { fn method_resolution_trait_before_autoref() {
let t = type_at( let t = type_at(
@ -3449,7 +3448,6 @@ fn test() { S.foo()<|>; }
assert_eq!(t, "u128"); assert_eq!(t, "u128");
} }
#[ignore]
#[test] #[test]
fn method_resolution_by_value_before_autoref() { fn method_resolution_by_value_before_autoref() {
let t = type_at( let t = type_at(