rustc_hir_analysis/collect/
type_of.rs

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