aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty/src/infer/path.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_ty/src/infer/path.rs')
-rw-r--r--crates/ra_hir_ty/src/infer/path.rs268
1 files changed, 268 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..14be66836
--- /dev/null
+++ b/crates/ra_hir_ty/src/infer/path.rs
@@ -0,0 +1,268 @@
1//! Path expression resolution.
2
3use hir_def::{
4 path::{Path, PathKind, PathSegment},
5 resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs},
6 AssocItemId, ContainerId, Lookup,
7};
8use hir_expand::name::Name;
9
10use crate::{db::HirDatabase, method_resolution, Substs, Ty, TypeWalk, ValueTyDefId};
11
12use super::{ExprOrPatId, InferenceContext, TraitRef};
13
14impl<'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 self_ty = self.db.impl_ty(impl_id).self_type().clone();
248 let self_ty_substs = self_ty.substs()?;
249 let actual_substs = actual_def_ty.substs()?;
250
251 let mut new_substs = vec![Ty::Unknown; gen.count_parent_params()];
252
253 // The following code *link up* the function actual parma type
254 // and impl_block type param index
255 self_ty_substs.iter().zip(actual_substs.iter()).for_each(|(param, pty)| {
256 if let Ty::Param { idx, .. } = param {
257 if let Some(s) = new_substs.get_mut(*idx as usize) {
258 *s = pty.clone();
259 }
260 }
261 });
262
263 Some(Substs(new_substs.into()))
264 } else {
265 None
266 }
267 }
268}