1use hir_def::{
4 AdtId, AssocItemId, GenericDefId, ItemContainerId, Lookup,
5 expr_store::path::{Path, PathSegment},
6 resolver::{ResolveValueResult, TypeNs, ValueNs},
7};
8use hir_expand::name::Name;
9use rustc_type_ir::inherent::{SliceLike, Ty as _};
10use stdx::never;
11
12use crate::{
13 InferenceDiagnostic, ValueTyDefId,
14 generics::generics,
15 infer::diagnostics::InferenceTyLoweringContext as TyLoweringContext,
16 lower::{GenericPredicates, LifetimeElisionKind},
17 method_resolution::{self, CandidateId, MethodError},
18 next_solver::{
19 GenericArg, GenericArgs, TraitRef, Ty,
20 infer::traits::{Obligation, ObligationCause},
21 util::clauses_as_obligations,
22 },
23};
24
25use super::{ExprOrPatId, InferenceContext, InferenceTyDiagnosticSource};
26
27impl<'db> InferenceContext<'_, 'db> {
28 pub(super) fn infer_path(&mut self, path: &Path, id: ExprOrPatId) -> Option<Ty<'db>> {
29 let (value_def, generic_def, substs) = match self.resolve_value_path(path, id)? {
30 ValuePathResolution::GenericDef(value_def, generic_def, substs) => {
31 (value_def, generic_def, substs)
32 }
33 ValuePathResolution::NonGeneric(ty) => return Some(ty),
34 };
35 let args = self.insert_type_vars(substs);
36
37 self.add_required_obligations_for_value_path(generic_def, args);
38
39 let ty = self.db.value_ty(value_def)?.instantiate(self.interner(), args);
40 let ty = self.process_remote_user_written_ty(ty);
41 Some(ty)
42 }
43
44 fn resolve_value_path(
45 &mut self,
46 path: &Path,
47 id: ExprOrPatId,
48 ) -> Option<ValuePathResolution<'db>> {
49 let (value, self_subst) = self.resolve_value_path_inner(path, id, false)?;
50
51 let value_def: ValueTyDefId = match value {
52 ValueNs::FunctionId(it) => it.into(),
53 ValueNs::ConstId(it) => it.into(),
54 ValueNs::StaticId(it) => it.into(),
55 ValueNs::StructId(it) => {
56 self.write_variant_resolution(id, it.into());
57
58 it.into()
59 }
60 ValueNs::EnumVariantId(it) => {
61 self.write_variant_resolution(id, it.into());
62
63 it.into()
64 }
65 ValueNs::LocalBinding(pat) => {
66 return match self.result.type_of_binding.get(pat) {
67 Some(ty) => Some(ValuePathResolution::NonGeneric(*ty)),
68 None => {
69 never!("uninferred pattern?");
70 None
71 }
72 };
73 }
74 ValueNs::ImplSelf(impl_id) => {
75 let ty = self.db.impl_self_ty(impl_id).instantiate_identity();
76 return if let Some((AdtId::StructId(struct_id), substs)) = ty.as_adt() {
77 Some(ValuePathResolution::GenericDef(
78 struct_id.into(),
79 struct_id.into(),
80 substs,
81 ))
82 } else {
83 None
85 };
86 }
87 ValueNs::GenericParam(it) => {
88 return Some(ValuePathResolution::NonGeneric(self.db.const_param_ty_ns(it)));
89 }
90 };
91
92 let generic_def = value_def.to_generic_def_id(self.db);
93 if let GenericDefId::StaticId(_) = generic_def {
94 let ty = self.db.value_ty(value_def)?.skip_binder();
96 return Some(ValuePathResolution::NonGeneric(ty));
97 };
98
99 let substs = if self_subst.is_some_and(|it| !it.is_empty())
100 && matches!(value_def, ValueTyDefId::EnumVariantId(_))
101 {
102 self.types.empty_args
106 } else {
107 self.with_body_ty_lowering(|ctx| {
108 let mut path_ctx = ctx.at_path(path, id);
109 let last_segment = path.segments().len().checked_sub(1);
110 if let Some(last_segment) = last_segment {
111 path_ctx.set_current_segment(last_segment)
112 }
113 path_ctx.substs_from_path(value_def, true, false)
114 })
115 };
116
117 let parent_substs_len = self_subst.map_or(0, |it| it.len());
118 let substs = GenericArgs::fill_rest(
119 self.interner(),
120 generic_def.into(),
121 self_subst.iter().flat_map(|it| it.iter()).chain(substs.iter().skip(parent_substs_len)),
122 |_, id, _| GenericArg::error_from_id(self.interner(), id),
123 );
124
125 Some(ValuePathResolution::GenericDef(value_def, generic_def, substs))
126 }
127
128 pub(super) fn resolve_value_path_inner(
129 &mut self,
130 path: &Path,
131 id: ExprOrPatId,
132 no_diagnostics: bool,
133 ) -> Option<(ValueNs, Option<GenericArgs<'db>>)> {
134 let mut ctx = TyLoweringContext::new(
136 self.db,
137 &self.resolver,
138 self.body,
139 &self.diagnostics,
140 InferenceTyDiagnosticSource::Body,
141 self.generic_def,
142 LifetimeElisionKind::Infer,
143 );
144 let mut path_ctx = if no_diagnostics {
145 ctx.at_path_forget_diagnostics(path)
146 } else {
147 ctx.at_path(path, id)
148 };
149 let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
150 let last = path.segments().last()?;
151
152 let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref);
153 let ty = self.table.process_user_written_ty(ty);
154
155 path_ctx.ignore_last_segment();
156 let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true);
157 drop_ctx(ctx, no_diagnostics);
158 let ty = self.table.process_user_written_ty(ty);
159 self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
160 } else {
161 let hygiene = self.body.expr_or_pat_path_hygiene(id);
162 let value_or_partial = path_ctx.resolve_path_in_value_ns(hygiene)?;
164
165 match value_or_partial {
166 ResolveValueResult::ValueNs(it, _) => {
167 drop_ctx(ctx, no_diagnostics);
168 (it, None)
169 }
170 ResolveValueResult::Partial(def, remaining_index, _) => {
171 let remaining_segments = path.segments().skip(remaining_index);
176 let is_before_last = remaining_segments.len() == 1;
177 let last_segment = remaining_segments
178 .last()
179 .expect("there should be at least one segment here");
180
181 let (resolution, substs) = match (def, is_before_last) {
182 (TypeNs::TraitId(trait_), true) => {
183 let self_ty = self.table.next_ty_var();
184 let trait_ref =
185 path_ctx.lower_trait_ref_from_resolved_path(trait_, self_ty, true);
186 drop_ctx(ctx, no_diagnostics);
187 self.resolve_trait_assoc_item(trait_ref, last_segment, id)
188 }
189 (def, _) => {
190 path_ctx.ignore_last_segment();
195 let (ty, _) = path_ctx.lower_partly_resolved_path(def, true);
196 drop_ctx(ctx, no_diagnostics);
197 if ty.is_ty_error() {
198 return None;
199 }
200
201 let ty = self.process_user_written_ty(ty);
202
203 self.resolve_ty_assoc_item(ty, last_segment.name, id)
204 }
205 }?;
206 (resolution, Some(substs))
207 }
208 }
209 };
210 return Some((value, self_subst));
211
212 #[inline]
213 fn drop_ctx(mut ctx: TyLoweringContext<'_, '_>, no_diagnostics: bool) {
214 if no_diagnostics {
215 ctx.forget_diagnostics();
216 }
217 }
218 }
219
220 fn add_required_obligations_for_value_path(
221 &mut self,
222 def: GenericDefId,
223 subst: GenericArgs<'db>,
224 ) {
225 let interner = self.interner();
226 let predicates = GenericPredicates::query_all(self.db, def);
227 let param_env = self.table.param_env;
228 self.table.register_predicates(clauses_as_obligations(
229 predicates.iter_instantiated_copied(interner, subst.as_slice()),
230 ObligationCause::new(),
231 param_env,
232 ));
233
234 let container = match def {
236 GenericDefId::FunctionId(id) => id.lookup(self.db).container,
237 GenericDefId::ConstId(id) => id.lookup(self.db).container,
238 _ => return,
239 };
240
241 if let ItemContainerId::TraitId(trait_) = container {
242 let parent_len = generics(self.db, def).parent_generics().map_or(0, |g| g.len_self());
243 let parent_subst = GenericArgs::new_from_iter(
244 interner,
245 subst.as_slice()[..parent_len].iter().copied(),
246 );
247 let trait_ref = TraitRef::new(interner, trait_.into(), parent_subst);
248 self.table.register_predicate(Obligation::new(
249 interner,
250 ObligationCause::new(),
251 param_env,
252 trait_ref,
253 ));
254 }
255 }
256
257 fn resolve_trait_assoc_item(
258 &mut self,
259 trait_ref: TraitRef<'db>,
260 segment: PathSegment<'_>,
261 id: ExprOrPatId,
262 ) -> Option<(ValueNs, GenericArgs<'db>)> {
263 let trait_ = trait_ref.def_id.0;
264 let item =
265 trait_.trait_items(self.db).items.iter().map(|(_name, id)| *id).find_map(|item| {
266 match item {
267 AssocItemId::FunctionId(func) => {
268 if segment.name == &self.db.function_signature(func).name {
269 Some(CandidateId::FunctionId(func))
270 } else {
271 None
272 }
273 }
274
275 AssocItemId::ConstId(konst) => {
276 if self.db.const_signature(konst).name.as_ref() == Some(segment.name) {
277 Some(CandidateId::ConstId(konst))
278 } else {
279 None
280 }
281 }
282 AssocItemId::TypeAliasId(_) => None,
283 }
284 })?;
285 let def = match item {
286 CandidateId::FunctionId(f) => ValueNs::FunctionId(f),
287 CandidateId::ConstId(c) => ValueNs::ConstId(c),
288 };
289
290 self.write_assoc_resolution(id, item, trait_ref.args);
291 Some((def, trait_ref.args))
292 }
293
294 fn resolve_ty_assoc_item(
295 &mut self,
296 ty: Ty<'db>,
297 name: &Name,
298 id: ExprOrPatId,
299 ) -> Option<(ValueNs, GenericArgs<'db>)> {
300 if ty.is_ty_error() {
301 return None;
302 }
303
304 if let Some(result) = self.resolve_enum_variant_on_ty(ty, name, id) {
305 return Some(result);
306 }
307
308 let res = self.with_method_resolution(|ctx| {
309 ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty)
310 });
311 let (item, visible) = match res {
312 Ok(res) => (res.item, true),
313 Err(error) => match error {
314 MethodError::PrivateMatch(candidate_id) => (candidate_id.item, false),
315 _ => {
316 self.push_diagnostic(InferenceDiagnostic::UnresolvedAssocItem { id });
317 return None;
318 }
319 },
320 };
321
322 let (def, container) = match item {
323 CandidateId::FunctionId(f) => (ValueNs::FunctionId(f), f.lookup(self.db).container),
324 CandidateId::ConstId(c) => (ValueNs::ConstId(c), c.lookup(self.db).container),
325 };
326 let substs = match container {
327 ItemContainerId::ImplId(impl_id) => {
328 let impl_substs = self.table.fresh_args_for_item(impl_id.into());
329 let impl_self_ty =
330 self.db.impl_self_ty(impl_id).instantiate(self.interner(), impl_substs);
331 self.unify(impl_self_ty, ty);
332 impl_substs
333 }
334 ItemContainerId::TraitId(trait_) => {
335 let args = GenericArgs::fill_rest(
337 self.interner(),
338 trait_.into(),
339 [ty.into()],
340 |_, id, _| self.table.next_var_for_param(id),
341 );
342 let trait_ref = TraitRef::new(self.interner(), trait_.into(), args);
343 self.table.register_predicate(Obligation::new(
344 self.interner(),
345 ObligationCause::new(),
346 self.table.param_env,
347 trait_ref,
348 ));
349 args
350 }
351 ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
352 never!("assoc item contained in module/extern block");
353 return None;
354 }
355 };
356
357 self.write_assoc_resolution(id, item, substs);
358 if !visible {
359 let item = match item {
360 CandidateId::FunctionId(it) => it.into(),
361 CandidateId::ConstId(it) => it.into(),
362 };
363 self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item });
364 }
365 Some((def, substs))
366 }
367
368 fn resolve_enum_variant_on_ty(
369 &mut self,
370 ty: Ty<'db>,
371 name: &Name,
372 id: ExprOrPatId,
373 ) -> Option<(ValueNs, GenericArgs<'db>)> {
374 let ty = self.table.try_structurally_resolve_type(ty);
375 let (enum_id, subst) = match ty.as_adt() {
376 Some((AdtId::EnumId(e), subst)) => (e, subst),
377 _ => return None,
378 };
379 let enum_data = enum_id.enum_variants(self.db);
380 let variant = enum_data.variant(name)?;
381 self.write_variant_resolution(id, variant.into());
382 Some((ValueNs::EnumVariantId(variant), subst))
383 }
384}
385
386#[derive(Debug)]
387enum ValuePathResolution<'db> {
388 GenericDef(ValueTyDefId, GenericDefId, GenericArgs<'db>),
391 NonGeneric(Ty<'db>),
392}