diff options
Diffstat (limited to 'crates/ra_hir_ty/src/method_resolution.rs')
-rw-r--r-- | crates/ra_hir_ty/src/method_resolution.rs | 225 |
1 files changed, 186 insertions, 39 deletions
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs index 2bded3dbd..21efb196a 100644 --- a/crates/ra_hir_ty/src/method_resolution.rs +++ b/crates/ra_hir_ty/src/method_resolution.rs | |||
@@ -7,19 +7,20 @@ use std::sync::Arc; | |||
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, AssocItemId, AstItemDef, |
10 | FunctionId, HasModule, ImplId, TraitId, | 10 | 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, 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. |
@@ -176,7 +177,6 @@ pub fn iterate_method_candidates<T>( | |||
176 | mode: LookupMode, | 177 | mode: LookupMode, |
177 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 178 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
178 | ) -> Option<T> { | 179 | ) -> Option<T> { |
179 | let krate = resolver.krate()?; | ||
180 | match mode { | 180 | match mode { |
181 | LookupMode::MethodCall => { | 181 | LookupMode::MethodCall => { |
182 | // 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 |
@@ -189,57 +189,159 @@ pub fn iterate_method_candidates<T>( | |||
189 | // rustc does an autoderef and then autoref again). | 189 | // rustc does an autoderef and then autoref again). |
190 | let environment = TraitEnvironment::lower(db, resolver); | 190 | let environment = TraitEnvironment::lower(db, resolver); |
191 | let ty = InEnvironment { value: ty.clone(), environment }; | 191 | let ty = InEnvironment { value: ty.clone(), environment }; |
192 | for derefed_ty in autoderef::autoderef(db, resolver.krate(), ty) { | 192 | let krate = resolver.krate()?; |
193 | if let Some(result) = | 193 | |
194 | 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 |
195 | { | 195 | // in here. Consider the case where we're resolving `x.clone()` |
196 | return Some(result); | 196 | // where `x: &Vec<_>`. This resolves to the clone method with self |
197 | } | 197 | // type `Vec<_>`, *not* `&_`. I.e. we need to consider methods where |
198 | if let Some(result) = iterate_trait_method_candidates( | 198 | // the receiver type exactly matches before cases where we have to |
199 | &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..], | ||
200 | db, | 211 | db, |
201 | resolver, | 212 | resolver, |
202 | name, | 213 | name, |
203 | mode, | ||
204 | &mut callback, | 214 | &mut callback, |
205 | ) { | 215 | ) { |
206 | return Some(result); | 216 | return Some(result); |
207 | } | 217 | } |
208 | } | 218 | } |
219 | None | ||
209 | } | 220 | } |
210 | LookupMode::Path => { | 221 | LookupMode::Path => { |
211 | // No autoderef for path lookups | 222 | // No autoderef for path lookups |
212 | if let Some(result) = | 223 | iterate_method_candidates_for_self_ty(&ty, db, resolver, name, &mut callback) |
213 | iterate_inherent_methods(&ty, db, name, mode, krate.into(), &mut callback) | 224 | } |
214 | { | 225 | } |
215 | return Some(result); | 226 | } |
216 | } | 227 | |
217 | if let Some(result) = | 228 | fn iterate_method_candidates_with_autoref<T>( |
218 | iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback) | 229 | deref_chain: &[Canonical<Ty>], |
219 | { | 230 | db: &impl HirDatabase, |
220 | return Some(result); | 231 | resolver: &Resolver, |
221 | } | 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); | ||
222 | } | 305 | } |
223 | } | 306 | } |
224 | None | 307 | None |
225 | } | 308 | } |
226 | 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 | |||
227 | fn iterate_trait_method_candidates<T>( | 329 | fn iterate_trait_method_candidates<T>( |
228 | ty: &Canonical<Ty>, | 330 | self_ty: &Canonical<Ty>, |
229 | db: &impl HirDatabase, | 331 | db: &impl HirDatabase, |
230 | resolver: &Resolver, | 332 | resolver: &Resolver, |
231 | name: Option<&Name>, | 333 | name: Option<&Name>, |
232 | mode: LookupMode, | 334 | receiver_ty: Option<&Canonical<Ty>>, |
233 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 335 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
234 | ) -> Option<T> { | 336 | ) -> Option<T> { |
235 | let krate = resolver.krate()?; | 337 | let krate = resolver.krate()?; |
236 | // 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) |
237 | let env = TraitEnvironment::lower(db, resolver); | 339 | let env = TraitEnvironment::lower(db, resolver); |
238 | // 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 |
239 | let inherent_trait = ty.value.inherent_trait().into_iter(); | 341 | let inherent_trait = self_ty.value.inherent_trait().into_iter(); |
240 | // 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 |
241 | let traits_from_env = env | 343 | let traits_from_env = env |
242 | .trait_predicates_for_self_ty(&ty.value) | 344 | .trait_predicates_for_self_ty(&self_ty.value) |
243 | .map(|tr| tr.trait_) | 345 | .map(|tr| tr.trait_) |
244 | .flat_map(|t| all_super_traits(db, t)); | 346 | .flat_map(|t| all_super_traits(db, t)); |
245 | let traits = | 347 | let traits = |
@@ -252,17 +354,17 @@ fn iterate_trait_method_candidates<T>( | |||
252 | // iteration | 354 | // iteration |
253 | let mut known_implemented = false; | 355 | let mut known_implemented = false; |
254 | for (_name, item) in data.items.iter() { | 356 | for (_name, item) in data.items.iter() { |
255 | if !is_valid_candidate(db, name, mode, (*item).into()) { | 357 | if !is_valid_candidate(db, name, receiver_ty, (*item).into(), self_ty) { |
256 | continue; | 358 | continue; |
257 | } | 359 | } |
258 | if !known_implemented { | 360 | if !known_implemented { |
259 | let goal = generic_implements_goal(db, env.clone(), t, ty.clone()); | 361 | let goal = generic_implements_goal(db, env.clone(), t, self_ty.clone()); |
260 | if db.trait_solve(krate.into(), goal).is_none() { | 362 | if db.trait_solve(krate.into(), goal).is_none() { |
261 | continue 'traits; | 363 | continue 'traits; |
262 | } | 364 | } |
263 | } | 365 | } |
264 | known_implemented = true; | 366 | known_implemented = true; |
265 | if let Some(result) = callback(&ty.value, (*item).into()) { | 367 | if let Some(result) = callback(&self_ty.value, (*item).into()) { |
266 | return Some(result); | 368 | return Some(result); |
267 | } | 369 | } |
268 | } | 370 | } |
@@ -271,22 +373,22 @@ fn iterate_trait_method_candidates<T>( | |||
271 | } | 373 | } |
272 | 374 | ||
273 | fn iterate_inherent_methods<T>( | 375 | fn iterate_inherent_methods<T>( |
274 | ty: &Canonical<Ty>, | 376 | self_ty: &Canonical<Ty>, |
275 | db: &impl HirDatabase, | 377 | db: &impl HirDatabase, |
276 | name: Option<&Name>, | 378 | name: Option<&Name>, |
277 | mode: LookupMode, | 379 | receiver_ty: Option<&Canonical<Ty>>, |
278 | krate: CrateId, | 380 | krate: CrateId, |
279 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, | 381 | mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>, |
280 | ) -> Option<T> { | 382 | ) -> Option<T> { |
281 | for krate in ty.value.def_crates(db, krate)? { | 383 | for krate in self_ty.value.def_crates(db, krate)? { |
282 | let impls = db.impls_in_crate(krate); | 384 | let impls = db.impls_in_crate(krate); |
283 | 385 | ||
284 | for impl_block in impls.lookup_impl_blocks(&ty.value) { | 386 | for impl_block in impls.lookup_impl_blocks(&self_ty.value) { |
285 | for &item in db.impl_data(impl_block).items.iter() { | 387 | for &item in db.impl_data(impl_block).items.iter() { |
286 | if !is_valid_candidate(db, name, mode, item) { | 388 | if !is_valid_candidate(db, name, receiver_ty, item, self_ty) { |
287 | continue; | 389 | continue; |
288 | } | 390 | } |
289 | if let Some(result) = callback(&ty.value, item.into()) { | 391 | if let Some(result) = callback(&self_ty.value, item) { |
290 | return Some(result); | 392 | return Some(result); |
291 | } | 393 | } |
292 | } | 394 | } |
@@ -298,23 +400,68 @@ fn iterate_inherent_methods<T>( | |||
298 | fn is_valid_candidate( | 400 | fn is_valid_candidate( |
299 | db: &impl HirDatabase, | 401 | db: &impl HirDatabase, |
300 | name: Option<&Name>, | 402 | name: Option<&Name>, |
301 | mode: LookupMode, | 403 | receiver_ty: Option<&Canonical<Ty>>, |
302 | item: AssocItemId, | 404 | item: AssocItemId, |
405 | self_ty: &Canonical<Ty>, | ||
303 | ) -> bool { | 406 | ) -> bool { |
304 | match item { | 407 | match item { |
305 | AssocItemId::FunctionId(m) => { | 408 | AssocItemId::FunctionId(m) => { |
306 | let data = db.function_data(m); | 409 | let data = db.function_data(m); |
307 | name.map_or(true, |name| &data.name == name) | 410 | if let Some(name) = name { |
308 | && (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 | ||
309 | } | 428 | } |
310 | AssocItemId::ConstId(c) => { | 429 | AssocItemId::ConstId(c) => { |
311 | let data = db.const_data(c); | 430 | let data = db.const_data(c); |
312 | 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() |
313 | } | 432 | } |
314 | _ => false, | 433 | _ => false, |
315 | } | 434 | } |
316 | } | 435 | } |
317 | 436 | ||
437 | pub(crate) fn inherent_impl_substs( | ||
438 | db: &impl HirDatabase, | ||
439 | impl_id: ImplId, | ||
440 | self_ty: &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 | hir_def::ContainerId::TraitId(_) => Substs::build_for_def(db, function_id) | ||
455 | .push(self_ty.value.clone()) | ||
456 | .fill_with_unknown() | ||
457 | .build(), | ||
458 | hir_def::ContainerId::ImplId(impl_id) => inherent_impl_substs(db, impl_id, &self_ty.value)?, | ||
459 | hir_def::ContainerId::ModuleId(_) => unreachable!(), | ||
460 | }; | ||
461 | let sig = db.callable_item_signature(function_id.into()); | ||
462 | Some(sig.params()[0].clone().subst(&substs)) | ||
463 | } | ||
464 | |||
318 | pub fn implements_trait( | 465 | pub fn implements_trait( |
319 | ty: &Canonical<Ty>, | 466 | ty: &Canonical<Ty>, |
320 | db: &impl HirDatabase, | 467 | db: &impl HirDatabase, |