diff options
Diffstat (limited to 'crates/ra_hir/src/ty/infer')
-rw-r--r-- | crates/ra_hir/src/ty/infer/path.rs | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/crates/ra_hir/src/ty/infer/path.rs b/crates/ra_hir/src/ty/infer/path.rs new file mode 100644 index 000000000..54aae4f0c --- /dev/null +++ b/crates/ra_hir/src/ty/infer/path.rs | |||
@@ -0,0 +1,195 @@ | |||
1 | //! Path expression resolution. | ||
2 | |||
3 | use super::{ExprOrPatId, InferenceContext}; | ||
4 | use crate::{ | ||
5 | db::HirDatabase, | ||
6 | resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs}, | ||
7 | ty::{Substs, Ty, TypableDef, TypeWalk}, | ||
8 | AssocItem, HasGenericParams, Namespace, Path, | ||
9 | }; | ||
10 | |||
11 | impl<'a, D: HirDatabase> InferenceContext<'a, D> { | ||
12 | pub(super) fn infer_path( | ||
13 | &mut self, | ||
14 | resolver: &Resolver, | ||
15 | path: &Path, | ||
16 | id: ExprOrPatId, | ||
17 | ) -> Option<Ty> { | ||
18 | let ty = self.resolve_value_path(resolver, path, id)?; | ||
19 | let ty = self.insert_type_vars(ty); | ||
20 | let ty = self.normalize_associated_types_in(ty); | ||
21 | Some(ty) | ||
22 | } | ||
23 | |||
24 | fn resolve_value_path( | ||
25 | &mut self, | ||
26 | resolver: &Resolver, | ||
27 | path: &Path, | ||
28 | id: ExprOrPatId, | ||
29 | ) -> Option<Ty> { | ||
30 | let (value, self_subst) = if let crate::PathKind::Type(type_ref) = &path.kind { | ||
31 | if path.segments.is_empty() { | ||
32 | // This can't actually happen syntax-wise | ||
33 | return None; | ||
34 | } | ||
35 | let ty = self.make_ty(type_ref); | ||
36 | let remaining_segments_for_ty = &path.segments[..path.segments.len() - 1]; | ||
37 | let ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty); | ||
38 | self.resolve_ty_assoc_item( | ||
39 | ty, | ||
40 | path.segments.last().expect("path had at least one segment"), | ||
41 | id, | ||
42 | )? | ||
43 | } else { | ||
44 | let value_or_partial = resolver.resolve_path_in_value_ns(self.db, &path)?; | ||
45 | |||
46 | match value_or_partial { | ||
47 | ResolveValueResult::ValueNs(it) => (it, None), | ||
48 | ResolveValueResult::Partial(def, remaining_index) => { | ||
49 | self.resolve_assoc_item(def, path, remaining_index, id)? | ||
50 | } | ||
51 | } | ||
52 | }; | ||
53 | |||
54 | let typable: TypableDef = match value { | ||
55 | ValueNs::LocalBinding(pat) => { | ||
56 | let ty = self.result.type_of_pat.get(pat)?.clone(); | ||
57 | let ty = self.resolve_ty_as_possible(&mut vec![], ty); | ||
58 | return Some(ty); | ||
59 | } | ||
60 | ValueNs::Function(it) => it.into(), | ||
61 | ValueNs::Const(it) => it.into(), | ||
62 | ValueNs::Static(it) => it.into(), | ||
63 | ValueNs::Struct(it) => it.into(), | ||
64 | ValueNs::EnumVariant(it) => it.into(), | ||
65 | }; | ||
66 | |||
67 | let mut ty = self.db.type_for_def(typable, Namespace::Values); | ||
68 | if let Some(self_subst) = self_subst { | ||
69 | ty = ty.subst(&self_subst); | ||
70 | } | ||
71 | |||
72 | let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable); | ||
73 | let ty = ty.subst(&substs); | ||
74 | Some(ty) | ||
75 | } | ||
76 | |||
77 | fn resolve_assoc_item( | ||
78 | &mut self, | ||
79 | def: TypeNs, | ||
80 | path: &Path, | ||
81 | remaining_index: usize, | ||
82 | id: ExprOrPatId, | ||
83 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
84 | assert!(remaining_index < path.segments.len()); | ||
85 | // there may be more intermediate segments between the resolved one and | ||
86 | // the end. Only the last segment needs to be resolved to a value; from | ||
87 | // the segments before that, we need to get either a type or a trait ref. | ||
88 | |||
89 | let resolved_segment = &path.segments[remaining_index - 1]; | ||
90 | let remaining_segments = &path.segments[remaining_index..]; | ||
91 | let is_before_last = remaining_segments.len() == 1; | ||
92 | |||
93 | match (def, is_before_last) { | ||
94 | (TypeNs::Trait(_trait), true) => { | ||
95 | // FIXME Associated item of trait, e.g. `Default::default` | ||
96 | None | ||
97 | } | ||
98 | (def, _) => { | ||
99 | // Either we already have a type (e.g. `Vec::new`), or we have a | ||
100 | // trait but it's not the last segment, so the next segment | ||
101 | // should resolve to an associated type of that trait (e.g. `<T | ||
102 | // as Iterator>::Item::default`) | ||
103 | let remaining_segments_for_ty = &remaining_segments[..remaining_segments.len() - 1]; | ||
104 | let ty = Ty::from_partly_resolved_hir_path( | ||
105 | self.db, | ||
106 | &self.resolver, | ||
107 | def, | ||
108 | resolved_segment, | ||
109 | remaining_segments_for_ty, | ||
110 | ); | ||
111 | if let Ty::Unknown = ty { | ||
112 | return None; | ||
113 | } | ||
114 | |||
115 | let segment = | ||
116 | remaining_segments.last().expect("there should be at least one segment here"); | ||
117 | |||
118 | self.resolve_ty_assoc_item(ty, segment, id) | ||
119 | } | ||
120 | } | ||
121 | } | ||
122 | |||
123 | fn resolve_ty_assoc_item( | ||
124 | &mut self, | ||
125 | ty: Ty, | ||
126 | segment: &crate::path::PathSegment, | ||
127 | id: ExprOrPatId, | ||
128 | ) -> Option<(ValueNs, Option<Substs>)> { | ||
129 | if let Ty::Unknown = ty { | ||
130 | return None; | ||
131 | } | ||
132 | |||
133 | let krate = self.resolver.krate()?; | ||
134 | |||
135 | // Find impl | ||
136 | // FIXME: consider trait candidates | ||
137 | let item = ty.clone().iterate_impl_items(self.db, krate, |item| match item { | ||
138 | AssocItem::Function(func) => { | ||
139 | if segment.name == func.name(self.db) { | ||
140 | Some(AssocItem::Function(func)) | ||
141 | } else { | ||
142 | None | ||
143 | } | ||
144 | } | ||
145 | |||
146 | AssocItem::Const(konst) => { | ||
147 | if konst.name(self.db).map_or(false, |n| n == segment.name) { | ||
148 | Some(AssocItem::Const(konst)) | ||
149 | } else { | ||
150 | None | ||
151 | } | ||
152 | } | ||
153 | AssocItem::TypeAlias(_) => None, | ||
154 | })?; | ||
155 | let def = match item { | ||
156 | AssocItem::Function(f) => ValueNs::Function(f), | ||
157 | AssocItem::Const(c) => ValueNs::Const(c), | ||
158 | AssocItem::TypeAlias(_) => unreachable!(), | ||
159 | }; | ||
160 | let substs = self.find_self_types(&def, ty); | ||
161 | |||
162 | self.write_assoc_resolution(id, item); | ||
163 | Some((def, substs)) | ||
164 | } | ||
165 | |||
166 | fn find_self_types(&self, def: &ValueNs, actual_def_ty: Ty) -> Option<Substs> { | ||
167 | if let ValueNs::Function(func) = def { | ||
168 | // We only do the infer if parent has generic params | ||
169 | let gen = func.generic_params(self.db); | ||
170 | if gen.count_parent_params() == 0 { | ||
171 | return None; | ||
172 | } | ||
173 | |||
174 | let impl_block = func.impl_block(self.db)?.target_ty(self.db); | ||
175 | let impl_block_substs = impl_block.substs()?; | ||
176 | let actual_substs = actual_def_ty.substs()?; | ||
177 | |||
178 | let mut new_substs = vec![Ty::Unknown; gen.count_parent_params()]; | ||
179 | |||
180 | // The following code *link up* the function actual parma type | ||
181 | // and impl_block type param index | ||
182 | impl_block_substs.iter().zip(actual_substs.iter()).for_each(|(param, pty)| { | ||
183 | if let Ty::Param { idx, .. } = param { | ||
184 | if let Some(s) = new_substs.get_mut(*idx as usize) { | ||
185 | *s = pty.clone(); | ||
186 | } | ||
187 | } | ||
188 | }); | ||
189 | |||
190 | Some(Substs(new_substs.into())) | ||
191 | } else { | ||
192 | None | ||
193 | } | ||
194 | } | ||
195 | } | ||