Skip to content

Commit cbf262a

Browse files
committed
Change order of calls to get method candidate order correct
1 parent 3376c08 commit cbf262a

File tree

2 files changed

+115
-39
lines changed

2 files changed

+115
-39
lines changed

crates/ra_hir_ty/src/method_resolution.rs

Lines changed: 115 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,6 @@ pub fn iterate_method_candidates<T>(
176176
mode: LookupMode,
177177
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
178178
) -> Option<T> {
179-
let krate = resolver.krate()?;
180179
match mode {
181180
LookupMode::MethodCall => {
182181
// For method calls, rust first does any number of autoderef, and then one
@@ -189,57 +188,125 @@ pub fn iterate_method_candidates<T>(
189188
// rustc does an autoderef and then autoref again).
190189
let environment = TraitEnvironment::lower(db, resolver);
191190
let ty = InEnvironment { value: ty.clone(), environment };
192-
for derefed_ty in autoderef::autoderef(db, resolver.krate(), ty) {
193-
if let Some(result) =
194-
iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback)
195-
{
196-
return Some(result);
197-
}
198-
if let Some(result) = iterate_trait_method_candidates(
199-
&derefed_ty,
191+
let krate = resolver.krate()?;
192+
193+
// We have to be careful about the order of operations here.
194+
// Consider the case where we're resolving `x.clone()` where `x:
195+
// &Vec<_>`. This resolves to the clone method with self type
196+
// `Vec<_>`, *not* `&_`. I.e. we need to consider methods where the
197+
// receiver type exactly matches before cases where we have to do
198+
// autoref. But in the autoderef steps, the `&_` self type comes up
199+
// *before* the `Vec<_>` self type.
200+
//
201+
// On the other hand, we don't want to just pick any by-value method
202+
// before any by-autoref method; it's just that we need to consider
203+
// the methods by autoderef order of *receiver types*, not *self
204+
// types*.
205+
206+
let deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty.clone()).collect();
207+
for i in 0..deref_chain.len() {
208+
if let Some(result) = iterate_method_candidates_autoref(
209+
&deref_chain[i..],
200210
db,
201211
resolver,
202212
name,
203-
mode,
204213
&mut callback,
205214
) {
206215
return Some(result);
207216
}
208217
}
218+
None
209219
}
210220
LookupMode::Path => {
211221
// No autoderef for path lookups
212-
if let Some(result) =
213-
iterate_inherent_methods(&ty, db, name, mode, krate.into(), &mut callback)
214-
{
215-
return Some(result);
216-
}
217-
if let Some(result) =
218-
iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback)
219-
{
220-
return Some(result);
221-
}
222+
iterate_method_candidates_inner(&ty, db, resolver, name, None, &mut callback)
223+
}
224+
}
225+
}
226+
227+
fn iterate_method_candidates_autoref<T>(
228+
deref_chain: &[Canonical<Ty>],
229+
db: &impl HirDatabase,
230+
resolver: &Resolver,
231+
name: Option<&Name>,
232+
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
233+
) -> Option<T> {
234+
if let Some(result) = iterate_method_candidates_by_receiver(&deref_chain[0], &deref_chain[1..], db, resolver, name, &mut callback) {
235+
return Some(result);
236+
}
237+
let refed = Canonical {
238+
num_vars: deref_chain[0].num_vars,
239+
value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()),
240+
};
241+
if let Some(result) = iterate_method_candidates_by_receiver(&refed, deref_chain, db, resolver, name, &mut callback) {
242+
return Some(result);
243+
}
244+
let ref_muted = Canonical {
245+
num_vars: deref_chain[0].num_vars,
246+
value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()),
247+
};
248+
if let Some(result) = iterate_method_candidates_by_receiver(&ref_muted, deref_chain, db, resolver, name, &mut callback) {
249+
return Some(result);
250+
}
251+
None
252+
}
253+
254+
fn iterate_method_candidates_by_receiver<T>(
255+
receiver_ty: &Canonical<Ty>,
256+
deref_chain: &[Canonical<Ty>],
257+
db: &impl HirDatabase,
258+
resolver: &Resolver,
259+
name: Option<&Name>,
260+
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
261+
) -> Option<T> {
262+
// TODO: do we need to do the whole loop for inherents before traits?
263+
// We're looking for methods with *receiver* type receiver_ty. These could
264+
// be found in any of the derefs of receiver_ty, so we have to go through
265+
// that.
266+
for self_ty in std::iter::once(receiver_ty).chain(deref_chain) {
267+
if let Some(result) = iterate_method_candidates_inner(self_ty, db, resolver, name, Some(receiver_ty), &mut callback) {
268+
return Some(result);
222269
}
223270
}
224271
None
225272
}
226273

274+
fn iterate_method_candidates_inner<T>(
275+
self_ty: &Canonical<Ty>,
276+
db: &impl HirDatabase,
277+
resolver: &Resolver,
278+
name: Option<&Name>,
279+
receiver_ty: Option<&Canonical<Ty>>,
280+
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
281+
) -> Option<T> {
282+
let krate = resolver.krate()?;
283+
if let Some(result) = iterate_inherent_methods(self_ty, db, name, receiver_ty, krate, &mut callback) {
284+
return Some(result);
285+
}
286+
if let Some(result) =
287+
iterate_trait_method_candidates(self_ty, db, resolver, name, receiver_ty, &mut callback)
288+
{
289+
return Some(result);
290+
}
291+
None
292+
}
293+
227294
fn iterate_trait_method_candidates<T>(
228-
ty: &Canonical<Ty>,
295+
self_ty: &Canonical<Ty>,
229296
db: &impl HirDatabase,
230297
resolver: &Resolver,
231298
name: Option<&Name>,
232-
mode: LookupMode,
299+
receiver_ty: Option<&Canonical<Ty>>,
233300
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
234301
) -> Option<T> {
235302
let krate = resolver.krate()?;
236303
// FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
237304
let env = TraitEnvironment::lower(db, resolver);
238305
// if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope
239-
let inherent_trait = ty.value.inherent_trait().into_iter();
306+
let inherent_trait = self_ty.value.inherent_trait().into_iter();
240307
// if we have `T: Trait` in the param env, the trait doesn't need to be in scope
241308
let traits_from_env = env
242-
.trait_predicates_for_self_ty(&ty.value)
309+
.trait_predicates_for_self_ty(&self_ty.value)
243310
.map(|tr| tr.trait_)
244311
.flat_map(|t| all_super_traits(db, t));
245312
let traits =
@@ -252,17 +319,17 @@ fn iterate_trait_method_candidates<T>(
252319
// iteration
253320
let mut known_implemented = false;
254321
for (_name, item) in data.items.iter() {
255-
if !is_valid_candidate(db, name, mode, (*item).into()) {
322+
if !is_valid_candidate(db, name, receiver_ty, (*item).into(), self_ty) {
256323
continue;
257324
}
258325
if !known_implemented {
259-
let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
326+
let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone());
260327
if db.trait_solve(krate.into(), goal).is_none() {
261328
continue 'traits;
262329
}
263330
}
264331
known_implemented = true;
265-
if let Some(result) = callback(&ty.value, (*item).into()) {
332+
if let Some(result) = callback(&self_ty.value, (*item).into()) {
266333
return Some(result);
267334
}
268335
}
@@ -271,22 +338,22 @@ fn iterate_trait_method_candidates<T>(
271338
}
272339

273340
fn iterate_inherent_methods<T>(
274-
ty: &Canonical<Ty>,
341+
self_ty: &Canonical<Ty>,
275342
db: &impl HirDatabase,
276343
name: Option<&Name>,
277-
mode: LookupMode,
344+
receiver_ty: Option<&Canonical<Ty>>,
278345
krate: CrateId,
279346
mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
280347
) -> Option<T> {
281-
for krate in ty.value.def_crates(db, krate)? {
348+
for krate in self_ty.value.def_crates(db, krate)? {
282349
let impls = db.impls_in_crate(krate);
283350

284-
for impl_block in impls.lookup_impl_blocks(&ty.value) {
351+
for impl_block in impls.lookup_impl_blocks(&self_ty.value) {
285352
for &item in db.impl_data(impl_block).items.iter() {
286-
if !is_valid_candidate(db, name, mode, item) {
353+
if !is_valid_candidate(db, name, receiver_ty, item, self_ty) {
287354
continue;
288355
}
289-
if let Some(result) = callback(&ty.value, item.into()) {
356+
if let Some(result) = callback(&self_ty.value, item) {
290357
return Some(result);
291358
}
292359
}
@@ -298,18 +365,29 @@ fn iterate_inherent_methods<T>(
298365
fn is_valid_candidate(
299366
db: &impl HirDatabase,
300367
name: Option<&Name>,
301-
mode: LookupMode,
368+
receiver_ty: Option<&Canonical<Ty>>,
302369
item: AssocItemId,
370+
self_ty: &Canonical<Ty>,
303371
) -> bool {
304372
match item {
305373
AssocItemId::FunctionId(m) => {
306374
let data = db.function_data(m);
307-
name.map_or(true, |name| &data.name == name)
308-
&& (data.has_self_param || mode == LookupMode::Path)
375+
if let Some(name) = name {
376+
if &data.name != name {
377+
return false;
378+
}
379+
}
380+
if let Some(receiver_ty) = receiver_ty {
381+
if !data.has_self_param {
382+
return false;
383+
}
384+
// TODO compare receiver ty
385+
}
386+
true
309387
}
310388
AssocItemId::ConstId(c) => {
311389
let data = db.const_data(c);
312-
name.map_or(true, |name| data.name.as_ref() == Some(name)) && (mode == LookupMode::Path)
390+
name.map_or(true, |name| data.name.as_ref() == Some(name)) && receiver_ty.is_none()
313391
}
314392
_ => false,
315393
}

crates/ra_hir_ty/src/tests.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3433,7 +3433,6 @@ pub fn baz() -> usize { 31usize }
34333433
assert_eq!("(i32, usize)", type_at_pos(&db, pos));
34343434
}
34353435

3436-
#[ignore]
34373436
#[test]
34383437
fn method_resolution_trait_before_autoref() {
34393438
let t = type_at(
@@ -3449,7 +3448,6 @@ fn test() { S.foo()<|>; }
34493448
assert_eq!(t, "u128");
34503449
}
34513450

3452-
#[ignore]
34533451
#[test]
34543452
fn method_resolution_by_value_before_autoref() {
34553453
let t = type_at(

0 commit comments

Comments
 (0)