Skip to main content

hir_ty/infer/
path.rs

1//! Path expression resolution.
2
3use 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                    // FIXME: report error, invalid Self reference
101                    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            // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type.
112            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            // This is something like `TypeAlias::<Args>::EnumVariant`. Do not call `substs_from_path()`,
121            // as it'll try to re-lower the previous segment assuming it refers to the enum, but it refers
122            // to the type alias and they may have different generics.
123            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        // Don't use `self.make_ty()` here as we need `orig_ns`.
153        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            // FIXME: report error, unresolved first path segment
191            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                    // there may be more intermediate segments between the resolved one and
223                    // the end. Only the last segment needs to be resolved to a value; from
224                    // the segments before that, we need to get either a type or a trait ref.
225
226                    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                            // Either we already have a type (e.g. `Vec::new`), or we have a
246                            // trait but it's not the last segment, so the next segment
247                            // should resolve to an associated type of that trait (e.g. `<T
248                            // as Iterator>::Item::default`)
249                            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                // we're picking this method
375                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    // It's awkward to wrap a single ID in two enums, but we need both and this saves fallible
420    // conversion between them + `unwrap()`.
421    GenericDef(ValueTyDefId, GenericDefId, GenericArgs<'db>),
422    NonGeneric(Ty<'db>),
423}