Skip to main content

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