aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty/method_resolution.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-11-27 18:27:45 +0000
committerGitHub <[email protected]>2019-11-27 18:27:45 +0000
commit4946169a96f3d442f463724af481fdb760381ccb (patch)
treeb68c3da4bb47e0a285968319bfb33b44cbfd5546 /crates/ra_hir/src/ty/method_resolution.rs
parent2798beeeb05ab0e71773a2ed51b7b0c90bf6b06a (diff)
parent47ec2ceb12df756b3482ddd2b1947e4b38f23706 (diff)
Merge #2429
2429: Move type inference to a separate crate r=matklad a=matklad Co-authored-by: Aleksey Kladov <[email protected]>
Diffstat (limited to 'crates/ra_hir/src/ty/method_resolution.rs')
-rw-r--r--crates/ra_hir/src/ty/method_resolution.rs362
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.
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 ty::primitive::{FloatBitness, Uncertain},
20 ty::{utils::all_super_traits, Ty, TypeCtor},
21};
22
23use 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)]
27pub enum TyFingerprint {
28 Apply(TypeCtor),
29}
30
31impl 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)]
44pub struct CrateImplBlocks {
45 impls: FxHashMap<TyFingerprint, Vec<ImplId>>,
46 impls_by_trait: FxHashMap<TraitId, Vec<ImplId>>,
47}
48
49impl 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
99impl 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).
151pub(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)]
167pub 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?
179pub(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
235fn 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
281fn 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
306fn 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
326pub(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.
347fn 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}