]> git.lizzy.rs Git - rust.git/blob - crates/ra_hir_ty/src/infer/path.rs
a96ab75d1f49e98a2e197c6b0b20e7b80916ab31
[rust.git] / crates / ra_hir_ty / src / infer / path.rs
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     AssocContainerId, AssocItemId, Lookup,
9 };
10 use hir_expand::name::Name;
11
12 use crate::{db::HirDatabase, method_resolution, Substs, Ty, TypeWalk, ValueTyDefId};
13
14 use super::{ExprOrPatId, InferenceContext, TraitRef};
15
16 impl<'a, D: HirDatabase> InferenceContext<'a, D> {
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 ty = Ty::from_type_relative_path(self.db, resolver, ty, remaining_segments_for_ty);
43             self.resolve_ty_assoc_item(
44                 ty,
45                 &path.segments().last().expect("path had at least one segment").name,
46                 id,
47             )?
48         } else {
49             let value_or_partial = resolver.resolve_path_in_value_ns(self.db, path.mod_path())?;
50
51             match value_or_partial {
52                 ResolveValueResult::ValueNs(it) => (it, None),
53                 ResolveValueResult::Partial(def, remaining_index) => {
54                     self.resolve_assoc_item(def, path, remaining_index, id)?
55                 }
56             }
57         };
58
59         let typable: ValueTyDefId = match value {
60             ValueNs::LocalBinding(pat) => {
61                 let ty = self.result.type_of_pat.get(pat)?.clone();
62                 let ty = self.resolve_ty_as_possible(ty);
63                 return Some(ty);
64             }
65             ValueNs::FunctionId(it) => it.into(),
66             ValueNs::ConstId(it) => it.into(),
67             ValueNs::StaticId(it) => it.into(),
68             ValueNs::StructId(it) => it.into(),
69             ValueNs::EnumVariantId(it) => it.into(),
70         };
71
72         let mut ty = self.db.value_ty(typable);
73         if let Some(self_subst) = self_subst {
74             ty = ty.subst(&self_subst);
75         }
76         let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
77         let ty = ty.subst(&substs);
78         Some(ty)
79     }
80
81     fn resolve_assoc_item(
82         &mut self,
83         def: TypeNs,
84         path: &Path,
85         remaining_index: usize,
86         id: ExprOrPatId,
87     ) -> Option<(ValueNs, Option<Substs>)> {
88         assert!(remaining_index < path.segments().len());
89         // there may be more intermediate segments between the resolved one and
90         // the end. Only the last segment needs to be resolved to a value; from
91         // the segments before that, we need to get either a type or a trait ref.
92
93         let resolved_segment = path.segments().get(remaining_index - 1).unwrap();
94         let remaining_segments = path.segments().skip(remaining_index);
95         let is_before_last = remaining_segments.len() == 1;
96
97         match (def, is_before_last) {
98             (TypeNs::TraitId(trait_), true) => {
99                 let segment =
100                     remaining_segments.last().expect("there should be at least one segment here");
101                 let trait_ref = TraitRef::from_resolved_path(
102                     self.db,
103                     &self.resolver,
104                     trait_.into(),
105                     resolved_segment,
106                     None,
107                 );
108                 self.resolve_trait_assoc_item(trait_ref, segment, id)
109             }
110             (def, _) => {
111                 // Either we already have a type (e.g. `Vec::new`), or we have a
112                 // trait but it's not the last segment, so the next segment
113                 // should resolve to an associated type of that trait (e.g. `<T
114                 // as Iterator>::Item::default`)
115                 let remaining_segments_for_ty =
116                     remaining_segments.take(remaining_segments.len() - 1);
117                 let ty = Ty::from_partly_resolved_hir_path(
118                     self.db,
119                     &self.resolver,
120                     def,
121                     resolved_segment,
122                     remaining_segments_for_ty,
123                 );
124                 if let Ty::Unknown = ty {
125                     return None;
126                 }
127
128                 let ty = self.insert_type_vars(ty);
129                 let ty = self.normalize_associated_types_in(ty);
130
131                 let segment =
132                     remaining_segments.last().expect("there should be at least one segment here");
133
134                 self.resolve_ty_assoc_item(ty, &segment.name, id)
135             }
136         }
137     }
138
139     fn resolve_trait_assoc_item(
140         &mut self,
141         trait_ref: TraitRef,
142         segment: PathSegment<'_>,
143         id: ExprOrPatId,
144     ) -> Option<(ValueNs, Option<Substs>)> {
145         let trait_ = trait_ref.trait_;
146         let item = self
147             .db
148             .trait_data(trait_)
149             .items
150             .iter()
151             .map(|(_name, id)| (*id).into())
152             .find_map(|item| match item {
153                 AssocItemId::FunctionId(func) => {
154                     if segment.name == &self.db.function_data(func).name {
155                         Some(AssocItemId::FunctionId(func))
156                     } else {
157                         None
158                     }
159                 }
160
161                 AssocItemId::ConstId(konst) => {
162                     if self.db.const_data(konst).name.as_ref().map_or(false, |n| n == segment.name)
163                     {
164                         Some(AssocItemId::ConstId(konst))
165                     } else {
166                         None
167                     }
168                 }
169                 AssocItemId::TypeAliasId(_) => None,
170             })?;
171         let def = match item {
172             AssocItemId::FunctionId(f) => ValueNs::FunctionId(f),
173             AssocItemId::ConstId(c) => ValueNs::ConstId(c),
174             AssocItemId::TypeAliasId(_) => unreachable!(),
175         };
176         let substs = Substs::build_for_def(self.db, item)
177             .use_parent_substs(&trait_ref.substs)
178             .fill_with_params()
179             .build();
180
181         self.write_assoc_resolution(id, item);
182         Some((def, Some(substs)))
183     }
184
185     fn resolve_ty_assoc_item(
186         &mut self,
187         ty: Ty,
188         name: &Name,
189         id: ExprOrPatId,
190     ) -> Option<(ValueNs, Option<Substs>)> {
191         if let Ty::Unknown = ty {
192             return None;
193         }
194
195         let canonical_ty = self.canonicalizer().canonicalize_ty(ty.clone());
196
197         method_resolution::iterate_method_candidates(
198             &canonical_ty.value,
199             self.db,
200             &self.resolver.clone(),
201             Some(name),
202             method_resolution::LookupMode::Path,
203             move |_ty, item| {
204                 let (def, container) = match item {
205                     AssocItemId::FunctionId(f) => {
206                         (ValueNs::FunctionId(f), f.lookup(self.db).container)
207                     }
208                     AssocItemId::ConstId(c) => (ValueNs::ConstId(c), c.lookup(self.db).container),
209                     AssocItemId::TypeAliasId(_) => unreachable!(),
210                 };
211                 let substs = match container {
212                     AssocContainerId::ImplId(impl_id) => {
213                         let impl_substs = Substs::build_for_def(self.db, impl_id)
214                             .fill(iter::repeat_with(|| self.table.new_type_var()))
215                             .build();
216                         let impl_self_ty = self.db.impl_self_ty(impl_id).subst(&impl_substs);
217                         let substs = Substs::build_for_def(self.db, item)
218                             .use_parent_substs(&impl_substs)
219                             .fill_with_params()
220                             .build();
221                         self.unify(&impl_self_ty, &ty);
222                         Some(substs)
223                     }
224                     AssocContainerId::TraitId(trait_) => {
225                         // we're picking this method
226                         let trait_substs = Substs::build_for_def(self.db, trait_)
227                             .push(ty.clone())
228                             .fill(std::iter::repeat_with(|| self.table.new_type_var()))
229                             .build();
230                         let substs = Substs::build_for_def(self.db, item)
231                             .use_parent_substs(&trait_substs)
232                             .fill_with_params()
233                             .build();
234                         self.obligations.push(super::Obligation::Trait(TraitRef {
235                             trait_,
236                             substs: trait_substs,
237                         }));
238                         Some(substs)
239                     }
240                     AssocContainerId::ModuleId(_) | AssocContainerId::DefWithBodyId(_) => None,
241                 };
242
243                 self.write_assoc_resolution(id, item.into());
244                 Some((def, substs))
245             },
246         )
247     }
248 }