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)]
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_lifetimes() + generics.len_type_or_consts())
93        .map(|_| false)
94        .collect::<Box<[bool]>>();
95    let mut handle_variant = |variant| {
96        for field in db.field_types(variant).values() {
97            params_in_repr_ty(
98                db,
99                field.ty().instantiate_identity().skip_norm_wip(),
100                &mut params_in_repr,
101            );
102        }
103    };
104    match def_id {
105        AdtId::StructId(def_id) => handle_variant(def_id.into()),
106        AdtId::UnionId(def_id) => handle_variant(def_id.into()),
107        AdtId::EnumId(def_id) => {
108            for &(variant, ..) in def_id.enum_variants(db).variants.values() {
109                handle_variant(variant.into());
110            }
111        }
112    }
113    params_in_repr
114}
115
116fn params_in_repr_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>, params_in_repr: &mut [bool]) {
117    match ty.kind() {
118        TyKind::Adt(adt, args) => {
119            let inner_params_in_repr = self::params_in_repr(db, adt.def_id());
120            for (i, arg) in args.iter().enumerate() {
121                if let GenericArgKind::Type(ty) = arg.kind()
122                    && inner_params_in_repr[i]
123                {
124                    params_in_repr_ty(db, ty, params_in_repr);
125                }
126            }
127        }
128        TyKind::Array(ty, _) => params_in_repr_ty(db, ty, params_in_repr),
129        TyKind::Tuple(tys) => tys.iter().for_each(|ty| params_in_repr_ty(db, ty, params_in_repr)),
130        TyKind::Param(param) => {
131            params_in_repr[param.index as usize] = true;
132        }
133        _ => {}
134    }
135}