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;
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_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 self.store_owner,
163 self.generic_def,
164 &self.generics,
165 LifetimeElisionKind::Infer,
166 self.allow_using_generic_params,
167 &mut vars_ctx,
168 &self.defined_anon_consts,
169 LifetimeLoweringMode::LateParam,
170 );
171 let mut path_ctx = if no_diagnostics {
172 ctx.at_path_forget_diagnostics(path)
173 } else {
174 ctx.at_path(path, id)
175 };
176 let (value, self_subst) = if let Some(type_ref) = path.type_anchor() {
177 let last = path.segments().last()?;
178
179 let (ty, orig_ns) = path_ctx.ty_ctx().lower_ty_ext(type_ref);
180 let ty = path_ctx.expect_table().process_user_written_ty(ty);
181
182 path_ctx.ignore_last_segment();
183 let (ty, _) = path_ctx.lower_ty_relative_path(ty, orig_ns, true, id.into());
184 drop_ctx(ctx, no_diagnostics);
185 let ty = self.table.process_user_written_ty(ty);
186 self.resolve_ty_assoc_item(ty, last.name, id).map(|(it, substs)| (it, Some(substs)))?
187 } else {
188 let hygiene = self.store.expr_or_pat_path_hygiene(id.unpack());
189 let value_or_partial = path_ctx.resolve_path_in_value_ns(hygiene)?;
191
192 match value_or_partial {
193 ResolveValueResult::ValueNs(it) => {
194 drop_ctx(ctx, no_diagnostics);
195
196 let args = if let Path::LangItem(..) = path {
197 let def_and_container = match it {
198 ValueNs::ConstId(it) => Some((it.into(), it.loc(self.db).container)),
199 ValueNs::FunctionId(it) => Some((it.into(), it.loc(self.db).container)),
200 _ => None,
201 };
202 let def_and_container =
203 def_and_container.and_then(|(def, container)| match container {
204 ItemContainerId::ImplId(it) => Some((def, it.into())),
205 ItemContainerId::TraitId(it) => Some((def, it.into())),
206 ItemContainerId::ExternBlockId(_)
207 | ItemContainerId::ModuleId(_) => None,
208 });
209 def_and_container.map(|(def, container)| {
210 let args = self.infcx().fresh_args_for_item(id.into(), container);
211 self.write_assoc_resolution(id, def, args);
212 args
213 })
214 } else {
215 None
216 };
217
218 (it, args)
219 }
220 ResolveValueResult::Partial(def, remaining_index) => {
221 let remaining_segments = path.segments().skip(remaining_index);
226 let is_before_last = remaining_segments.len() == 1;
227 let last_segment = remaining_segments
228 .last()
229 .expect("there should be at least one segment here");
230
231 let (resolution, substs) = match (def, is_before_last) {
232 (TypeNs::TraitId(trait_), true) => {
233 let self_ty = path_ctx.expect_table().next_ty_var(id.into());
234 let trait_ref = path_ctx.lower_trait_ref_from_resolved_path(
235 trait_,
236 self_ty,
237 true,
238 id.into(),
239 );
240 drop_ctx(ctx, no_diagnostics);
241 self.resolve_trait_assoc_item(trait_ref, last_segment, id)
242 }
243 (def, _) => {
244 path_ctx.ignore_last_segment();
249 let (ty, _) = path_ctx.lower_partly_resolved_path(def, true, id.into());
250 drop_ctx(ctx, no_diagnostics);
251 if ty.is_ty_error() {
252 return None;
253 }
254
255 let ty = self.process_user_written_ty(ty);
256
257 self.resolve_ty_assoc_item(ty, last_segment.name, id)
258 }
259 }?;
260 (resolution, Some(substs))
261 }
262 }
263 };
264 return Some((value, self_subst));
265
266 #[inline]
267 fn drop_ctx(mut ctx: TyLoweringContext<'_, '_>, no_diagnostics: bool) {
268 if no_diagnostics {
269 ctx.forget_diagnostics();
270 }
271 }
272 }
273
274 pub(super) fn add_required_obligations_for_value_path(
275 &mut self,
276 node: ExprOrPatIdPacked,
277 def: GenericDefId,
278 subst: GenericArgs<'db>,
279 ) {
280 let interner = self.interner();
281 let predicates = GenericPredicates::query_all(self.db, def);
282 let param_env = self.table.param_env;
283 self.table.register_predicates(clauses_as_obligations(
284 predicates
285 .iter_instantiated(interner, subst.as_slice())
286 .map(Unnormalized::skip_norm_wip),
287 ObligationCause::new(node),
288 param_env,
289 ));
290 }
291
292 fn resolve_trait_assoc_item(
293 &mut self,
294 trait_ref: TraitRef<'db>,
295 segment: PathSegment<'_>,
296 id: ExprOrPatIdPacked,
297 ) -> Option<(ValueNs, GenericArgs<'db>)> {
298 let trait_ = trait_ref.def_id.0;
299 let item =
300 trait_.trait_items(self.db).items.iter().map(|(_name, id)| *id).find_map(|item| {
301 match item {
302 AssocItemId::FunctionId(func) => {
303 if segment.name == &FunctionSignature::of(self.db, func).name {
304 Some(CandidateId::FunctionId(func))
305 } else {
306 None
307 }
308 }
309
310 AssocItemId::ConstId(konst) => {
311 if ConstSignature::of(self.db, konst).name.as_ref() == Some(segment.name) {
312 Some(CandidateId::ConstId(konst))
313 } else {
314 None
315 }
316 }
317 AssocItemId::TypeAliasId(_) => None,
318 }
319 })?;
320 let def = match item {
321 CandidateId::FunctionId(f) => ValueNs::FunctionId(f),
322 CandidateId::ConstId(c) => ValueNs::ConstId(c),
323 };
324
325 self.write_assoc_resolution(id, item, trait_ref.args);
326 Some((def, trait_ref.args))
327 }
328
329 fn resolve_ty_assoc_item(
330 &mut self,
331 ty: Ty<'db>,
332 name: &Name,
333 id: ExprOrPatIdPacked,
334 ) -> Option<(ValueNs, GenericArgs<'db>)> {
335 if ty.is_ty_error() {
336 return None;
337 }
338
339 if let Some(result) = self.resolve_enum_variant_on_ty(ty, name, id) {
340 return Some(result);
341 }
342
343 let res = self.with_method_resolution(Span::Dummy, Span::Dummy, |ctx| {
344 ctx.probe_for_name(method_resolution::Mode::Path, name.clone(), ty)
345 });
346 let (item, visible) = match res {
347 Ok(res) => (res.item, true),
348 Err(error) => match error {
349 MethodError::PrivateMatch(candidate_id) => (candidate_id.item, false),
350 _ => {
351 self.push_diagnostic(InferenceDiagnostic::UnresolvedAssocItem { id });
352 return None;
353 }
354 },
355 };
356
357 let (def, container) = match item {
358 CandidateId::FunctionId(f) => (ValueNs::FunctionId(f), f.lookup(self.db).container),
359 CandidateId::ConstId(c) => (ValueNs::ConstId(c), c.lookup(self.db).container),
360 };
361 let substs = match container {
362 ItemContainerId::ImplId(impl_id) => {
363 let impl_substs = self.table.fresh_args_for_item(id.into(), impl_id.into());
364 let impl_self_ty = self
365 .db
366 .impl_self_ty(impl_id)
367 .instantiate(self.interner(), impl_substs)
368 .skip_norm_wip();
369 _ = self.demand_eqtype(id, impl_self_ty, ty);
370 impl_substs
371 }
372 ItemContainerId::TraitId(trait_) => {
373 GenericArgs::fill_rest(
375 self.interner(),
376 trait_.into(),
377 [ty.into()],
378 |_, param, _| self.table.var_for_def(param, id.into()),
379 )
380 }
381 ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => {
382 never!("assoc item contained in module/extern block");
383 return None;
384 }
385 };
386
387 self.write_assoc_resolution(id, item, substs);
388 if !visible {
389 let item = match item {
390 CandidateId::FunctionId(it) => it.into(),
391 CandidateId::ConstId(it) => it.into(),
392 };
393 self.push_diagnostic(InferenceDiagnostic::PrivateAssocItem { id, item });
394 }
395 Some((def, substs))
396 }
397
398 fn resolve_enum_variant_on_ty(
399 &mut self,
400 ty: Ty<'db>,
401 name: &Name,
402 id: ExprOrPatIdPacked,
403 ) -> Option<(ValueNs, GenericArgs<'db>)> {
404 let ty = self.table.try_structurally_resolve_type(id.into(), ty);
405 let (enum_id, subst) = match ty.as_adt() {
406 Some((AdtId::EnumId(e), subst)) => (e, subst),
407 _ => return None,
408 };
409 let enum_data = enum_id.enum_variants(self.db);
410 let variant = enum_data.variant(name)?;
411 self.write_variant_resolution(id, variant.into());
412 Some((ValueNs::EnumVariantId(variant), subst))
413 }
414}
415
416#[derive(Debug)]
417enum ValuePathResolution<'db> {
418 GenericDef(ValueTyDefId, GenericDefId, GenericArgs<'db>),
421 NonGeneric(Ty<'db>),
422}