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