Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/inhabitedness.rs')
-rw-r--r--crates/hir-ty/src/inhabitedness.rs68
1 files changed, 24 insertions, 44 deletions
diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs
index 532b650e8f..7546369d8d 100644
--- a/crates/hir-ty/src/inhabitedness.rs
+++ b/crates/hir-ty/src/inhabitedness.rs
@@ -5,42 +5,36 @@ use chalk_ir::{
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
DebruijnIndex,
};
-use hir_def::{
- attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule,
- ModuleId, VariantId,
-};
+use hir_def::{visibility::Visibility, AdtId, EnumVariantId, HasModule, ModuleId, VariantId};
use rustc_hash::FxHashSet;
use crate::{
consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
};
+// FIXME: Turn this into a query, it can be quite slow
/// Checks whether a type is visibly uninhabited from a particular module.
-pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
+pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool {
+ let _p = tracing::span!(tracing::Level::INFO, "is_ty_uninhabited_from", ?ty).entered();
let mut uninhabited_from =
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
inhabitedness == BREAK_VISIBLY_UNINHABITED
}
+// FIXME: Turn this into a query, it can be quite slow
/// Checks whether a variant is visibly uninhabited from a particular module.
pub(crate) fn is_enum_variant_uninhabited_from(
+ db: &dyn HirDatabase,
variant: EnumVariantId,
subst: &Substitution,
target_mod: ModuleId,
- db: &dyn HirDatabase,
) -> bool {
- let is_local = variant.module(db.upcast()).krate() == target_mod.krate();
+ let _p = tracing::span!(tracing::Level::INFO, "is_enum_variant_uninhabited_from").entered();
let mut uninhabited_from =
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
- let inhabitedness = uninhabited_from.visit_variant(
- variant.into(),
- &db.enum_variant_data(variant).variant_data,
- subst,
- &db.attrs(variant.into()),
- is_local,
- );
+ let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst);
inhabitedness == BREAK_VISIBLY_UNINHABITED
}
@@ -98,34 +92,18 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
impl UninhabitedFrom<'_> {
fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> {
- let attrs = self.db.attrs(adt.into());
- let adt_non_exhaustive = attrs.by_key("non_exhaustive").exists();
- let is_local = adt.module(self.db.upcast()).krate() == self.target_mod.krate();
- if adt_non_exhaustive && !is_local {
- return CONTINUE_OPAQUELY_INHABITED;
- }
-
// An ADT is uninhabited iff all its variants uninhabited.
match adt {
// rustc: For now, `union`s are never considered uninhabited.
AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
- AdtId::StructId(s) => {
- let struct_data = self.db.struct_data(s);
- self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local)
- }
+ AdtId::StructId(s) => self.visit_variant(s.into(), subst),
AdtId::EnumId(e) => {
let enum_data = self.db.enum_data(e);
for &(variant, _) in enum_data.variants.iter() {
- let variant_inhabitedness = self.visit_variant(
- variant.into(),
- &self.db.enum_variant_data(variant).variant_data,
- subst,
- &self.db.attrs(variant.into()),
- is_local,
- );
+ let variant_inhabitedness = self.visit_variant(variant.into(), subst);
match variant_inhabitedness {
- Break(VisiblyUninhabited) => continue,
+ Break(VisiblyUninhabited) => (),
Continue(()) => return CONTINUE_OPAQUELY_INHABITED,
}
}
@@ -137,34 +115,36 @@ impl UninhabitedFrom<'_> {
fn visit_variant(
&mut self,
variant: VariantId,
- variant_data: &VariantData,
subst: &Substitution,
- attrs: &Attrs,
- is_local: bool,
) -> ControlFlow<VisiblyUninhabited> {
- let non_exhaustive_field_list = attrs.by_key("non_exhaustive").exists();
- if non_exhaustive_field_list && !is_local {
+ let is_local = variant.krate(self.db.upcast()) == self.target_mod.krate();
+ if !is_local && self.db.attrs(variant.into()).by_key("non_exhaustive").exists() {
+ return CONTINUE_OPAQUELY_INHABITED;
+ }
+
+ let variant_data = self.db.variant_data(variant);
+ let fields = variant_data.fields();
+ if fields.is_empty() {
return CONTINUE_OPAQUELY_INHABITED;
}
let is_enum = matches!(variant, VariantId::EnumVariantId(..));
let field_tys = self.db.field_types(variant);
- let field_vis = self.db.field_visibilities(variant);
+ let field_vis = if is_enum { None } else { Some(self.db.field_visibilities(variant)) };
- for (fid, _) in variant_data.fields().iter() {
- self.visit_field(field_vis[fid], &field_tys[fid], subst, is_enum)?;
+ for (fid, _) in fields.iter() {
+ self.visit_field(field_vis.as_ref().map(|it| it[fid]), &field_tys[fid], subst)?;
}
CONTINUE_OPAQUELY_INHABITED
}
fn visit_field(
&mut self,
- vis: Visibility,
+ vis: Option<Visibility>,
ty: &Binders<Ty>,
subst: &Substitution,
- is_enum: bool,
) -> ControlFlow<VisiblyUninhabited> {
- if is_enum || vis.is_visible_from(self.db.upcast(), self.target_mod) {
+ if vis.map_or(true, |it| it.is_visible_from(self.db.upcast(), self.target_mod)) {
let ty = ty.clone().substitute(Interner, subst);
ty.visit_with(self, DebruijnIndex::INNERMOST)
} else {