rustc_hir_analysis/collect/
type_of.rs

1use core::ops::ControlFlow;
2
3use rustc_errors::{Applicability, StashKey, Suggestions};
4use rustc_hir::def_id::{DefId, LocalDefId};
5use rustc_hir::intravisit::VisitorExt;
6use rustc_hir::{self as hir, AmbigArg, HirId};
7use rustc_middle::query::plumbing::CyclePlaceholder;
8use rustc_middle::ty::print::with_forced_trimmed_paths;
9use rustc_middle::ty::util::IntTypeExt;
10use rustc_middle::ty::{
11    self, DefiningScopeKind, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, fold_regions,
12};
13use rustc_middle::{bug, span_bug};
14use rustc_span::{DUMMY_SP, Ident, Span};
15
16use super::{HirPlaceholderCollector, ItemCtxt, bad_placeholder};
17use crate::errors::TypeofReservedKeywordUsed;
18use crate::hir_ty_lowering::HirTyLowerer;
19
20mod opaque;
21
22fn anon_const_type_of<'tcx>(icx: &ItemCtxt<'tcx>, def_id: LocalDefId) -> Ty<'tcx> {
23    use hir::*;
24    use rustc_middle::ty::Ty;
25    let tcx = icx.tcx;
26    let hir_id = tcx.local_def_id_to_hir_id(def_id);
27
28    let node = tcx.hir_node(hir_id);
29    let Node::AnonConst(&AnonConst { span, .. }) = node else {
30        span_bug!(
31            tcx.def_span(def_id),
32            "expected anon const in `anon_const_type_of`, got {node:?}"
33        );
34    };
35
36    let parent_node_id = tcx.parent_hir_id(hir_id);
37    let parent_node = tcx.hir_node(parent_node_id);
38
39    match parent_node {
40        // Anon consts "inside" the type system.
41        Node::ConstArg(&ConstArg {
42            hir_id: arg_hir_id,
43            kind: ConstArgKind::Anon(&AnonConst { hir_id: anon_hir_id, .. }),
44            ..
45        }) if anon_hir_id == hir_id => const_arg_anon_type_of(icx, arg_hir_id, span),
46
47        Node::Variant(Variant { disr_expr: Some(e), .. }) if e.hir_id == hir_id => {
48            tcx.adt_def(tcx.hir_get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
49        }
50        // Sort of affects the type system, but only for the purpose of diagnostics
51        // so no need for ConstArg.
52        Node::Ty(&hir::Ty { kind: TyKind::Typeof(ref e), span, .. }) if e.hir_id == hir_id => {
53            let ty = tcx.typeck(def_id).node_type(tcx.local_def_id_to_hir_id(def_id));
54            let ty = fold_regions(tcx, ty, |r, _| {
55                if r.is_erased() { ty::Region::new_error_misc(tcx) } else { r }
56            });
57            let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false, None) {
58                (ty, Some((span, Applicability::MachineApplicable)))
59            } else {
60                (ty, None)
61            };
62            tcx.dcx().emit_err(TypeofReservedKeywordUsed { span, ty, opt_sugg });
63            return ty;
64        }
65
66        Node::Field(&hir::FieldDef { default: Some(c), def_id: field_def_id, .. })
67            if c.hir_id == hir_id =>
68        {
69            tcx.type_of(field_def_id).instantiate_identity()
70        }
71
72        _ => Ty::new_error_with_message(
73            tcx,
74            span,
75            format!("unexpected anon const parent in type_of(): {parent_node:?}"),
76        ),
77    }
78}
79
80fn const_arg_anon_type_of<'tcx>(icx: &ItemCtxt<'tcx>, arg_hir_id: HirId, span: Span) -> Ty<'tcx> {
81    use hir::*;
82    use rustc_middle::ty::Ty;
83
84    let tcx = icx.tcx;
85
86    match tcx.parent_hir_node(arg_hir_id) {
87        // Array length const arguments do not have `type_of` fed as there is never a corresponding
88        // generic parameter definition.
89        Node::Ty(&hir::Ty { kind: TyKind::Array(_, ref constant), .. })
90        | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
91            if constant.hir_id == arg_hir_id =>
92        {
93            tcx.types.usize
94        }
95
96        Node::TyPat(pat) => {
97            let node = match tcx.parent_hir_node(pat.hir_id) {
98                // Or patterns can be nested one level deep
99                Node::TyPat(p) => tcx.parent_hir_node(p.hir_id),
100                other => other,
101            };
102            let hir::TyKind::Pat(ty, _) = node.expect_ty().kind else { bug!() };
103            icx.lower_ty(ty)
104        }
105
106        // This is not a `bug!` as const arguments in path segments that did not resolve to anything
107        // will result in `type_of` never being fed.
108        _ => Ty::new_error_with_message(
109            tcx,
110            span,
111            "`type_of` called on const argument's anon const before the const argument was lowered",
112        ),
113    }
114}
115
116pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, Ty<'_>> {
117    use rustc_hir::*;
118    use rustc_middle::ty::Ty;
119
120    // If we are computing `type_of` the synthesized associated type for an RPITIT in the impl
121    // side, use `collect_return_position_impl_trait_in_trait_tys` to infer the value of the
122    // associated type in the impl.
123    match tcx.opt_rpitit_info(def_id.to_def_id()) {
124        Some(ty::ImplTraitInTraitData::Impl { fn_def_id }) => {
125            match tcx.collect_return_position_impl_trait_in_trait_tys(fn_def_id) {
126                Ok(map) => {
127                    let assoc_item = tcx.associated_item(def_id);
128                    return map[&assoc_item.trait_item_def_id.unwrap()];
129                }
130                Err(_) => {
131                    return ty::EarlyBinder::bind(Ty::new_error_with_message(
132                        tcx,
133                        DUMMY_SP,
134                        "Could not collect return position impl trait in trait tys",
135                    ));
136                }
137            }
138        }
139        // For an RPITIT in a trait, just return the corresponding opaque.
140        Some(ty::ImplTraitInTraitData::Trait { opaque_def_id, .. }) => {
141            return ty::EarlyBinder::bind(Ty::new_opaque(
142                tcx,
143                opaque_def_id,
144                ty::GenericArgs::identity_for_item(tcx, opaque_def_id),
145            ));
146        }
147        None => {}
148    }
149
150    let hir_id = tcx.local_def_id_to_hir_id(def_id);
151
152    let icx = ItemCtxt::new(tcx, def_id);
153
154    let output = match tcx.hir_node(hir_id) {
155        Node::TraitItem(item) => match item.kind {
156            TraitItemKind::Fn(..) => {
157                let args = ty::GenericArgs::identity_for_item(tcx, def_id);
158                Ty::new_fn_def(tcx, def_id.to_def_id(), args)
159            }
160            TraitItemKind::Const(ty, body_id) => body_id
161                .and_then(|body_id| {
162                    ty.is_suggestable_infer_ty().then(|| {
163                        infer_placeholder_type(
164                            icx.lowerer(),
165                            def_id,
166                            body_id,
167                            ty.span,
168                            item.ident,
169                            "associated constant",
170                        )
171                    })
172                })
173                .unwrap_or_else(|| icx.lower_ty(ty)),
174            TraitItemKind::Type(_, Some(ty)) => icx.lower_ty(ty),
175            TraitItemKind::Type(_, None) => {
176                span_bug!(item.span, "associated type missing default");
177            }
178        },
179
180        Node::ImplItem(item) => match item.kind {
181            ImplItemKind::Fn(..) => {
182                let args = ty::GenericArgs::identity_for_item(tcx, def_id);
183                Ty::new_fn_def(tcx, def_id.to_def_id(), args)
184            }
185            ImplItemKind::Const(ty, body_id) => {
186                if ty.is_suggestable_infer_ty() {
187                    infer_placeholder_type(
188                        icx.lowerer(),
189                        def_id,
190                        body_id,
191                        ty.span,
192                        item.ident,
193                        "associated constant",
194                    )
195                } else {
196                    icx.lower_ty(ty)
197                }
198            }
199            ImplItemKind::Type(ty) => {
200                if tcx.impl_trait_ref(tcx.hir_get_parent_item(hir_id)).is_none() {
201                    check_feature_inherent_assoc_ty(tcx, item.span);
202                }
203
204                icx.lower_ty(ty)
205            }
206        },
207
208        Node::Item(item) => match item.kind {
209            ItemKind::Static(_, ident, ty, body_id) => {
210                if ty.is_suggestable_infer_ty() {
211                    infer_placeholder_type(
212                        icx.lowerer(),
213                        def_id,
214                        body_id,
215                        ty.span,
216                        ident,
217                        "static variable",
218                    )
219                } else {
220                    icx.lower_ty(ty)
221                }
222            }
223            ItemKind::Const(ident, _, ty, body_id) => {
224                if ty.is_suggestable_infer_ty() {
225                    infer_placeholder_type(
226                        icx.lowerer(),
227                        def_id,
228                        body_id,
229                        ty.span,
230                        ident,
231                        "constant",
232                    )
233                } else {
234                    icx.lower_ty(ty)
235                }
236            }
237            ItemKind::TyAlias(_, _, self_ty) => icx.lower_ty(self_ty),
238            ItemKind::Impl(hir::Impl { self_ty, .. }) => match self_ty.find_self_aliases() {
239                spans if spans.len() > 0 => {
240                    let guar = tcx
241                        .dcx()
242                        .emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: () });
243                    Ty::new_error(tcx, guar)
244                }
245                _ => icx.lower_ty(*self_ty),
246            },
247            ItemKind::Fn { .. } => {
248                let args = ty::GenericArgs::identity_for_item(tcx, def_id);
249                Ty::new_fn_def(tcx, def_id.to_def_id(), args)
250            }
251            ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => {
252                let def = tcx.adt_def(def_id);
253                let args = ty::GenericArgs::identity_for_item(tcx, def_id);
254                Ty::new_adt(tcx, def, args)
255            }
256            ItemKind::GlobalAsm { .. } => tcx.typeck(def_id).node_type(hir_id),
257            ItemKind::Trait(..)
258            | ItemKind::TraitAlias(..)
259            | ItemKind::Macro(..)
260            | ItemKind::Mod(..)
261            | ItemKind::ForeignMod { .. }
262            | ItemKind::ExternCrate(..)
263            | ItemKind::Use(..) => {
264                span_bug!(item.span, "compute_type_of_item: unexpected item type: {:?}", item.kind);
265            }
266        },
267
268        Node::OpaqueTy(..) => tcx.type_of_opaque(def_id).map_or_else(
269            |CyclePlaceholder(guar)| Ty::new_error(tcx, guar),
270            |ty| ty.instantiate_identity(),
271        ),
272
273        Node::ForeignItem(foreign_item) => match foreign_item.kind {
274            ForeignItemKind::Fn(..) => {
275                let args = ty::GenericArgs::identity_for_item(tcx, def_id);
276                Ty::new_fn_def(tcx, def_id.to_def_id(), args)
277            }
278            ForeignItemKind::Static(t, _, _) => icx.lower_ty(t),
279            ForeignItemKind::Type => Ty::new_foreign(tcx, def_id.to_def_id()),
280        },
281
282        Node::Ctor(def) | Node::Variant(Variant { data: def, .. }) => match def {
283            VariantData::Unit(..) | VariantData::Struct { .. } => {
284                tcx.type_of(tcx.hir_get_parent_item(hir_id)).instantiate_identity()
285            }
286            VariantData::Tuple(_, _, ctor) => {
287                let args = ty::GenericArgs::identity_for_item(tcx, def_id);
288                Ty::new_fn_def(tcx, ctor.to_def_id(), args)
289            }
290        },
291
292        Node::Field(field) => icx.lower_ty(field.ty),
293
294        Node::Expr(&Expr { kind: ExprKind::Closure { .. }, .. }) => {
295            tcx.typeck(def_id).node_type(hir_id)
296        }
297
298        Node::AnonConst(_) => anon_const_type_of(&icx, def_id),
299
300        Node::ConstBlock(_) => {
301            let args = ty::GenericArgs::identity_for_item(tcx, def_id.to_def_id());
302            args.as_inline_const().ty()
303        }
304
305        Node::GenericParam(param) => match &param.kind {
306            GenericParamKind::Type { default: Some(ty), .. }
307            | GenericParamKind::Const { ty, .. } => icx.lower_ty(ty),
308            x => bug!("unexpected non-type Node::GenericParam: {:?}", x),
309        },
310
311        x => {
312            bug!("unexpected sort of node in type_of(): {:?}", x);
313        }
314    };
315    if let Err(e) = icx.check_tainted_by_errors()
316        && !output.references_error()
317    {
318        ty::EarlyBinder::bind(Ty::new_error(tcx, e))
319    } else {
320        ty::EarlyBinder::bind(output)
321    }
322}
323
324pub(super) fn type_of_opaque(
325    tcx: TyCtxt<'_>,
326    def_id: DefId,
327) -> Result<ty::EarlyBinder<'_, Ty<'_>>, CyclePlaceholder> {
328    if let Some(def_id) = def_id.as_local() {
329        Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
330            hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
331                opaque::find_opaque_ty_constraints_for_tait(
332                    tcx,
333                    def_id,
334                    DefiningScopeKind::MirBorrowck,
335                )
336            }
337            hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => {
338                opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
339                    tcx,
340                    def_id,
341                    DefiningScopeKind::MirBorrowck,
342                )
343            }
344            // Opaque types desugared from `impl Trait`.
345            hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl }
346            | hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl } => {
347                if in_trait_or_impl == Some(hir::RpitContext::Trait)
348                    && !tcx.defaultness(owner).has_value()
349                {
350                    span_bug!(
351                        tcx.def_span(def_id),
352                        "tried to get type of this RPITIT with no definition"
353                    );
354                }
355                opaque::find_opaque_ty_constraints_for_rpit(
356                    tcx,
357                    def_id,
358                    owner,
359                    DefiningScopeKind::MirBorrowck,
360                )
361            }
362        }))
363    } else {
364        // Foreign opaque type will go through the foreign provider
365        // and load the type from metadata.
366        Ok(tcx.type_of(def_id))
367    }
368}
369
370pub(super) fn type_of_opaque_hir_typeck(
371    tcx: TyCtxt<'_>,
372    def_id: LocalDefId,
373) -> ty::EarlyBinder<'_, Ty<'_>> {
374    ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id).expect_opaque_ty().origin {
375        hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => {
376            opaque::find_opaque_ty_constraints_for_tait(tcx, def_id, DefiningScopeKind::HirTypeck)
377        }
378        hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true, .. } => {
379            opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
380                tcx,
381                def_id,
382                DefiningScopeKind::HirTypeck,
383            )
384        }
385        // Opaque types desugared from `impl Trait`.
386        hir::OpaqueTyOrigin::FnReturn { parent: owner, in_trait_or_impl }
387        | hir::OpaqueTyOrigin::AsyncFn { parent: owner, in_trait_or_impl } => {
388            if in_trait_or_impl == Some(hir::RpitContext::Trait)
389                && !tcx.defaultness(owner).has_value()
390            {
391                span_bug!(
392                    tcx.def_span(def_id),
393                    "tried to get type of this RPITIT with no definition"
394                );
395            }
396            opaque::find_opaque_ty_constraints_for_rpit(
397                tcx,
398                def_id,
399                owner,
400                DefiningScopeKind::HirTypeck,
401            )
402        }
403    })
404}
405
406fn infer_placeholder_type<'tcx>(
407    cx: &dyn HirTyLowerer<'tcx>,
408    def_id: LocalDefId,
409    body_id: hir::BodyId,
410    span: Span,
411    item_ident: Ident,
412    kind: &'static str,
413) -> Ty<'tcx> {
414    let tcx = cx.tcx();
415    let ty = tcx.typeck(def_id).node_type(body_id.hir_id);
416
417    // If this came from a free `const` or `static mut?` item,
418    // then the user may have written e.g. `const A = 42;`.
419    // In this case, the parser has stashed a diagnostic for
420    // us to improve in typeck so we do that now.
421    let guar = cx
422        .dcx()
423        .try_steal_modify_and_emit_err(span, StashKey::ItemNoType, |err| {
424            if !ty.references_error() {
425                // Only suggest adding `:` if it was missing (and suggested by parsing diagnostic).
426                let colon = if span == item_ident.span.shrink_to_hi() { ":" } else { "" };
427
428                // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type.
429                // We are typeck and have the real type, so remove that and suggest the actual type.
430                if let Suggestions::Enabled(suggestions) = &mut err.suggestions {
431                    suggestions.clear();
432                }
433
434                if let Some(ty) = ty.make_suggestable(tcx, false, None) {
435                    err.span_suggestion(
436                        span,
437                        format!("provide a type for the {kind}"),
438                        format!("{colon} {ty}"),
439                        Applicability::MachineApplicable,
440                    );
441                } else {
442                    with_forced_trimmed_paths!(err.span_note(
443                        tcx.hir_body(body_id).value.span,
444                        format!("however, the inferred type `{ty}` cannot be named"),
445                    ));
446                }
447            }
448        })
449        .unwrap_or_else(|| {
450            let mut visitor = HirPlaceholderCollector::default();
451            let node = tcx.hir_node_by_def_id(def_id);
452            if let Some(ty) = node.ty() {
453                visitor.visit_ty_unambig(ty);
454            }
455            // If we didn't find any infer tys, then just fallback to `span`.
456            if visitor.spans.is_empty() {
457                visitor.spans.push(span);
458            }
459            let mut diag = bad_placeholder(cx, visitor.spans, kind);
460
461            // HACK(#69396): Stashing and stealing diagnostics does not interact
462            // well with macros which may delay more than one diagnostic on the
463            // same span. If this happens, we will fall through to this arm, so
464            // we need to suppress the suggestion since it's invalid. Ideally we
465            // would suppress the duplicated error too, but that's really hard.
466            if span.is_empty() && span.from_expansion() {
467                // An approximately better primary message + no suggestion...
468                diag.primary_message("missing type for item");
469            } else if !ty.references_error() {
470                if let Some(ty) = ty.make_suggestable(tcx, false, None) {
471                    diag.span_suggestion_verbose(
472                        span,
473                        "replace this with a fully-specified type",
474                        ty,
475                        Applicability::MachineApplicable,
476                    );
477                } else {
478                    with_forced_trimmed_paths!(diag.span_note(
479                        tcx.hir_body(body_id).value.span,
480                        format!("however, the inferred type `{ty}` cannot be named"),
481                    ));
482                }
483            }
484
485            diag.emit()
486        });
487    Ty::new_error(tcx, guar)
488}
489
490fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) {
491    if !tcx.features().inherent_associated_types() {
492        use rustc_session::parse::feature_err;
493        use rustc_span::sym;
494        feature_err(
495            &tcx.sess,
496            sym::inherent_associated_types,
497            span,
498            "inherent associated types are unstable",
499        )
500        .emit();
501    }
502}
503
504pub(crate) fn type_alias_is_lazy<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
505    use hir::intravisit::Visitor;
506    if tcx.features().lazy_type_alias() {
507        return true;
508    }
509    struct HasTait;
510    impl<'tcx> Visitor<'tcx> for HasTait {
511        type Result = ControlFlow<()>;
512        fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx, AmbigArg>) -> Self::Result {
513            if let hir::TyKind::OpaqueDef(..) = t.kind {
514                ControlFlow::Break(())
515            } else {
516                hir::intravisit::walk_ty(self, t)
517            }
518        }
519    }
520    HasTait.visit_ty_unambig(tcx.hir_expect_item(def_id).expect_ty_alias().2).is_break()
521}