Skip to main content

rustc_ty_utils/
representability.rs

1use rustc_hir::def::DefKind;
2use rustc_index::bit_set::DenseBitSet;
3use rustc_middle::bug;
4use rustc_middle::query::Providers;
5use rustc_middle::ty::{self, Representability, Ty, TyCtxt};
6use rustc_span::def_id::LocalDefId;
7
8pub(crate) fn provide(providers: &mut Providers) {
9    *providers = Providers {
10        check_representability,
11        check_representability_adt_ty,
12        params_in_repr,
13        ..*providers
14    };
15}
16
17fn check_representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability {
18    match tcx.def_kind(def_id) {
19        DefKind::Struct | DefKind::Union | DefKind::Enum => {
20            for variant in tcx.adt_def(def_id).variants() {
21                for field in variant.fields.iter() {
22                    let _ = tcx.check_representability(field.did.expect_local());
23                }
24            }
25        }
26        DefKind::Field => {
27            check_representability_ty(tcx, tcx.type_of(def_id).instantiate_identity());
28        }
29        def_kind => ::rustc_middle::util::bug::bug_fmt(format_args!("unexpected {0:?}", def_kind))bug!("unexpected {def_kind:?}"),
30    }
31    Representability
32}
33
34fn check_representability_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) {
35    match *ty.kind() {
36        // This one must be a query rather than a vanilla `check_representability_adt_ty` call. See
37        // the comment on `check_representability_adt_ty` below for why.
38        ty::Adt(..) => {
39            let _ = tcx.check_representability_adt_ty(ty);
40        }
41        // FIXME(#11924) allow zero-length arrays?
42        ty::Array(ty, _) => {
43            check_representability_ty(tcx, ty);
44        }
45        ty::Tuple(tys) => {
46            for ty in tys {
47                check_representability_ty(tcx, ty);
48            }
49        }
50        _ => {}
51    }
52}
53
54// The reason for this being a separate query is very subtle. Consider this
55// infinitely sized struct: `struct Foo(Box<Foo>, Bar<Foo>)`. When calling
56// check_representability(Foo), a query cycle will occur:
57//
58//   check_representability(Foo)
59//     -> check_representability_adt_ty(Bar<Foo>)
60//     -> check_representability(Foo)
61//
62// For the diagnostic output (in `Value::from_cycle_error`), we want to detect
63// that the `Foo` in the *second* field of the struct is culpable. This
64// requires traversing the HIR of the struct and calling `params_in_repr(Bar)`.
65// But we can't call params_in_repr for a given type unless it is known to be
66// representable. params_in_repr will cycle/panic on infinitely sized types.
67// Looking at the query cycle above, we know that `Bar` is representable
68// because `check_representability_adt_ty(Bar<..>)` is in the cycle and
69// `check_representability(Bar)` is *not* in the cycle.
70fn check_representability_adt_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Representability {
71    let ty::Adt(adt, args) = ty.kind() else { ::rustc_middle::util::bug::bug_fmt(format_args!("expected adt"))bug!("expected adt") };
72    if let Some(def_id) = adt.did().as_local() {
73        let _ = tcx.check_representability(def_id);
74    }
75    // At this point, we know that the item of the ADT type is representable;
76    // but the type parameters may cause a cycle with an upstream type
77    let params_in_repr = tcx.params_in_repr(adt.did());
78    for (i, arg) in args.iter().enumerate() {
79        if let ty::GenericArgKind::Type(ty) = arg.kind() {
80            if params_in_repr.contains(i as u32) {
81                check_representability_ty(tcx, ty);
82            }
83        }
84    }
85    Representability
86}
87
88fn params_in_repr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DenseBitSet<u32> {
89    let adt_def = tcx.adt_def(def_id);
90    let generics = tcx.generics_of(def_id);
91    let mut params_in_repr = DenseBitSet::new_empty(generics.own_params.len());
92    for variant in adt_def.variants() {
93        for field in variant.fields.iter() {
94            params_in_repr_ty(
95                tcx,
96                tcx.type_of(field.did).instantiate_identity(),
97                &mut params_in_repr,
98            );
99        }
100    }
101    params_in_repr
102}
103
104fn params_in_repr_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, params_in_repr: &mut DenseBitSet<u32>) {
105    match *ty.kind() {
106        ty::Adt(adt, args) => {
107            let inner_params_in_repr = tcx.params_in_repr(adt.did());
108            for (i, arg) in args.iter().enumerate() {
109                if let ty::GenericArgKind::Type(ty) = arg.kind() {
110                    if inner_params_in_repr.contains(i as u32) {
111                        params_in_repr_ty(tcx, ty, params_in_repr);
112                    }
113                }
114            }
115        }
116        ty::Array(ty, _) => params_in_repr_ty(tcx, ty, params_in_repr),
117        ty::Tuple(tys) => tys.iter().for_each(|ty| params_in_repr_ty(tcx, ty, params_in_repr)),
118        ty::Param(param) => {
119            params_in_repr.insert(param.index);
120        }
121        _ => {}
122    }
123}