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.rs131
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;
+ }
+ _ => {}
+ }
+}