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