aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir/src/ty/infer/path.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir/src/ty/infer/path.rs')
-rw-r--r--crates/ra_hir/src/ty/infer/path.rs195
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
3use super::{ExprOrPatId, InferenceContext};
4use crate::{
5 db::HirDatabase,
6 resolve::{ResolveValueResult, Resolver, TypeNs, ValueNs},
7 ty::{Substs, Ty, TypableDef, TypeWalk},
8 AssocItem, HasGenericParams, Namespace, Path,
9};
10
11impl<'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}