diff options
Diffstat (limited to 'crates/ra_hir_ty/src/method_resolution.rs')
-rw-r--r-- | crates/ra_hir_ty/src/method_resolution.rs | 363 |
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. | ||
5 | use std::sync::Arc; | ||
6 | |||
7 | use arrayvec::ArrayVec; | ||
8 | use hir_def::{ | ||
9 | lang_item::LangItemTarget, resolver::HasResolver, resolver::Resolver, type_ref::Mutability, | ||
10 | AssocItemId, AstItemDef, FunctionId, HasModule, ImplId, TraitId, | ||
11 | }; | ||
12 | use hir_expand::name::Name; | ||
13 | use ra_db::CrateId; | ||
14 | use ra_prof::profile; | ||
15 | use rustc_hash::FxHashMap; | ||
16 | |||
17 | use crate::{ | ||
18 | db::HirDatabase, | ||
19 | primitive::{FloatBitness, Uncertain}, | ||
20 | utils::all_super_traits, | ||
21 | Ty, TypeCtor, | ||
22 | }; | ||
23 | |||
24 | use 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)] | ||
28 | pub enum TyFingerprint { | ||
29 | Apply(TypeCtor), | ||
30 | } | ||
31 | |||
32 | impl 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)] | ||
45 | pub struct CrateImplBlocks { | ||
46 | impls: FxHashMap<TyFingerprint, Vec<ImplId>>, | ||
47 | impls_by_trait: FxHashMap<TraitId, Vec<ImplId>>, | ||
48 | } | ||
49 | |||
50 | impl 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 | |||
100 | impl 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). | ||
152 | pub(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)] | ||
168 | pub 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? | ||
180 | pub 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 | |||
236 | fn 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 | |||
282 | fn 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 | |||
307 | fn 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 | |||
327 | pub 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. | ||
348 | fn 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 | } | ||