diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2019-11-27 18:27:45 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2019-11-27 18:27:45 +0000 |
commit | 4946169a96f3d442f463724af481fdb760381ccb (patch) | |
tree | b68c3da4bb47e0a285968319bfb33b44cbfd5546 /crates/ra_hir_ty/src/infer/path.rs | |
parent | 2798beeeb05ab0e71773a2ed51b7b0c90bf6b06a (diff) | |
parent | 47ec2ceb12df756b3482ddd2b1947e4b38f23706 (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_ty/src/infer/path.rs')
-rw-r--r-- | crates/ra_hir_ty/src/infer/path.rs | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/crates/ra_hir_ty/src/infer/path.rs b/crates/ra_hir_ty/src/infer/path.rs new file mode 100644 index 000000000..e6676e1aa --- /dev/null +++ b/crates/ra_hir_ty/src/infer/path.rs | |||
@@ -0,0 +1,270 @@ | |||
1 | //! Path expression resolution. | ||
2 | |||
3 | use hir_def::{ | ||
4 | path::{Path, PathKind, PathSegment}, | ||
5 | resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, | ||
6 | AssocItemId, ContainerId, Lookup, | ||
7 | }; | ||
8 | use hir_expand::name::Name; | ||
9 | |||
10 | use crate::{db::HirDatabase, method_resolution, Substs, Ty, TypeWalk, ValueTyDefId}; | ||
11 | |||
12 | use super::{ExprOrPatId, InferenceContext, TraitRef}; | ||
13 | |||
14 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { | ||
15 | pub(super) fn infer_path( | ||
16 | &mut self, | ||
17 | resolver: &Resolver, | ||
18 | path: &Path, | ||
19 | id: ExprOrPatId, | ||
20 | ) -> Option<Ty> { | ||
21 | let ty = self.resolve_value_path(resolver, path, id)?; | ||
22 | let ty = self.insert_type_vars(ty); | ||
23 | let ty = self.normalize_associated_types_in(ty); | ||
24 | Some(ty) | ||
25 | } | ||
26 | |||
27 | fn resolve_value_path( | ||
28 | &mut self, | ||
29 | resolver: &Resolver, | ||
30 | path: &Path, | ||
31 | id: ExprOrPatId, | ||
32 | ) -> Option<Ty> { | ||
33 | let (value, self_subst) = if let PathKind::Type(type_ref) = &path.kind { | ||
34 | if path.segments.is_empty() { | ||
35 | // This can't actually happen syntax-wise | ||
36 | return None; | ||
37 | } | ||
38 | let ty = self.make_ty(type_ref); | ||
39 | let remaining_segments_for_ty = &path.segments[..path.segments.len() - 1]; | ||
40 | let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty); | ||
41 | self.resolve_ty_assoc_item( | ||
42 | ty, | ||
43 | &path.segments.last().expect("path had at least one segment").name, | ||
44 | id, | ||
45 | )? | ||
46 | } else { | ||
47 | let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?; | ||
48 | |||
49 | match value_or_partial { | ||
50 | ResolveValueResult::ValueNs(it) => (it, None), | ||
51 | ResolveValueResult::Partial(def, remaining_index) => { | ||
52 | self.resolve_assoc_item(def, path, remaining_index, id)? | ||
53 | } | ||
54 | } | ||
55 | }; | ||
56 | |||
57 | let typable: ValueTyDefId = match value { | ||
58 | ValueNs::LocalBinding(pat) => { | ||
59 | let ty = self.result.type_of_pat.get(pat)?.clone(); | ||
60 | let ty = self.resolve_ty_as_possible(&mut vec![], ty); | ||
61 | return Some(ty); | ||
62 | } | ||
63 | ValueNs::FunctionId(it) => it.into(), | ||
64 | ValueNs::ConstId(it) => it.into(), | ||
65 | ValueNs::StaticId(it) => it.into(), | ||
66 | ValueNs::StructId(it) => it.into(), | ||
67 | ValueNs::EnumVariantId(it) => it.into(), | ||
68 | }; | ||
69 | |||
70 | let mut ty = self.db.value_ty(typable); | ||
71 | if let Some(self_subst) = self_subst { | ||
72 | ty = ty.subst(&self_subst); | ||
73 | } | ||
74 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); | ||
75 | let ty = ty.subst(&substs); | ||
76 | Some(ty) | ||
77 | } | ||
78 | |||
79 | fn resolve_assoc_item( | ||
80 | &mut self, | ||
81 | def: TypeNs, | ||
82 | path: &Path, | ||
83 | remaining_index: usize, | ||
84 | id: ExprOrPatId, | ||
85 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
86 | assert!(remaining_index < path.segments.len()); | ||
87 | // there may be more intermediate segments between the resolved one and | ||
88 | // the end. Only the last segment needs to be resolved to a value; from | ||
89 | // the segments before that, we need to get either a type or a trait ref. | ||
90 | |||
91 | let resolved_segment = &path.segments[remaining_index - 1]; | ||
92 | let remaining_segments = &path.segments[remaining_index..]; | ||
93 | let is_before_last = remaining_segments.len() == 1; | ||
94 | |||
95 | match (def, is_before_last) { | ||
96 | (TypeNs::TraitId(trait_), true) => { | ||
97 | let segment = | ||
98 | remaining_segments.last().expect("there should be at least one segment here"); | ||
99 | let trait_ref = TraitRef::from_resolved_path( | ||
100 | self.db, | ||
101 | &self.resolver, | ||
102 | trait_.into(), | ||
103 | resolved_segment, | ||
104 | None, | ||
105 | ); | ||
106 | self.resolve_trait_assoc_item(trait_ref, segment, id) | ||
107 | } | ||
108 | (def, _) => { | ||
109 | // Either we already have a type (e.g. `Vec::new`), or we have a | ||
110 | // trait but it's not the last segment, so the next segment | ||
111 | // should resolve to an associated type of that trait (e.g. `<T | ||
112 | // as Iterator>::Item::default`) | ||
113 | let remaining_segments_for_ty = &remaining_segments[..remaining_segments.len() - 1]; | ||
114 | let ty = Ty::from_partly_resolved_hir_path( | ||
115 | self.db, | ||
116 | &self.resolver, | ||
117 | def, | ||
118 | resolved_segment, | ||
119 | remaining_segments_for_ty, | ||
120 | ); | ||
121 | if let Ty::Unknown = ty { | ||
122 | return None; | ||
123 | } | ||
124 | |||
125 | let ty = self.insert_type_vars(ty); | ||
126 | let ty = self.normalize_associated_types_in(ty); | ||
127 | |||
128 | let segment = | ||
129 | remaining_segments.last().expect("there should be at least one segment here"); | ||
130 | |||
131 | self.resolve_ty_assoc_item(ty, &segment.name, id) | ||
132 | } | ||
133 | } | ||
134 | } | ||
135 | |||
136 | fn resolve_trait_assoc_item( | ||
137 | &mut self, | ||
138 | trait_ref: TraitRef, | ||
139 | segment: &PathSegment, | ||
140 | id: ExprOrPatId, | ||
141 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
142 | let trait_ = trait_ref.trait_; | ||
143 | let item = self | ||
144 | .db | ||
145 | .trait_data(trait_) | ||
146 | .items | ||
147 | .iter() | ||
148 | .map(|(_name, id)| (*id).into()) | ||
149 | .find_map(|item| match item { | ||
150 | AssocItemId::FunctionId(func) => { | ||
151 | if segment.name == self.db.function_data(func).name { | ||
152 | Some(AssocItemId::FunctionId(func)) | ||
153 | } else { | ||
154 | None | ||
155 | } | ||
156 | } | ||
157 | |||
158 | AssocItemId::ConstId(konst) => { | ||
159 | if self.db.const_data(konst).name.as_ref().map_or(false, |n| n == &segment.name) | ||
160 | { | ||
161 | Some(AssocItemId::ConstId(konst)) | ||
162 | } else { | ||
163 | None | ||
164 | } | ||
165 | } | ||
166 | AssocItemId::TypeAliasId(_) => None, | ||
167 | })?; | ||
168 | let def = match item { | ||
169 | AssocItemId::FunctionId(f) => ValueNs::FunctionId(f), | ||
170 | AssocItemId::ConstId(c) => ValueNs::ConstId(c), | ||
171 | AssocItemId::TypeAliasId(_) => unreachable!(), | ||
172 | }; | ||
173 | let substs = Substs::build_for_def(self.db, item) | ||
174 | .use_parent_substs(&trait_ref.substs) | ||
175 | .fill_with_params() | ||
176 | .build(); | ||
177 | |||
178 | self.write_assoc_resolution(id, item); | ||
179 | Some((def, Some(substs))) | ||
180 | } | ||
181 | |||
182 | fn resolve_ty_assoc_item( | ||
183 | &mut self, | ||
184 | ty: Ty, | ||
185 | name: &Name, | ||
186 | id: ExprOrPatId, | ||
187 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
188 | if let Ty::Unknown = ty { | ||
189 | return None; | ||
190 | } | ||
191 | |||
192 | let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); | ||
193 | |||
194 | method_resolution::iterate_method_candidates( | ||
195 | &canonical_ty.value, | ||
196 | self.db, | ||
197 | &self.resolver.clone(), | ||
198 | Some(name), | ||
199 | method_resolution::LookupMode::Path, | ||
200 | move |_ty, item| { | ||
201 | let (def, container) = match item { | ||
202 | AssocItemId::FunctionId(f) => { | ||
203 | (ValueNs::FunctionId(f), f.lookup(self.db).container) | ||
204 | } | ||
205 | AssocItemId::ConstId(c) => (ValueNs::ConstId(c), c.lookup(self.db).container), | ||
206 | AssocItemId::TypeAliasId(_) => unreachable!(), | ||
207 | }; | ||
208 | let substs = match container { | ||
209 | ContainerId::ImplId(_) => self.find_self_types(&def, ty.clone()), | ||
210 | ContainerId::TraitId(trait_) => { | ||
211 | // we're picking this method | ||
212 | let trait_substs = Substs::build_for_def(self.db, trait_) | ||
213 | .push(ty.clone()) | ||
214 | .fill(std::iter::repeat_with(|| self.new_type_var())) | ||
215 | .build(); | ||
216 | let substs = Substs::build_for_def(self.db, item) | ||
217 | .use_parent_substs(&trait_substs) | ||
218 | .fill_with_params() | ||
219 | .build(); | ||
220 | self.obligations.push(super::Obligation::Trait(TraitRef { | ||
221 | trait_, | ||
222 | substs: trait_substs, | ||
223 | })); | ||
224 | Some(substs) | ||
225 | } | ||
226 | ContainerId::ModuleId(_) => None, | ||
227 | }; | ||
228 | |||
229 | self.write_assoc_resolution(id, item.into()); | ||
230 | Some((def, substs)) | ||
231 | }, | ||
232 | ) | ||
233 | } | ||
234 | |||
235 | fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> { | ||
236 | if let ValueNs::FunctionId(func) = *def { | ||
237 | // We only do the infer if parent has generic params | ||
238 | let gen = self.db.generic_params(func.into()); | ||
239 | if gen.count_parent_params() == 0 { | ||
240 | return None; | ||
241 | } | ||
242 | |||
243 | let impl_id = match func.lookup(self.db).container { | ||
244 | ContainerId::ImplId(it) => it, | ||
245 | _ => return None, | ||
246 | }; | ||
247 | let resolver = impl_id.resolver(self.db); | ||
248 | let impl_data = self.db.impl_data(impl_id); | ||
249 | let impl_block = Ty::from_hir(self.db, &resolver, &impl_data.target_type); | ||
250 | let impl_block_substs = impl_block.substs()?; | ||
251 | let actual_substs = actual_def_ty.substs()?; | ||
252 | |||
253 | let mut new_substs = vec![Ty::Unknown; gen.count_parent_params()]; | ||
254 | |||
255 | // The following code *link up* the function actual parma type | ||
256 | // and impl_block type param index | ||
257 | impl_block_substs.iter().zip(actual_substs.iter()).for_each(|(param, pty)| { | ||
258 | if let Ty::Param { idx, .. } = param { | ||
259 | if let Some(s) = new_substs.get_mut(*idx as usize) { | ||
260 | *s = pty.clone(); | ||
261 | } | ||
262 | } | ||
263 | }); | ||
264 | |||
265 | Some(Substs(new_substs.into())) | ||
266 | } else { | ||
267 | None | ||
268 | } | ||
269 | } | ||
270 | } | ||