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.rs | 143 |
1 files changed, 80 insertions, 63 deletions
diff --git a/crates/hir-ty/src/inhabitedness.rs b/crates/hir-ty/src/inhabitedness.rs index b16b6a1178..5e742bba3e 100644 --- a/crates/hir-ty/src/inhabitedness.rs +++ b/crates/hir-ty/src/inhabitedness.rs @@ -1,58 +1,62 @@ //! Type inhabitedness logic. use std::ops::ControlFlow::{self, Break, Continue}; -use chalk_ir::{ - DebruijnIndex, - visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, -}; use hir_def::{AdtId, EnumVariantId, ModuleId, VariantId, visibility::Visibility}; use rustc_hash::FxHashSet; +use rustc_type_ir::{ + TypeSuperVisitable, TypeVisitable, TypeVisitor, + inherent::{AdtDef, IntoKind}, +}; use triomphe::Arc; use crate::{ - AliasTy, Binders, Interner, Substitution, TraitEnvironment, Ty, TyKind, - consteval::try_const_usize, db::HirDatabase, + TraitEnvironment, + consteval::try_const_usize, + db::HirDatabase, + next_solver::{ + DbInterner, EarlyBinder, GenericArgs, Ty, TyKind, + infer::{InferCtxt, traits::ObligationCause}, + obligation_ctxt::ObligationCtxt, + }, }; // 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( - db: &dyn HirDatabase, - ty: &Ty, +pub(crate) fn is_ty_uninhabited_from<'db>( + infcx: &InferCtxt<'db>, + ty: Ty<'db>, target_mod: ModuleId, - env: Arc<TraitEnvironment>, + env: Arc<TraitEnvironment<'db>>, ) -> bool { let _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered(); - let mut uninhabited_from = - UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env }; - let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST); + let mut uninhabited_from = UninhabitedFrom::new(infcx, target_mod, env); + let inhabitedness = ty.visit_with(&mut uninhabited_from); 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, +pub(crate) fn is_enum_variant_uninhabited_from<'db>( + infcx: &InferCtxt<'db>, variant: EnumVariantId, - subst: &Substitution, + subst: GenericArgs<'db>, target_mod: ModuleId, - env: Arc<TraitEnvironment>, + env: Arc<TraitEnvironment<'db>>, ) -> bool { let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered(); - let mut uninhabited_from = - UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env }; + let mut uninhabited_from = UninhabitedFrom::new(infcx, target_mod, env); let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst); inhabitedness == BREAK_VISIBLY_UNINHABITED } -struct UninhabitedFrom<'a> { +struct UninhabitedFrom<'a, 'db> { target_mod: ModuleId, - recursive_ty: FxHashSet<Ty>, + recursive_ty: FxHashSet<Ty<'db>>, // guard for preventing stack overflow in non trivial non terminating types max_depth: usize, - db: &'a dyn HirDatabase, - env: Arc<TraitEnvironment>, + infcx: &'a InferCtxt<'db>, + env: Arc<TraitEnvironment<'db>>, } const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(()); @@ -60,60 +64,73 @@ const BREAK_VISIBLY_UNINHABITED: ControlFlow<VisiblyUninhabited> = Break(Visibly #[derive(PartialEq, Eq)] struct VisiblyUninhabited; -impl TypeVisitor<Interner> for UninhabitedFrom<'_> { - type BreakTy = VisiblyUninhabited; +impl<'db> TypeVisitor<DbInterner<'db>> for UninhabitedFrom<'_, 'db> { + type Result = ControlFlow<VisiblyUninhabited>; - fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = VisiblyUninhabited> { - self - } - - fn visit_ty( - &mut self, - ty: &Ty, - outer_binder: DebruijnIndex, - ) -> ControlFlow<VisiblyUninhabited> { - if self.recursive_ty.contains(ty) || self.max_depth == 0 { + fn visit_ty(&mut self, mut ty: Ty<'db>) -> ControlFlow<VisiblyUninhabited> { + if self.recursive_ty.contains(&ty) || self.max_depth == 0 { // rustc considers recursive types always inhabited. I think it is valid to consider // recursive types as always uninhabited, but we should do what rustc is doing. return CONTINUE_OPAQUELY_INHABITED; } - self.recursive_ty.insert(ty.clone()); + self.recursive_ty.insert(ty); self.max_depth -= 1; - let r = match ty.kind(Interner) { - TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst), + + if matches!(ty.kind(), TyKind::Alias(..)) { + let mut ocx = ObligationCtxt::new(self.infcx); + match ocx.structurally_normalize_ty(&ObligationCause::dummy(), self.env.env, ty) { + Ok(it) => ty = it, + Err(_) => return CONTINUE_OPAQUELY_INHABITED, + } + } + + let r = match ty.kind() { + TyKind::Adt(adt, subst) => self.visit_adt(adt.def_id().0, subst), TyKind::Never => BREAK_VISIBLY_UNINHABITED, - TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder), - TyKind::Array(item_ty, len) => match try_const_usize(self.db, len) { + TyKind::Tuple(..) => ty.super_visit_with(self), + TyKind::Array(item_ty, len) => match try_const_usize(self.infcx.interner.db, len) { Some(0) | None => CONTINUE_OPAQUELY_INHABITED, - Some(1..) => item_ty.super_visit_with(self, outer_binder), + Some(1..) => item_ty.visit_with(self), }, - TyKind::Alias(AliasTy::Projection(projection)) => { - // FIXME: I think this currently isn't used for monomorphized bodies, so there is no need to handle - // `TyKind::AssociatedType`, but perhaps in the future it will. - let normalized = self.db.normalize_projection(projection.clone(), self.env.clone()); - self.visit_ty(&normalized, outer_binder) - } _ => CONTINUE_OPAQUELY_INHABITED, }; - self.recursive_ty.remove(ty); + self.recursive_ty.remove(&ty); self.max_depth += 1; r } +} - fn interner(&self) -> Interner { - Interner +impl<'a, 'db> UninhabitedFrom<'a, 'db> { + fn new( + infcx: &'a InferCtxt<'db>, + target_mod: ModuleId, + env: Arc<TraitEnvironment<'db>>, + ) -> Self { + Self { target_mod, recursive_ty: FxHashSet::default(), max_depth: 500, infcx, env } + } + + #[inline] + fn interner(&self) -> DbInterner<'db> { + self.infcx.interner } -} -impl UninhabitedFrom<'_> { - fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> { + #[inline] + fn db(&self) -> &'db dyn HirDatabase { + self.interner().db + } + + fn visit_adt( + &mut self, + adt: AdtId, + subst: GenericArgs<'db>, + ) -> ControlFlow<VisiblyUninhabited> { // 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) => self.visit_variant(s.into(), subst), AdtId::EnumId(e) => { - let enum_data = e.enum_variants(self.db); + let enum_data = e.enum_variants(self.db()); for &(variant, _, _) in enum_data.variants.iter() { let variant_inhabitedness = self.visit_variant(variant.into(), subst); @@ -130,17 +147,17 @@ impl UninhabitedFrom<'_> { fn visit_variant( &mut self, variant: VariantId, - subst: &Substitution, + subst: GenericArgs<'db>, ) -> ControlFlow<VisiblyUninhabited> { - let variant_data = variant.fields(self.db); + let variant_data = variant.fields(self.db()); 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 = if is_enum { None } else { Some(self.db.field_visibilities(variant)) }; + let field_tys = self.db().field_types(variant); + let field_vis = if is_enum { None } else { Some(self.db().field_visibilities(variant)) }; for (fid, _) in fields.iter() { self.visit_field(field_vis.as_ref().map(|it| it[fid]), &field_tys[fid], subst)?; @@ -151,12 +168,12 @@ impl UninhabitedFrom<'_> { fn visit_field( &mut self, vis: Option<Visibility>, - ty: &Binders<Ty>, - subst: &Substitution, + ty: &EarlyBinder<'db, Ty<'db>>, + subst: GenericArgs<'db>, ) -> ControlFlow<VisiblyUninhabited> { - if vis.is_none_or(|it| it.is_visible_from(self.db, self.target_mod)) { - let ty = ty.clone().substitute(Interner, subst); - ty.visit_with(self, DebruijnIndex::INNERMOST) + if vis.is_none_or(|it| it.is_visible_from(self.db(), self.target_mod)) { + let ty = ty.instantiate(self.interner(), subst); + ty.visit_with(self) } else { CONTINUE_OPAQUELY_INHABITED } |