diff options
Diffstat (limited to 'crates/hir_ty/src/infer/path.rs')
-rw-r--r-- | crates/hir_ty/src/infer/path.rs | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/crates/hir_ty/src/infer/path.rs b/crates/hir_ty/src/infer/path.rs new file mode 100644 index 000000000..80d7ed10e --- /dev/null +++ b/crates/hir_ty/src/infer/path.rs | |||
@@ -0,0 +1,287 @@ | |||
1 | //! Path expression resolution. | ||
2 | |||
3 | use std::iter; | ||
4 | |||
5 | use hir_def::{ | ||
6 | path::{Path, PathSegment}, | ||
7 | resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs}, | ||
8 | AdtId, AssocContainerId, AssocItemId, EnumVariantId, Lookup, | ||
9 | }; | ||
10 | use hir_expand::name::Name; | ||
11 | |||
12 | use crate::{method_resolution, Substs, Ty, ValueTyDefId}; | ||
13 | |||
14 | use super::{ExprOrPatId, InferenceContext, TraitRef}; | ||
15 | |||
16 | impl<'a> InferenceContext<'a> { | ||
17 | pub(super) fn infer_path( | ||
18 | &mut self, | ||
19 | resolver: &Resolver, | ||
20 | path: &Path, | ||
21 | id: ExprOrPatId, | ||
22 | ) -> Option<Ty> { | ||
23 | let ty = self.resolve_value_path(resolver, path, id)?; | ||
24 | let ty = self.insert_type_vars(ty); | ||
25 | let ty = self.normalize_associated_types_in(ty); | ||
26 | Some(ty) | ||
27 | } | ||
28 | |||
29 | fn resolve_value_path( | ||
30 | &mut self, | ||
31 | resolver: &Resolver, | ||
32 | path: &Path, | ||
33 | id: ExprOrPatId, | ||
34 | ) -> Option<Ty> { | ||
35 | let (value, self_subst) = if let Some(type_ref) = path.type_anchor() { | ||
36 | if path.segments().is_empty() { | ||
37 | // This can't actually happen syntax-wise | ||
38 | return None; | ||
39 | } | ||
40 | let ty = self.make_ty(type_ref); | ||
41 | let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1); | ||
42 | let ctx = crate::lower::TyLoweringContext::new(self.db, &resolver); | ||
43 | let (ty, _) = Ty::from_type_relative_path(&ctx, ty, None, remaining_segments_for_ty); | ||
44 | self.resolve_ty_assoc_item( | ||
45 | ty, | ||
46 | &path.segments().last().expect("path had at least one segment").name, | ||
47 | id, | ||
48 | )? | ||
49 | } else { | ||
50 | let value_or_partial = | ||
51 | resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?; | ||
52 | |||
53 | match value_or_partial { | ||
54 | ResolveValueResult::ValueNs(it) => (it, None), | ||
55 | ResolveValueResult::Partial(def, remaining_index) => { | ||
56 | self.resolve_assoc_item(def, path, remaining_index, id)? | ||
57 | } | ||
58 | } | ||
59 | }; | ||
60 | |||
61 | let typable: ValueTyDefId = match value { | ||
62 | ValueNs::LocalBinding(pat) => { | ||
63 | let ty = self.result.type_of_pat.get(pat)?.clone(); | ||
64 | let ty = self.resolve_ty_as_possible(ty); | ||
65 | return Some(ty); | ||
66 | } | ||
67 | ValueNs::FunctionId(it) => it.into(), | ||
68 | ValueNs::ConstId(it) => it.into(), | ||
69 | ValueNs::StaticId(it) => it.into(), | ||
70 | ValueNs::StructId(it) => { | ||
71 | self.write_variant_resolution(id, it.into()); | ||
72 | |||
73 | it.into() | ||
74 | } | ||
75 | ValueNs::EnumVariantId(it) => { | ||
76 | self.write_variant_resolution(id, it.into()); | ||
77 | |||
78 | it.into() | ||
79 | } | ||
80 | ValueNs::ImplSelf(impl_id) => { | ||
81 | let generics = crate::utils::generics(self.db.upcast(), impl_id.into()); | ||
82 | let substs = Substs::type_params_for_generics(&generics); | ||
83 | let ty = self.db.impl_self_ty(impl_id).subst(&substs); | ||
84 | if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() { | ||
85 | let ty = self.db.value_ty(struct_id.into()).subst(&substs); | ||
86 | return Some(ty); | ||
87 | } else { | ||
88 | // FIXME: diagnostic, invalid Self reference | ||
89 | return None; | ||
90 | } | ||
91 | } | ||
92 | }; | ||
93 | |||
94 | let ty = self.db.value_ty(typable); | ||
95 | // self_subst is just for the parent | ||
96 | let parent_substs = self_subst.unwrap_or_else(Substs::empty); | ||
97 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); | ||
98 | let substs = Ty::substs_from_path(&ctx, path, typable, true); | ||
99 | let full_substs = Substs::builder(substs.len()) | ||
100 | .use_parent_substs(&parent_substs) | ||
101 | .fill(substs.0[parent_substs.len()..].iter().cloned()) | ||
102 | .build(); | ||
103 | let ty = ty.subst(&full_substs); | ||
104 | Some(ty) | ||
105 | } | ||
106 | |||
107 | fn resolve_assoc_item( | ||
108 | &mut self, | ||
109 | def: TypeNs, | ||
110 | path: &Path, | ||
111 | remaining_index: usize, | ||
112 | id: ExprOrPatId, | ||
113 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
114 | assert!(remaining_index < path.segments().len()); | ||
115 | // there may be more intermediate segments between the resolved one and | ||
116 | // the end. Only the last segment needs to be resolved to a value; from | ||
117 | // the segments before that, we need to get either a type or a trait ref. | ||
118 | |||
119 | let resolved_segment = path.segments().get(remaining_index - 1).unwrap(); | ||
120 | let remaining_segments = path.segments().skip(remaining_index); | ||
121 | let is_before_last = remaining_segments.len() == 1; | ||
122 | |||
123 | match (def, is_before_last) { | ||
124 | (TypeNs::TraitId(trait_), true) => { | ||
125 | let segment = | ||
126 | remaining_segments.last().expect("there should be at least one segment here"); | ||
127 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); | ||
128 | let trait_ref = TraitRef::from_resolved_path(&ctx, trait_, resolved_segment, None); | ||
129 | self.resolve_trait_assoc_item(trait_ref, segment, id) | ||
130 | } | ||
131 | (def, _) => { | ||
132 | // Either we already have a type (e.g. `Vec::new`), or we have a | ||
133 | // trait but it's not the last segment, so the next segment | ||
134 | // should resolve to an associated type of that trait (e.g. `<T | ||
135 | // as Iterator>::Item::default`) | ||
136 | let remaining_segments_for_ty = | ||
137 | remaining_segments.take(remaining_segments.len() - 1); | ||
138 | let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver); | ||
139 | let (ty, _) = Ty::from_partly_resolved_hir_path( | ||
140 | &ctx, | ||
141 | def, | ||
142 | resolved_segment, | ||
143 | remaining_segments_for_ty, | ||
144 | true, | ||
145 | ); | ||
146 | if let Ty::Unknown = ty { | ||
147 | return None; | ||
148 | } | ||
149 | |||
150 | let ty = self.insert_type_vars(ty); | ||
151 | let ty = self.normalize_associated_types_in(ty); | ||
152 | |||
153 | let segment = | ||
154 | remaining_segments.last().expect("there should be at least one segment here"); | ||
155 | |||
156 | self.resolve_ty_assoc_item(ty, &segment.name, id) | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
161 | fn resolve_trait_assoc_item( | ||
162 | &mut self, | ||
163 | trait_ref: TraitRef, | ||
164 | segment: PathSegment<'_>, | ||
165 | id: ExprOrPatId, | ||
166 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
167 | let trait_ = trait_ref.trait_; | ||
168 | let item = | ||
169 | self.db.trait_data(trait_).items.iter().map(|(_name, id)| (*id)).find_map(|item| { | ||
170 | match item { | ||
171 | AssocItemId::FunctionId(func) => { | ||
172 | if segment.name == &self.db.function_data(func).name { | ||
173 | Some(AssocItemId::FunctionId(func)) | ||
174 | } else { | ||
175 | None | ||
176 | } | ||
177 | } | ||
178 | |||
179 | AssocItemId::ConstId(konst) => { | ||
180 | if self | ||
181 | .db | ||
182 | .const_data(konst) | ||
183 | .name | ||
184 | .as_ref() | ||
185 | .map_or(false, |n| n == segment.name) | ||
186 | { | ||
187 | Some(AssocItemId::ConstId(konst)) | ||
188 | } else { | ||
189 | None | ||
190 | } | ||
191 | } | ||
192 | AssocItemId::TypeAliasId(_) => None, | ||
193 | } | ||
194 | })?; | ||
195 | let def = match item { | ||
196 | AssocItemId::FunctionId(f) => ValueNs::FunctionId(f), | ||
197 | AssocItemId::ConstId(c) => ValueNs::ConstId(c), | ||
198 | AssocItemId::TypeAliasId(_) => unreachable!(), | ||
199 | }; | ||
200 | |||
201 | self.write_assoc_resolution(id, item); | ||
202 | Some((def, Some(trait_ref.substs))) | ||
203 | } | ||
204 | |||
205 | fn resolve_ty_assoc_item( | ||
206 | &mut self, | ||
207 | ty: Ty, | ||
208 | name: &Name, | ||
209 | id: ExprOrPatId, | ||
210 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
211 | if let Ty::Unknown = ty { | ||
212 | return None; | ||
213 | } | ||
214 | |||
215 | if let Some(result) = self.resolve_enum_variant_on_ty(&ty, name, id) { | ||
216 | return Some(result); | ||
217 | } | ||
218 | |||
219 | let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone()); | ||
220 | let krate = self.resolver.krate()?; | ||
221 | let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast()); | ||
222 | |||
223 | method_resolution::iterate_method_candidates( | ||
224 | &canonical_ty.value, | ||
225 | self.db, | ||
226 | self.trait_env.clone(), | ||
227 | krate, | ||
228 | &traits_in_scope, | ||
229 | Some(name), | ||
230 | method_resolution::LookupMode::Path, | ||
231 | move |_ty, item| { | ||
232 | let (def, container) = match item { | ||
233 | AssocItemId::FunctionId(f) => { | ||
234 | (ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container) | ||
235 | } | ||
236 | AssocItemId::ConstId(c) => { | ||
237 | (ValueNs::ConstId(c), c.lookup(self.db.upcast()).container) | ||
238 | } | ||
239 | AssocItemId::TypeAliasId(_) => unreachable!(), | ||
240 | }; | ||
241 | let substs = match container { | ||
242 | AssocContainerId::ImplId(impl_id) => { | ||
243 | let impl_substs = Substs::build_for_def(self.db, impl_id) | ||
244 | .fill(iter::repeat_with(|| self.table.new_type_var())) | ||
245 | .build(); | ||
246 | let impl_self_ty = self.db.impl_self_ty(impl_id).subst(&impl_substs); | ||
247 | self.unify(&impl_self_ty, &ty); | ||
248 | Some(impl_substs) | ||
249 | } | ||
250 | AssocContainerId::TraitId(trait_) => { | ||
251 | // we're picking this method | ||
252 | let trait_substs = Substs::build_for_def(self.db, trait_) | ||
253 | .push(ty.clone()) | ||
254 | .fill(std::iter::repeat_with(|| self.table.new_type_var())) | ||
255 | .build(); | ||
256 | self.obligations.push(super::Obligation::Trait(TraitRef { | ||
257 | trait_, | ||
258 | substs: trait_substs.clone(), | ||
259 | })); | ||
260 | Some(trait_substs) | ||
261 | } | ||
262 | AssocContainerId::ContainerId(_) => None, | ||
263 | }; | ||
264 | |||
265 | self.write_assoc_resolution(id, item); | ||
266 | Some((def, substs)) | ||
267 | }, | ||
268 | ) | ||
269 | } | ||
270 | |||
271 | fn resolve_enum_variant_on_ty( | ||
272 | &mut self, | ||
273 | ty: &Ty, | ||
274 | name: &Name, | ||
275 | id: ExprOrPatId, | ||
276 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
277 | let (enum_id, subst) = match ty.as_adt() { | ||
278 | Some((AdtId::EnumId(e), subst)) => (e, subst), | ||
279 | _ => return None, | ||
280 | }; | ||
281 | let enum_data = self.db.enum_data(enum_id); | ||
282 | let local_id = enum_data.variant(name)?; | ||
283 | let variant = EnumVariantId { parent: enum_id, local_id }; | ||
284 | self.write_variant_resolution(id, variant.into()); | ||
285 | Some((ValueNs::EnumVariantId(variant), Some(subst.clone()))) | ||
286 | } | ||
287 | } | ||