Skip to main content

hir_ty/
representability.rs

1//! Detecting whether a type is infinitely-sized.
2
3use hir_def::{AdtId, VariantId, hir::generics::GenericParams};
4use rustc_type_ir::inherent::IntoKind;
5
6use crate::{
7    db::HirDatabase,
8    next_solver::{GenericArgKind, GenericArgs, Ty, TyKind},
9};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub(crate) enum Representability {
13    Representable,
14    Infinite,
15}
16
17macro_rules! rtry {
18    ($e:expr) => {
19        match $e {
20            e @ Representability::Infinite => return e,
21            Representability::Representable => {}
22        }
23    };
24}
25
26#[salsa::tracked(cycle_result = representability_cycle, returns(copy))]
27pub(crate) fn representability(db: &dyn HirDatabase, id: AdtId) -> Representability {
28    match id {
29        AdtId::StructId(id) => variant_representability(db, id.into()),
30        AdtId::UnionId(id) => variant_representability(db, id.into()),
31        AdtId::EnumId(id) => {
32            for &(variant, ..) in id.enum_variants(db).variants.values() {
33                rtry!(variant_representability(db, variant.into()));
34            }
35            Representability::Representable
36        }
37    }
38}
39
40pub(crate) fn representability_cycle(
41    _db: &dyn HirDatabase,
42    _: salsa::Id,
43    _id: AdtId,
44) -> Representability {
45    Representability::Infinite
46}
47
48fn variant_representability(db: &dyn HirDatabase, id: VariantId) -> Representability {
49    for ty in db.field_types(id).values() {
50        rtry!(representability_ty(db, ty.ty().instantiate_identity().skip_norm_wip()));
51    }
52    Representability::Representable
53}
54
55fn representability_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>) -> Representability {
56    match ty.kind() {
57        TyKind::Adt(adt_id, args) => representability_adt_ty(db, adt_id.def_id(), args),
58        // FIXME(#11924) allow zero-length arrays?
59        TyKind::Array(ty, _) => representability_ty(db, ty),
60        TyKind::Tuple(tys) => {
61            for ty in tys {
62                rtry!(representability_ty(db, ty));
63            }
64            Representability::Representable
65        }
66        _ => Representability::Representable,
67    }
68}
69
70fn representability_adt_ty<'db>(
71    db: &'db dyn HirDatabase,
72    def_id: AdtId,
73    args: GenericArgs<'db>,
74) -> Representability {
75    rtry!(representability(db, def_id));
76
77    // At this point, we know that the item of the ADT type is representable;
78    // but the type parameters may cause a cycle with an upstream type
79    let params_in_repr = params_in_repr(db, def_id);
80    for (i, arg) in args.iter().enumerate() {
81        if let GenericArgKind::Type(ty) = arg.kind()
82            && params_in_repr[i]
83        {
84            rtry!(representability_ty(db, ty));
85        }
86    }
87    Representability::Representable
88}
89
90fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> {
91    let generics = GenericParams::of(db, def_id.into());
92    let mut params_in_repr = (0..generics.len_early_bound_lifetimes()
93        + generics.len_type_or_consts())
94        .map(|_| false)
95        .collect::<Box<[bool]>>();
96    let mut handle_variant = |variant| {
97        for field in db.field_types(variant).values() {
98            params_in_repr_ty(
99                db,
100                field.ty().instantiate_identity().skip_norm_wip(),
101                &mut params_in_repr,
102            );
103        }
104    };
105    match def_id {
106        AdtId::StructId(def_id) => handle_variant(def_id.into()),
107        AdtId::UnionId(def_id) => handle_variant(def_id.into()),
108        AdtId::EnumId(def_id) => {
109            for &(variant, ..) in def_id.enum_variants(db).variants.values() {
110                handle_variant(variant.into());
111            }
112        }
113    }
114    params_in_repr
115}
116
117fn params_in_repr_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>, params_in_repr: &mut [bool]) {
118    match ty.kind() {
119        TyKind::Adt(adt, args) => {
120            let inner_params_in_repr = self::params_in_repr(db, adt.def_id());
121            for (i, arg) in args.iter().enumerate() {
122                if let GenericArgKind::Type(ty) = arg.kind()
123                    && inner_params_in_repr[i]
124                {
125                    params_in_repr_ty(db, ty, params_in_repr);
126                }
127            }
128        }
129        TyKind::Array(ty, _) => params_in_repr_ty(db, ty, params_in_repr),
130        TyKind::Tuple(tys) => tys.iter().for_each(|ty| params_in_repr_ty(db, ty, params_in_repr)),
131        TyKind::Param(param) => {
132            params_in_repr[param.index as usize] = true;
133        }
134        _ => {}
135    }
136}