diff options
Diffstat (limited to 'crates/ra_hir_ty/src/method_resolution.rs')
-rw-r--r-- | crates/ra_hir_ty/src/method_resolution.rs | 238 |
1 files changed, 193 insertions, 45 deletions
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index ee1936b0e..888dc3116 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs | |||
@@ -6,20 +6,21 @@ use std::sync::Arc; | |||
6 | 6 | ||
7 | use arrayvec::ArrayVec; | 7 | use arrayvec::ArrayVec; |
8 | use hir_def::{ | 8 | use hir_def::{ |
9 | lang_item::LangItemTarget, resolver::Resolver, type_ref::Mutability, AssocItemId, AstItemDef, | 9 | lang_item::LangItemTarget, resolver::Resolver, type_ref::Mutability, AssocContainerId, |
10 | FunctionId, HasModule, ImplId, TraitId, | 10 | AssocItemId, FunctionId, HasModule, ImplId, Lookup, TraitId, |
11 | }; | 11 | }; |
12 | use hir_expand::name::Name; | 12 | use hir_expand::name::Name; |
13 | use ra_db::CrateId; | 13 | use ra_db::CrateId; |
14 | use ra_prof::profile; | 14 | use ra_prof::profile; |
15 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
16 | 16 | ||
17 | use super::Substs; | ||
17 | use crate::{ | 18 | use crate::{ |
18 | autoderef, | 19 | autoderef, |
19 | db::HirDatabase, | 20 | db::HirDatabase, |
20 | primitive::{FloatBitness, Uncertain}, | 21 | primitive::{FloatBitness, Uncertain}, |
21 | utils::all_super_traits, | 22 | utils::all_super_traits, |
22 | Canonical, ImplTy, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, | 23 | Canonical, InEnvironment, TraitEnvironment, TraitRef, Ty, TypeCtor, TypeWalk, |
23 | }; | 24 | }; |
24 | 25 | ||
25 | /// This is used as a key for indexing impls. | 26 | /// This is used as a key for indexing impls. |
@@ -57,12 +58,13 @@ impl CrateImplBlocks { | |||
57 | 58 | ||
58 | let crate_def_map = db.crate_def_map(krate); | 59 | let crate_def_map = db.crate_def_map(krate); |
59 | for (_module_id, module_data) in crate_def_map.modules.iter() { | 60 | for (_module_id, module_data) in crate_def_map.modules.iter() { |
60 | for &impl_id in module_data.impls.iter() { | 61 | for impl_id in module_data.scope.impls() { |
61 | match db.impl_ty(impl_id) { | 62 | match db.impl_trait(impl_id) { |
62 | ImplTy::TraitRef(tr) => { | 63 | Some(tr) => { |
63 | res.impls_by_trait.entry(tr.trait_).or_default().push(impl_id); | 64 | res.impls_by_trait.entry(tr.trait_).or_default().push(impl_id); |
64 | } | 65 | } |
65 | ImplTy::Inherent(self_ty) => { | 66 | None => { |
67 | let self_ty = db.impl_self_ty(impl_id); | ||
66 | if let Some(self_ty_fp) = TyFingerprint::for_impl(&self_ty) { | 68 | if let Some(self_ty_fp) = TyFingerprint::for_impl(&self_ty) { |
67 | res.impls.entry(self_ty_fp).or_default().push(impl_id); | 69 | res.impls.entry(self_ty_fp).or_default().push(impl_id); |
68 | } | 70 | } |
@@ -132,7 +134,7 @@ impl Ty { | |||
132 | LangItemTarget::ImplBlockId(it) => Some(it), | 134 | LangItemTarget::ImplBlockId(it) => Some(it), |
133 | _ => None, | 135 | _ => None, |
134 | }) | 136 | }) |
135 | .map(|it| it.module(db).krate) | 137 | .map(|it| it.lookup(db).container.module(db).krate) |
136 | .collect(); | 138 | .collect(); |
137 | Some(res) | 139 | Some(res) |
138 | } | 140 | } |
@@ -175,7 +177,6 @@ pub fn iterate_method_candidates<T>( | |||
175 | mode: LookupMode, | 177 | mode: LookupMode, |
176 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 178 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
177 | ) -> Option<T> { | 179 | ) -> Option<T> { |
178 | let krate = resolver.krate()?; | ||
179 | match mode { | 180 | match mode { |
180 | LookupMode::MethodCall => { | 181 | LookupMode::MethodCall => { |
181 | // For method calls, rust first does any number of autoderef, and then one | 182 | // For method calls, rust first does any number of autoderef, and then one |
@@ -188,57 +189,159 @@ pub fn iterate_method_candidates<T>( | |||
188 | // rustc does an autoderef and then autoref again). | 189 | // rustc does an autoderef and then autoref again). |
189 | let environment = TraitEnvironment::lower(db, resolver); | 190 | let environment = TraitEnvironment::lower(db, resolver); |
190 | let ty = InEnvironment { value: ty.clone(), environment }; | 191 | let ty = InEnvironment { value: ty.clone(), environment }; |
191 | for derefed_ty in autoderef::autoderef(db, resolver.krate(), ty) { | 192 | let krate = resolver.krate()?; |
192 | if let Some(result) = | 193 | |
193 | iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback) | 194 | // We have to be careful about the order we're looking at candidates |
194 | { | 195 | // in here. Consider the case where we're resolving `x.clone()` |
195 | return Some(result); | 196 | // where `x: &Vec<_>`. This resolves to the clone method with self |
196 | } | 197 | // type `Vec<_>`, *not* `&_`. I.e. we need to consider methods where |
197 | if let Some(result) = iterate_trait_method_candidates( | 198 | // the receiver type exactly matches before cases where we have to |
198 | &derefed_ty, | 199 | // do autoref. But in the autoderef steps, the `&_` self type comes |
200 | // up *before* the `Vec<_>` self type. | ||
201 | // | ||
202 | // On the other hand, we don't want to just pick any by-value method | ||
203 | // before any by-autoref method; it's just that we need to consider | ||
204 | // the methods by autoderef order of *receiver types*, not *self | ||
205 | // types*. | ||
206 | |||
207 | let deref_chain: Vec<_> = autoderef::autoderef(db, Some(krate), ty.clone()).collect(); | ||
208 | for i in 0..deref_chain.len() { | ||
209 | if let Some(result) = iterate_method_candidates_with_autoref( | ||
210 | &deref_chain[i..], | ||
199 | db, | 211 | db, |
200 | resolver, | 212 | resolver, |
201 | name, | 213 | name, |
202 | mode, | ||
203 | &mut callback, | 214 | &mut callback, |
204 | ) { | 215 | ) { |
205 | return Some(result); | 216 | return Some(result); |
206 | } | 217 | } |
207 | } | 218 | } |
219 | None | ||
208 | } | 220 | } |
209 | LookupMode::Path => { | 221 | LookupMode::Path => { |
210 | // No autoderef for path lookups | 222 | // No autoderef for path lookups |
211 | if let Some(result) = | 223 | iterate_method_candidates_for_self_ty(&ty, db, resolver, name, &mut callback) |
212 | iterate_inherent_methods(&ty, db, name, mode, krate.into(), &mut callback) | 224 | } |
213 | { | 225 | } |
214 | return Some(result); | 226 | } |
215 | } | 227 | |
216 | if let Some(result) = | 228 | fn iterate_method_candidates_with_autoref<T>( |
217 | iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback) | 229 | deref_chain: &[Canonical<Ty>], |
218 | { | 230 | db: &impl HirDatabase, |
219 | return Some(result); | 231 | resolver: &Resolver, |
220 | } | 232 | name: Option<&Name>, |
233 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | ||
234 | ) -> Option<T> { | ||
235 | if let Some(result) = iterate_method_candidates_by_receiver( | ||
236 | &deref_chain[0], | ||
237 | &deref_chain[1..], | ||
238 | db, | ||
239 | resolver, | ||
240 | name, | ||
241 | &mut callback, | ||
242 | ) { | ||
243 | return Some(result); | ||
244 | } | ||
245 | let refed = Canonical { | ||
246 | num_vars: deref_chain[0].num_vars, | ||
247 | value: Ty::apply_one(TypeCtor::Ref(Mutability::Shared), deref_chain[0].value.clone()), | ||
248 | }; | ||
249 | if let Some(result) = iterate_method_candidates_by_receiver( | ||
250 | &refed, | ||
251 | deref_chain, | ||
252 | db, | ||
253 | resolver, | ||
254 | name, | ||
255 | &mut callback, | ||
256 | ) { | ||
257 | return Some(result); | ||
258 | } | ||
259 | let ref_muted = Canonical { | ||
260 | num_vars: deref_chain[0].num_vars, | ||
261 | value: Ty::apply_one(TypeCtor::Ref(Mutability::Mut), deref_chain[0].value.clone()), | ||
262 | }; | ||
263 | if let Some(result) = iterate_method_candidates_by_receiver( | ||
264 | &ref_muted, | ||
265 | deref_chain, | ||
266 | db, | ||
267 | resolver, | ||
268 | name, | ||
269 | &mut callback, | ||
270 | ) { | ||
271 | return Some(result); | ||
272 | } | ||
273 | None | ||
274 | } | ||
275 | |||
276 | fn iterate_method_candidates_by_receiver<T>( | ||
277 | receiver_ty: &Canonical<Ty>, | ||
278 | rest_of_deref_chain: &[Canonical<Ty>], | ||
279 | db: &impl HirDatabase, | ||
280 | resolver: &Resolver, | ||
281 | name: Option<&Name>, | ||
282 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | ||
283 | ) -> Option<T> { | ||
284 | // We're looking for methods with *receiver* type receiver_ty. These could | ||
285 | // be found in any of the derefs of receiver_ty, so we have to go through | ||
286 | // that. | ||
287 | let krate = resolver.krate()?; | ||
288 | for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) { | ||
289 | if let Some(result) = | ||
290 | iterate_inherent_methods(self_ty, db, name, Some(receiver_ty), krate, &mut callback) | ||
291 | { | ||
292 | return Some(result); | ||
293 | } | ||
294 | } | ||
295 | for self_ty in std::iter::once(receiver_ty).chain(rest_of_deref_chain) { | ||
296 | if let Some(result) = iterate_trait_method_candidates( | ||
297 | self_ty, | ||
298 | db, | ||
299 | resolver, | ||
300 | name, | ||
301 | Some(receiver_ty), | ||
302 | &mut callback, | ||
303 | ) { | ||
304 | return Some(result); | ||
221 | } | 305 | } |
222 | } | 306 | } |
223 | None | 307 | None |
224 | } | 308 | } |
225 | 309 | ||
310 | fn iterate_method_candidates_for_self_ty<T>( | ||
311 | self_ty: &Canonical<Ty>, | ||
312 | db: &impl HirDatabase, | ||
313 | resolver: &Resolver, | ||
314 | name: Option<&Name>, | ||
315 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | ||
316 | ) -> Option<T> { | ||
317 | let krate = resolver.krate()?; | ||
318 | if let Some(result) = iterate_inherent_methods(self_ty, db, name, None, krate, &mut callback) { | ||
319 | return Some(result); | ||
320 | } | ||
321 | if let Some(result) = | ||
322 | iterate_trait_method_candidates(self_ty, db, resolver, name, None, &mut callback) | ||
323 | { | ||
324 | return Some(result); | ||
325 | } | ||
326 | None | ||
327 | } | ||
328 | |||
226 | fn iterate_trait_method_candidates<T>( | 329 | fn iterate_trait_method_candidates<T>( |
227 | ty: &Canonical<Ty>, | 330 | self_ty: &Canonical<Ty>, |
228 | db: &impl HirDatabase, | 331 | db: &impl HirDatabase, |
229 | resolver: &Resolver, | 332 | resolver: &Resolver, |
230 | name: Option<&Name>, | 333 | name: Option<&Name>, |
231 | mode: LookupMode, | 334 | receiver_ty: Option<&Canonical<Ty>>, |
232 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 335 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
233 | ) -> Option<T> { | 336 | ) -> Option<T> { |
234 | let krate = resolver.krate()?; | 337 | let krate = resolver.krate()?; |
235 | // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) | 338 | // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that) |
236 | let env = TraitEnvironment::lower(db, resolver); | 339 | let env = TraitEnvironment::lower(db, resolver); |
237 | // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope | 340 | // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope |
238 | let inherent_trait = ty.value.inherent_trait().into_iter(); | 341 | let inherent_trait = self_ty.value.inherent_trait().into_iter(); |
239 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope | 342 | // if we have `T: Trait` in the param env, the trait doesn't need to be in scope |
240 | let traits_from_env = env | 343 | let traits_from_env = env |
241 | .trait_predicates_for_self_ty(&ty.value) | 344 | .trait_predicates_for_self_ty(&self_ty.value) |
242 | .map(|tr| tr.trait_) | 345 | .map(|tr| tr.trait_) |
243 | .flat_map(|t| all_super_traits(db, t)); | 346 | .flat_map(|t| all_super_traits(db, t)); |
244 | let traits = | 347 | let traits = |
@@ -251,17 +354,17 @@ fn iterate_trait_method_candidates<T>( | |||
251 | // iteration | 354 | // iteration |
252 | let mut known_implemented = false; | 355 | let mut known_implemented = false; |
253 | for (_name, item) in data.items.iter() { | 356 | for (_name, item) in data.items.iter() { |
254 | if !is_valid_candidate(db, name, mode, (*item).into()) { | 357 | if !is_valid_candidate(db, name, receiver_ty, (*item).into(), self_ty) { |
255 | continue; | 358 | continue; |
256 | } | 359 | } |
257 | if !known_implemented { | 360 | if !known_implemented { |
258 | let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); | 361 | let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone()); |
259 | if db.trait_solve(krate.into(), goal).is_none() { | 362 | if db.trait_solve(krate.into(), goal).is_none() { |
260 | continue 'traits; | 363 | continue 'traits; |
261 | } | 364 | } |
262 | } | 365 | } |
263 | known_implemented = true; | 366 | known_implemented = true; |
264 | if let Some(result) = callback(&ty.value, (*item).into()) { | 367 | if let Some(result) = callback(&self_ty.value, (*item).into()) { |
265 | return Some(result); | 368 | return Some(result); |
266 | } | 369 | } |
267 | } | 370 | } |
@@ -270,22 +373,22 @@ fn iterate_trait_method_candidates<T>( | |||
270 | } | 373 | } |
271 | 374 | ||
272 | fn iterate_inherent_methods<T>( | 375 | fn iterate_inherent_methods<T>( |
273 | ty: &Canonical<Ty>, | 376 | self_ty: &Canonical<Ty>, |
274 | db: &impl HirDatabase, | 377 | db: &impl HirDatabase, |
275 | name: Option<&Name>, | 378 | name: Option<&Name>, |
276 | mode: LookupMode, | 379 | receiver_ty: Option<&Canonical<Ty>>, |
277 | krate: CrateId, | 380 | krate: CrateId, |
278 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 381 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
279 | ) -> Option<T> { | 382 | ) -> Option<T> { |
280 | for krate in ty.value.def_crates(db, krate)? { | 383 | for krate in self_ty.value.def_crates(db, krate)? { |
281 | let impls = db.impls_in_crate(krate); | 384 | let impls = db.impls_in_crate(krate); |
282 | 385 | ||
283 | for impl_block in impls.lookup_impl_blocks(&ty.value) { | 386 | for impl_block in impls.lookup_impl_blocks(&self_ty.value) { |
284 | for &item in db.impl_data(impl_block).items.iter() { | 387 | for &item in db.impl_data(impl_block).items.iter() { |
285 | if !is_valid_candidate(db, name, mode, item) { | 388 | if !is_valid_candidate(db, name, receiver_ty, item, self_ty) { |
286 | continue; | 389 | continue; |
287 | } | 390 | } |
288 | if let Some(result) = callback(&ty.value, item.into()) { | 391 | if let Some(result) = callback(&self_ty.value, item) { |
289 | return Some(result); | 392 | return Some(result); |
290 | } | 393 | } |
291 | } | 394 | } |
@@ -297,23 +400,68 @@ fn iterate_inherent_methods<T>( | |||
297 | fn is_valid_candidate( | 400 | fn is_valid_candidate( |
298 | db: &impl HirDatabase, | 401 | db: &impl HirDatabase, |
299 | name: Option<&Name>, | 402 | name: Option<&Name>, |
300 | mode: LookupMode, | 403 | receiver_ty: Option<&Canonical<Ty>>, |
301 | item: AssocItemId, | 404 | item: AssocItemId, |
405 | self_ty: &Canonical<Ty>, | ||
302 | ) -> bool { | 406 | ) -> bool { |
303 | match item { | 407 | match item { |
304 | AssocItemId::FunctionId(m) => { | 408 | AssocItemId::FunctionId(m) => { |
305 | let data = db.function_data(m); | 409 | let data = db.function_data(m); |
306 | name.map_or(true, |name| &data.name == name) | 410 | if let Some(name) = name { |
307 | && (data.has_self_param || mode == LookupMode::Path) | 411 | if &data.name != name { |
412 | return false; | ||
413 | } | ||
414 | } | ||
415 | if let Some(receiver_ty) = receiver_ty { | ||
416 | if !data.has_self_param { | ||
417 | return false; | ||
418 | } | ||
419 | let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) { | ||
420 | Some(ty) => ty, | ||
421 | None => return false, | ||
422 | }; | ||
423 | if transformed_receiver_ty != receiver_ty.value { | ||
424 | return false; | ||
425 | } | ||
426 | } | ||
427 | true | ||
308 | } | 428 | } |
309 | AssocItemId::ConstId(c) => { | 429 | AssocItemId::ConstId(c) => { |
310 | let data = db.const_data(c); | 430 | let data = db.const_data(c); |
311 | name.map_or(true, |name| data.name.as_ref() == Some(name)) && (mode == LookupMode::Path) | 431 | name.map_or(true, |name| data.name.as_ref() == Some(name)) && receiver_ty.is_none() |
312 | } | 432 | } |
313 | _ => false, | 433 | _ => false, |
314 | } | 434 | } |
315 | } | 435 | } |
316 | 436 | ||
437 | pub(crate) fn inherent_impl_substs( | ||
438 | db: &impl HirDatabase, | ||
439 | impl_id: ImplId, | ||
440 | self_ty: &Canonical<Ty>, | ||
441 | ) -> Option<Substs> { | ||
442 | let vars = Substs::build_for_def(db, impl_id).fill_with_bound_vars(0).build(); | ||
443 | let self_ty_with_vars = db.impl_self_ty(impl_id).subst(&vars); | ||
444 | let self_ty_with_vars = Canonical { num_vars: vars.len(), value: self_ty_with_vars }; | ||
445 | super::infer::unify(&self_ty_with_vars, self_ty) | ||
446 | } | ||
447 | |||
448 | fn transform_receiver_ty( | ||
449 | db: &impl HirDatabase, | ||
450 | function_id: FunctionId, | ||
451 | self_ty: &Canonical<Ty>, | ||
452 | ) -> Option<Ty> { | ||
453 | let substs = match function_id.lookup(db).container { | ||
454 | AssocContainerId::TraitId(_) => Substs::build_for_def(db, function_id) | ||
455 | .push(self_ty.value.clone()) | ||
456 | .fill_with_unknown() | ||
457 | .build(), | ||
458 | AssocContainerId::ImplId(impl_id) => inherent_impl_substs(db, impl_id, &self_ty)?, | ||
459 | AssocContainerId::ContainerId(_) => unreachable!(), | ||
460 | }; | ||
461 | let sig = db.callable_item_signature(function_id.into()); | ||
462 | Some(sig.params()[0].clone().subst(&substs)) | ||
463 | } | ||
464 | |||
317 | pub fn implements_trait( | 465 | pub fn implements_trait( |
318 | ty: &Canonical<Ty>, | 466 | ty: &Canonical<Ty>, |
319 | db: &impl HirDatabase, | 467 | db: &impl HirDatabase, |