]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / hir-ty / src / infer / path.rs
1 //! Path expression resolution.
2
3 use chalk_ir::cast::Cast;
4 use hir_def::{
5 path::{Path, PathSegment},
6 resolver::{ResolveValueResult, Resolver, TypeNs, ValueNs},
7 AdtId, AssocItemId, EnumVariantId, ItemContainerId, Lookup,
8 };
9 use hir_expand::name::Name;
10 use stdx::never;
11
12 use crate::{
13 builder::ParamKind,
14 consteval,
15 method_resolution::{self, VisibleFromModule},
16 utils::generics,
17 InferenceDiagnostic, Interner, Substitution, TraitRefExt, Ty, TyBuilder, TyExt, TyKind,
18 ValueTyDefId,
19 };
20
21 use super::{ExprOrPatId, InferenceContext, TraitRef};
22
23 impl<'a> InferenceContext<'a> {
24 pub(super) fn infer_path(
25 &mut self,
26 resolver: &Resolver,
27 path: &Path,
28 id: ExprOrPatId,
29 ) -> Option<Ty> {
30 let ty = self.resolve_value_path(resolver, path, id)?;
31 let ty = self.insert_type_vars(ty);
32 let ty = self.normalize_associated_types_in(ty);
33 Some(ty)
34 }
35
36 fn resolve_value_path(
37 &mut self,
38 resolver: &Resolver,
39 path: &Path,
40 id: ExprOrPatId,
41 ) -> Option<Ty> {
42 let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
43 if path.segments().is_empty() {
44 // This can't actually happen syntax-wise
45 return None;
46 }
47 let ty = self.make_ty(type_ref);
48 let remaining_segments_for_ty = path.segments().take(path.segments().len() - 1);
49 let ctx = crate::lower::TyLoweringContext::new(self.db, resolver);
50 let (ty, _) = ctx.lower_ty_relative_path(ty, None, remaining_segments_for_ty);
51 self.resolve_ty_assoc_item(
52 ty,
53 path.segments().last().expect("path had at least one segment").name,
54 id,
55 )?
56 } else {
57 let value_or_partial =
58 resolver.resolve_path_in_value_ns(self.db.upcast(), path.mod_path())?;
59
60 match value_or_partial {
61 ResolveValueResult::ValueNs(it) => (it, None),
62 ResolveValueResult::Partial(def, remaining_index) => {
63 self.resolve_assoc_item(def, path, remaining_index, id)?
64 }
65 }
66 };
67
68 let typable: ValueTyDefId = match value {
69 ValueNs::LocalBinding(pat) => {
70 let ty = self.result.type_of_pat.get(pat)?.clone();
71 return Some(ty);
72 }
73 ValueNs::FunctionId(it) => it.into(),
74 ValueNs::ConstId(it) => it.into(),
75 ValueNs::StaticId(it) => it.into(),
76 ValueNs::StructId(it) => {
77 self.write_variant_resolution(id, it.into());
78
79 it.into()
80 }
81 ValueNs::EnumVariantId(it) => {
82 self.write_variant_resolution(id, it.into());
83
84 it.into()
85 }
86 ValueNs::ImplSelf(impl_id) => {
87 let generics = crate::utils::generics(self.db.upcast(), impl_id.into());
88 let substs = generics.placeholder_subst(self.db);
89 let ty = self.db.impl_self_ty(impl_id).substitute(Interner, &substs);
90 if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
91 let ty = self.db.value_ty(struct_id.into()).substitute(Interner, &substs);
92 return Some(ty);
93 } else {
94 // FIXME: diagnostic, invalid Self reference
95 return None;
96 }
97 }
98 ValueNs::GenericParam(it) => return Some(self.db.const_param_ty(it)),
99 };
100
101 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
102 let substs = ctx.substs_from_path(path, typable, true);
103 let substs = substs.as_slice(Interner);
104 let parent_substs = self_subst.or_else(|| {
105 let generics = generics(self.db.upcast(), typable.to_generic_def_id()?);
106 let parent_params_len = generics.parent_generics()?.len();
107 let parent_args = &substs[substs.len() - parent_params_len..];
108 Some(Substitution::from_iter(Interner, parent_args))
109 });
110 let parent_substs_len = parent_substs.as_ref().map_or(0, |s| s.len(Interner));
111 let mut it = substs.iter().take(substs.len() - parent_substs_len).cloned();
112 let ty = TyBuilder::value_ty(self.db, typable, parent_substs)
113 .fill(|x| {
114 it.next().unwrap_or_else(|| match x {
115 ParamKind::Type => TyKind::Error.intern(Interner).cast(Interner),
116 ParamKind::Const(ty) => consteval::unknown_const_as_generic(ty.clone()),
117 })
118 })
119 .build();
120 Some(ty)
121 }
122
123 fn resolve_assoc_item(
124 &mut self,
125 def: TypeNs,
126 path: &Path,
127 remaining_index: usize,
128 id: ExprOrPatId,
129 ) -> Option<(ValueNs, Option<Substitution>)> {
130 assert!(remaining_index < path.segments().len());
131 // there may be more intermediate segments between the resolved one and
132 // the end. Only the last segment needs to be resolved to a value; from
133 // the segments before that, we need to get either a type or a trait ref.
134
135 let resolved_segment = path.segments().get(remaining_index - 1).unwrap();
136 let remaining_segments = path.segments().skip(remaining_index);
137 let is_before_last = remaining_segments.len() == 1;
138
139 match (def, is_before_last) {
140 (TypeNs::TraitId(trait_), true) => {
141 let segment =
142 remaining_segments.last().expect("there should be at least one segment here");
143 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
144 let trait_ref =
145 ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None);
146 self.resolve_trait_assoc_item(trait_ref, segment, id)
147 }
148 (def, _) => {
149 // Either we already have a type (e.g. `Vec::new`), or we have a
150 // trait but it's not the last segment, so the next segment
151 // should resolve to an associated type of that trait (e.g. `<T
152 // as Iterator>::Item::default`)
153 let remaining_segments_for_ty =
154 remaining_segments.take(remaining_segments.len() - 1);
155 let ctx = crate::lower::TyLoweringContext::new(self.db, &self.resolver);
156 let (ty, _) = ctx.lower_partly_resolved_path(
157 def,
158 resolved_segment,
159 remaining_segments_for_ty,
160 true,
161 );
162 if ty.is_unknown() {
163 return None;
164 }
165
166 let ty = self.insert_type_vars(ty);
167 let ty = self.normalize_associated_types_in(ty);
168
169 let segment =
170 remaining_segments.last().expect("there should be at least one segment here");
171
172 self.resolve_ty_assoc_item(ty, segment.name, id)
173 }
174 }
175 }
176
177 fn resolve_trait_assoc_item(
178 &mut self,
179 trait_ref: TraitRef,
180 segment: PathSegment<'_>,
181 id: ExprOrPatId,
182 ) -> Option<(ValueNs, Option<Substitution>)> {
183 let trait_ = trait_ref.hir_trait_id();
184 let item =
185 self.db.trait_data(trait_).items.iter().map(|(_name, id)| (*id)).find_map(|item| {
186 match item {
187 AssocItemId::FunctionId(func) => {
188 if segment.name == &self.db.function_data(func).name {
189 Some(AssocItemId::FunctionId(func))
190 } else {
191 None
192 }
193 }
194
195 AssocItemId::ConstId(konst) => {
196 if self
197 .db
198 .const_data(konst)
199 .name
200 .as_ref()
201 .map_or(false, |n| n == segment.name)
202 {
203 Some(AssocItemId::ConstId(konst))
204 } else {
205 None
206 }
207 }
208 AssocItemId::TypeAliasId(_) => None,
209 }
210 })?;
211 let def = match item {
212 AssocItemId::FunctionId(f) => ValueNs::FunctionId(f),
213 AssocItemId::ConstId(c) => ValueNs::ConstId(c),
214 AssocItemId::TypeAliasId(_) => unreachable!(),
215 };
216
217 self.write_assoc_resolution(id, item, trait_ref.substitution.clone());
218 Some((def, Some(trait_ref.substitution)))
219 }
220
221 fn resolve_ty_assoc_item(
222 &mut self,
223 ty: Ty,
224 name: &Name,
225 id: ExprOrPatId,
226 ) -> Option<(ValueNs, Option<Substitution>)> {
227 if let TyKind::Error = ty.kind(Interner) {
228 return None;
229 }
230
231 if let Some(result) = self.resolve_enum_variant_on_ty(&ty, name, id) {
232 return Some(result);
233 }
234
235 let canonical_ty = self.canonicalize(ty.clone());
236 let traits_in_scope = self.resolver.traits_in_scope(self.db.upcast());
237
238 let mut not_visible = None;
239 let res = method_resolution::iterate_method_candidates(
240 &canonical_ty.value,
241 self.db,
242 self.table.trait_env.clone(),
243 &traits_in_scope,
244 VisibleFromModule::Filter(self.resolver.module()),
245 Some(name),
246 method_resolution::LookupMode::Path,
247 |_ty, item, visible| {
248 let (def, container) = match item {
249 AssocItemId::FunctionId(f) => {
250 (ValueNs::FunctionId(f), f.lookup(self.db.upcast()).container)
251 }
252 AssocItemId::ConstId(c) => {
253 (ValueNs::ConstId(c), c.lookup(self.db.upcast()).container)
254 }
255 AssocItemId::TypeAliasId(_) => unreachable!(),
256 };
257 let substs = match container {
258 ItemContainerId::ImplId(impl_id) => {
259 let impl_substs = TyBuilder::subst_for_def(self.db, impl_id, None)
260 .fill_with_inference_vars(&mut self.table)
261 .build();
262 let impl_self_ty =
263 self.db.impl_self_ty(impl_id).substitute(Interner, &impl_substs);
264 self.unify(&impl_self_ty, &ty);
265 impl_substs
266 }
267 ItemContainerId::TraitId(trait_) => {
268 // we're picking this method
269 let trait_ref = TyBuilder::trait_ref(self.db, trait_)
270 .push(ty.clone())
271 .fill_with_inference_vars(&mut self.table)
272 .build();
273 self.push_obligation(trait_ref.clone().cast(Interner));
274 trait_ref.substitution
275 }
276 ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
277 never!("assoc item contained in module/extern block");
278 return None;
279 }
280 };
281
282 if visible {
283 Some((def, item, Some(substs), true))
284 } else {
285 if not_visible.is_none() {
286 not_visible = Some((def, item, Some(substs), false));
287 }
288 None
289 }
290 },
291 );
292 let res = res.or(not_visible);
293 if let Some((_, item, Some(ref substs), visible)) = res {
294 self.write_assoc_resolution(id, item, substs.clone());
295 if !visible {
296 self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item })
297 }
298 }
299 res.map(|(def, _, substs, _)| (def, substs))
300 }
301
302 fn resolve_enum_variant_on_ty(
303 &mut self,
304 ty: &Ty,
305 name: &Name,
306 id: ExprOrPatId,
307 ) -> Option<(ValueNs, Option<Substitution>)> {
308 let ty = self.resolve_ty_shallow(ty);
309 let (enum_id, subst) = match ty.as_adt() {
310 Some((AdtId::EnumId(e), subst)) => (e, subst),
311 _ => return None,
312 };
313 let enum_data = self.db.enum_data(enum_id);
314 let local_id = enum_data.variant(name)?;
315 let variant = EnumVariantId { parent: enum_id, local_id };
316 self.write_variant_resolution(id, variant.into());
317 Some((ValueNs::EnumVariantId(variant), Some(subst.clone())))
318 }
319 }