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    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                    // FIXME: report error, invalid Self reference
84                    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            // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type.
95            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            // This is something like `TypeAlias::<Args>::EnumVariant`. Do not call `substs_from_path()`,
103            // as it'll try to re-lower the previous segment assuming it refers to the enum, but it refers
104            // to the type alias and they may have different generics.
105            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        // Don't use `self.make_ty()` here as we need `orig_ns`.
135        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            // FIXME: report error, unresolved first path segment
163            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                    // there may be more intermediate segments between the resolved one and
172                    // the end. Only the last segment needs to be resolved to a value; from
173                    // the segments before that, we need to get either a type or a trait ref.
174
175                    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                            // Either we already have a type (e.g. `Vec::new`), or we have a
191                            // trait but it's not the last segment, so the next segment
192                            // should resolve to an associated type of that trait (e.g. `<T
193                            // as Iterator>::Item::default`)
194                            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        // We need to add `Self: Trait` obligation when `def` is a trait assoc item.
235        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                // we're picking this method
336                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    // It's awkward to wrap a single ID in two enums, but we need both and this saves fallible
389    // conversion between them + `unwrap()`.
390    GenericDef(ValueTyDefId, GenericDefId, GenericArgs<'db>),
391    NonGeneric(Ty<'db>),
392}