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