aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty/src/method_resolution.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_ty/src/method_resolution.rs')
-rw-r--r--crates/ra_hir_ty/src/method_resolution.rs363
1 files changed, 363 insertions, 0 deletions
diff --git a/crates/ra_hir_ty/src/method_resolution.rs b/crates/ra_hir_ty/src/method_resolution.rs
new file mode 100644
index 000000000..53c541eb8
--- /dev/null
+++ b/crates/ra_hir_ty/src/method_resolution.rs
@@ -0,0 +1,363 @@
1//! This module is concerned with finding methods that a given type provides.
2//! For details about how this works in rustc, see the method lookup page in the
3//! [rustc guide](https://rust-lang.github.io/rustc-guide/method-lookup.html)
4//! and the corresponding code mostly in librustc_typeck/check/method/probe.rs.
5use std::sync::Arc;
6
7use arrayvec::ArrayVec;
8use hir_def::{
9 lang_item::LangItemTarget, resolver::HasResolver, resolver::Resolver, type_ref::Mutability,
10 AssocItemId, AstItemDef, FunctionId, HasModule, ImplId, TraitId,
11};
12use hir_expand::name::Name;
13use ra_db::CrateId;
14use ra_prof::profile;
15use rustc_hash::FxHashMap;
16
17use crate::{
18 db::HirDatabase,
19 primitive::{FloatBitness, Uncertain},
20 utils::all_super_traits,
21 Ty, TypeCtor,
22};
23
24use super::{autoderef, Canonical, InEnvironment, TraitEnvironment, TraitRef};
25
26/// This is used as a key for indexing impls.
27#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
28pub enum TyFingerprint {
29 Apply(TypeCtor),
30}
31
32impl TyFingerprint {
33 /// Creates a TyFingerprint for looking up an impl. Only certain types can
34 /// have impls: if we have some `struct S`, we can have an `impl S`, but not
35 /// `impl &S`. Hence, this will return `None` for reference types and such.
36 fn for_impl(ty: &Ty) -> Option<TyFingerprint> {
37 match ty {
38 Ty::Apply(a_ty) => Some(TyFingerprint::Apply(a_ty.ctor)),
39 _ => None,
40 }
41 }
42}
43
44#[derive(Debug, PartialEq, Eq)]
45pub struct CrateImplBlocks {
46 impls: FxHashMap<TyFingerprint, Vec<ImplId>>,
47 impls_by_trait: FxHashMap<TraitId, Vec<ImplId>>,
48}
49
50impl CrateImplBlocks {
51 pub(crate) fn impls_in_crate_query(
52 db: &impl HirDatabase,
53 krate: CrateId,
54 ) -> Arc<CrateImplBlocks> {
55 let _p = profile("impls_in_crate_query");
56 let mut res =
57 CrateImplBlocks { impls: FxHashMap::default(), impls_by_trait: FxHashMap::default() };
58
59 let crate_def_map = db.crate_def_map(krate);
60 for (_module_id, module_data) in crate_def_map.modules.iter() {
61 for &impl_id in module_data.impls.iter() {
62 let impl_data = db.impl_data(impl_id);
63 let resolver = impl_id.resolver(db);
64
65 let target_ty = Ty::from_hir(db, &resolver, &impl_data.target_type);
66
67 match &impl_data.target_trait {
68 Some(trait_ref) => {
69 if let Some(tr) =
70 TraitRef::from_hir(db, &resolver, &trait_ref, Some(target_ty))
71 {
72 res.impls_by_trait.entry(tr.trait_).or_default().push(impl_id);
73 }
74 }
75 None => {
76 if let Some(target_ty_fp) = TyFingerprint::for_impl(&target_ty) {
77 res.impls.entry(target_ty_fp).or_default().push(impl_id);
78 }
79 }
80 }
81 }
82 }
83
84 Arc::new(res)
85 }
86 pub fn lookup_impl_blocks(&self, ty: &Ty) -> impl Iterator<Item = ImplId> + '_ {
87 let fingerprint = TyFingerprint::for_impl(ty);
88 fingerprint.and_then(|f| self.impls.get(&f)).into_iter().flatten().copied()
89 }
90
91 pub fn lookup_impl_blocks_for_trait(&self, tr: TraitId) -> impl Iterator<Item = ImplId> + '_ {
92 self.impls_by_trait.get(&tr).into_iter().flatten().copied()
93 }
94
95 pub fn all_impls<'a>(&'a self) -> impl Iterator<Item = ImplId> + 'a {
96 self.impls.values().chain(self.impls_by_trait.values()).flatten().copied()
97 }
98}
99
100impl Ty {
101 pub fn def_crates(
102 &self,
103 db: &impl HirDatabase,
104 cur_crate: CrateId,
105 ) -> Option<ArrayVec<[CrateId; 2]>> {
106 // Types like slice can have inherent impls in several crates, (core and alloc).
107 // The corresponding impls are marked with lang items, so we can use them to find the required crates.
108 macro_rules! lang_item_crate {
109 ($($name:expr),+ $(,)?) => {{
110 let mut v = ArrayVec::<[LangItemTarget; 2]>::new();
111 $(
112 v.extend(db.lang_item(cur_crate, $name.into()));
113 )+
114 v
115 }};
116 }
117
118 let lang_item_targets = match self {
119 Ty::Apply(a_ty) => match a_ty.ctor {
120 TypeCtor::Adt(def_id) => {
121 return Some(std::iter::once(def_id.module(db).krate).collect())
122 }
123 TypeCtor::Bool => lang_item_crate!("bool"),
124 TypeCtor::Char => lang_item_crate!("char"),
125 TypeCtor::Float(Uncertain::Known(f)) => match f.bitness {
126 // There are two lang items: one in libcore (fXX) and one in libstd (fXX_runtime)
127 FloatBitness::X32 => lang_item_crate!("f32", "f32_runtime"),
128 FloatBitness::X64 => lang_item_crate!("f64", "f64_runtime"),
129 },
130 TypeCtor::Int(Uncertain::Known(i)) => lang_item_crate!(i.ty_to_string()),
131 TypeCtor::Str => lang_item_crate!("str_alloc", "str"),
132 TypeCtor::Slice => lang_item_crate!("slice_alloc", "slice"),
133 TypeCtor::RawPtr(Mutability::Shared) => lang_item_crate!("const_ptr"),
134 TypeCtor::RawPtr(Mutability::Mut) => lang_item_crate!("mut_ptr"),
135 _ => return None,
136 },
137 _ => return None,
138 };
139 let res = lang_item_targets
140 .into_iter()
141 .filter_map(|it| match it {
142 LangItemTarget::ImplBlockId(it) => Some(it),
143 _ => None,
144 })
145 .map(|it| it.module(db).krate)
146 .collect();
147 Some(res)
148 }
149}
150/// Look up the method with the given name, returning the actual autoderefed
151/// receiver type (but without autoref applied yet).
152pub(crate) fn lookup_method(
153 ty: &Canonical<Ty>,
154 db: &impl HirDatabase,
155 name: &Name,
156 resolver: &Resolver,
157) -> Option<(Ty, FunctionId)> {
158 iterate_method_candidates(ty, db, resolver, Some(name), LookupMode::MethodCall, |ty, f| match f
159 {
160 AssocItemId::FunctionId(f) => Some((ty.clone(), f)),
161 _ => None,
162 })
163}
164
165/// Whether we're looking up a dotted method call (like `v.len()`) or a path
166/// (like `Vec::new`).
167#[derive(Copy, Clone, Debug, PartialEq, Eq)]
168pub enum LookupMode {
169 /// Looking up a method call like `v.len()`: We only consider candidates
170 /// that have a `self` parameter, and do autoderef.
171 MethodCall,
172 /// Looking up a path like `Vec::new` or `Vec::default`: We consider all
173 /// candidates including associated constants, but don't do autoderef.
174 Path,
175}
176
177// This would be nicer if it just returned an iterator, but that runs into
178// lifetime problems, because we need to borrow temp `CrateImplBlocks`.
179// FIXME add a context type here?
180pub fn iterate_method_candidates<T>(
181 ty: &Canonical<Ty>,
182 db: &impl HirDatabase,
183 resolver: &Resolver,
184 name: Option<&Name>,
185 mode: LookupMode,
186 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
187) -> Option<T> {
188 let krate = resolver.krate()?;
189 match mode {
190 LookupMode::MethodCall => {
191 // For method calls, rust first does any number of autoderef, and then one
192 // autoref (i.e. when the method takes &self or &mut self). We just ignore
193 // the autoref currently -- when we find a method matching the given name,
194 // we assume it fits.
195
196 // Also note that when we've got a receiver like &S, even if the method we
197 // find in the end takes &self, we still do the autoderef step (just as
198 // rustc does an autoderef and then autoref again).
199 let environment = TraitEnvironment::lower(db, resolver);
200 let ty = InEnvironment { value: ty.clone(), environment };
201 for derefed_ty in autoderef::autoderef(db, resolver.krate(), ty) {
202 if let Some(result) =
203 iterate_inherent_methods(&derefed_ty, db, name, mode, krate, &mut callback)
204 {
205 return Some(result);
206 }
207 if let Some(result) = iterate_trait_method_candidates(
208 &derefed_ty,
209 db,
210 resolver,
211 name,
212 mode,
213 &mut callback,
214 ) {
215 return Some(result);
216 }
217 }
218 }
219 LookupMode::Path => {
220 // No autoderef for path lookups
221 if let Some(result) =
222 iterate_inherent_methods(&ty, db, name, mode, krate.into(), &mut callback)
223 {
224 return Some(result);
225 }
226 if let Some(result) =
227 iterate_trait_method_candidates(&ty, db, resolver, name, mode, &mut callback)
228 {
229 return Some(result);
230 }
231 }
232 }
233 None
234}
235
236fn iterate_trait_method_candidates<T>(
237 ty: &Canonical<Ty>,
238 db: &impl HirDatabase,
239 resolver: &Resolver,
240 name: Option<&Name>,
241 mode: LookupMode,
242 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
243) -> Option<T> {
244 let krate = resolver.krate()?;
245 // FIXME: maybe put the trait_env behind a query (need to figure out good input parameters for that)
246 let env = TraitEnvironment::lower(db, resolver);
247 // if ty is `impl Trait` or `dyn Trait`, the trait doesn't need to be in scope
248 let inherent_trait = ty.value.inherent_trait().into_iter();
249 // if we have `T: Trait` in the param env, the trait doesn't need to be in scope
250 let traits_from_env = env
251 .trait_predicates_for_self_ty(&ty.value)
252 .map(|tr| tr.trait_)
253 .flat_map(|t| all_super_traits(db, t));
254 let traits =
255 inherent_trait.chain(traits_from_env).chain(resolver.traits_in_scope(db).into_iter());
256 'traits: for t in traits {
257 let data = db.trait_data(t);
258
259 // we'll be lazy about checking whether the type implements the
260 // trait, but if we find out it doesn't, we'll skip the rest of the
261 // iteration
262 let mut known_implemented = false;
263 for (_name, item) in data.items.iter() {
264 if !is_valid_candidate(db, name, mode, (*item).into()) {
265 continue;
266 }
267 if !known_implemented {
268 let goal = generic_implements_goal(db, env.clone(), t, ty.clone());
269 if db.trait_solve(krate.into(), goal).is_none() {
270 continue 'traits;
271 }
272 }
273 known_implemented = true;
274 if let Some(result) = callback(&ty.value, (*item).into()) {
275 return Some(result);
276 }
277 }
278 }
279 None
280}
281
282fn iterate_inherent_methods<T>(
283 ty: &Canonical<Ty>,
284 db: &impl HirDatabase,
285 name: Option<&Name>,
286 mode: LookupMode,
287 krate: CrateId,
288 mut callback: impl FnMut(&Ty, AssocItemId) -> Option<T>,
289) -> Option<T> {
290 for krate in ty.value.def_crates(db, krate)? {
291 let impls = db.impls_in_crate(krate);
292
293 for impl_block in impls.lookup_impl_blocks(&ty.value) {
294 for &item in db.impl_data(impl_block).items.iter() {
295 if !is_valid_candidate(db, name, mode, item) {
296 continue;
297 }
298 if let Some(result) = callback(&ty.value, item.into()) {
299 return Some(result);
300 }
301 }
302 }
303 }
304 None
305}
306
307fn is_valid_candidate(
308 db: &impl HirDatabase,
309 name: Option<&Name>,
310 mode: LookupMode,
311 item: AssocItemId,
312) -> bool {
313 match item {
314 AssocItemId::FunctionId(m) => {
315 let data = db.function_data(m);
316 name.map_or(true, |name| &data.name == name)
317 && (data.has_self_param || mode == LookupMode::Path)
318 }
319 AssocItemId::ConstId(c) => {
320 let data = db.const_data(c);
321 name.map_or(true, |name| data.name.as_ref() == Some(name)) && (mode == LookupMode::Path)
322 }
323 _ => false,
324 }
325}
326
327pub fn implements_trait(
328 ty: &Canonical<Ty>,
329 db: &impl HirDatabase,
330 resolver: &Resolver,
331 krate: CrateId,
332 trait_: TraitId,
333) -> bool {
334 if ty.value.inherent_trait() == Some(trait_) {
335 // FIXME this is a bit of a hack, since Chalk should say the same thing
336 // anyway, but currently Chalk doesn't implement `dyn/impl Trait` yet
337 return true;
338 }
339 let env = TraitEnvironment::lower(db, resolver);
340 let goal = generic_implements_goal(db, env, trait_, ty.clone());
341 let solution = db.trait_solve(krate.into(), goal);
342
343 solution.is_some()
344}
345
346/// This creates Substs for a trait with the given Self type and type variables
347/// for all other parameters, to query Chalk with it.
348fn generic_implements_goal(
349 db: &impl HirDatabase,
350 env: Arc<TraitEnvironment>,
351 trait_: TraitId,
352 self_ty: Canonical<Ty>,
353) -> Canonical<InEnvironment<super::Obligation>> {
354 let num_vars = self_ty.num_vars;
355 let substs = super::Substs::build_for_def(db, trait_)
356 .push(self_ty.value)
357 .fill_with_bound_vars(num_vars as u32)
358 .build();
359 let num_vars = substs.len() - 1 + self_ty.num_vars;
360 let trait_ref = TraitRef { trait_, substs };
361 let obligation = super::Obligation::Trait(trait_ref);
362 Canonical { num_vars, value: InEnvironment::new(env, obligation) }
363}