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