Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/representability.rs')
| -rw-r--r-- | crates/hir-ty/src/representability.rs | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/crates/hir-ty/src/representability.rs b/crates/hir-ty/src/representability.rs new file mode 100644 index 0000000000..7e40f2d7ac --- /dev/null +++ b/crates/hir-ty/src/representability.rs @@ -0,0 +1,131 @@ +//! Detecting whether a type is infinitely-sized. + +use hir_def::{AdtId, VariantId}; +use rustc_type_ir::inherent::{AdtDef, IntoKind}; + +use crate::{ + db::HirDatabase, + next_solver::{GenericArgKind, GenericArgs, Ty, TyKind}, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum Representability { + Representable, + Infinite, +} + +macro_rules! rtry { + ($e:expr) => { + match $e { + e @ Representability::Infinite => return e, + Representability::Representable => {} + } + }; +} + +#[salsa::tracked(cycle_result = representability_cycle)] +pub(crate) fn representability(db: &dyn HirDatabase, id: AdtId) -> Representability { + match id { + AdtId::StructId(id) => variant_representability(db, id.into()), + AdtId::UnionId(id) => variant_representability(db, id.into()), + AdtId::EnumId(id) => { + for &(variant, ..) in &id.enum_variants(db).variants { + rtry!(variant_representability(db, variant.into())); + } + Representability::Representable + } + } +} + +pub(crate) fn representability_cycle( + _db: &dyn HirDatabase, + _: salsa::Id, + _id: AdtId, +) -> Representability { + Representability::Infinite +} + +fn variant_representability(db: &dyn HirDatabase, id: VariantId) -> Representability { + for ty in db.field_types(id).values() { + rtry!(representability_ty(db, ty.get().instantiate_identity())); + } + Representability::Representable +} + +fn representability_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>) -> Representability { + match ty.kind() { + TyKind::Adt(adt_id, args) => representability_adt_ty(db, adt_id.def_id().0, args), + // FIXME(#11924) allow zero-length arrays? + TyKind::Array(ty, _) => representability_ty(db, ty), + TyKind::Tuple(tys) => { + for ty in tys { + rtry!(representability_ty(db, ty)); + } + Representability::Representable + } + _ => Representability::Representable, + } +} + +fn representability_adt_ty<'db>( + db: &'db dyn HirDatabase, + def_id: AdtId, + args: GenericArgs<'db>, +) -> Representability { + rtry!(representability(db, def_id)); + + // At this point, we know that the item of the ADT type is representable; + // but the type parameters may cause a cycle with an upstream type + let params_in_repr = params_in_repr(db, def_id); + for (i, arg) in args.iter().enumerate() { + if let GenericArgKind::Type(ty) = arg.kind() + && params_in_repr[i] + { + rtry!(representability_ty(db, ty)); + } + } + Representability::Representable +} + +fn params_in_repr(db: &dyn HirDatabase, def_id: AdtId) -> Box<[bool]> { + let generics = db.generic_params(def_id.into()); + let mut params_in_repr = (0..generics.len_lifetimes() + generics.len_type_or_consts()) + .map(|_| false) + .collect::<Box<[bool]>>(); + let mut handle_variant = |variant| { + for field in db.field_types(variant).values() { + params_in_repr_ty(db, field.get().instantiate_identity(), &mut params_in_repr); + } + }; + match def_id { + AdtId::StructId(def_id) => handle_variant(def_id.into()), + AdtId::UnionId(def_id) => handle_variant(def_id.into()), + AdtId::EnumId(def_id) => { + for &(variant, ..) in &def_id.enum_variants(db).variants { + handle_variant(variant.into()); + } + } + } + params_in_repr +} + +fn params_in_repr_ty<'db>(db: &'db dyn HirDatabase, ty: Ty<'db>, params_in_repr: &mut [bool]) { + match ty.kind() { + TyKind::Adt(adt, args) => { + let inner_params_in_repr = self::params_in_repr(db, adt.def_id().0); + for (i, arg) in args.iter().enumerate() { + if let GenericArgKind::Type(ty) = arg.kind() + && inner_params_in_repr[i] + { + params_in_repr_ty(db, ty, params_in_repr); + } + } + } + TyKind::Array(ty, _) => params_in_repr_ty(db, ty, params_in_repr), + TyKind::Tuple(tys) => tys.iter().for_each(|ty| params_in_repr_ty(db, ty, params_in_repr)), + TyKind::Param(param) => { + params_in_repr[param.index as usize] = true; + } + _ => {} + } +} |