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