//! 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::>(); 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; } _ => {} } }