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