From 92eef5348ac32ac8934bc9397f11b1fd6bdb2eea Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Sun, 12 Oct 2025 11:13:11 +0300 Subject: [PATCH] Migrate inhabitedness checking to the new solver --- .../diagnostics/match_check/pat_analysis.rs | 29 +++- .../crates/hir-ty/src/inhabitedness.rs | 150 ++++++++++-------- .../crates/hir-ty/src/mir/lower.rs | 6 +- .../crates/hir-ty/src/next_solver/def_id.rs | 38 ++++- .../crates/hir-ty/src/next_solver/interner.rs | 4 +- 5 files changed, 139 insertions(+), 88 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs index eb20d3c51ff4..76f50c194835 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs @@ -19,6 +19,11 @@ use crate::{ db::HirDatabase, infer::normalize, inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from}, + next_solver::{ + DbInterner, TypingMode, + infer::{DbInternerInferExt, InferCtxt}, + mapping::ChalkToNextSolver, + }, }; use super::{FieldPat, Pat, PatKind}; @@ -28,7 +33,7 @@ use Constructor::*; // Re-export r-a-specific versions of all these types. pub(crate) type DeconstructedPat<'db> = rustc_pattern_analysis::pat::DeconstructedPat>; -pub(crate) type MatchArm<'db> = rustc_pattern_analysis::MatchArm<'db, MatchCheckCtx<'db>>; +pub(crate) type MatchArm<'a, 'db> = rustc_pattern_analysis::MatchArm<'a, MatchCheckCtx<'db>>; pub(crate) type WitnessPat<'db> = rustc_pattern_analysis::pat::WitnessPat>; /// [Constructor] uses this in unimplemented variants. @@ -71,6 +76,7 @@ pub(crate) struct MatchCheckCtx<'db> { pub(crate) db: &'db dyn HirDatabase, exhaustive_patterns: bool, env: Arc>, + infcx: InferCtxt<'db>, } impl<'db> MatchCheckCtx<'db> { @@ -82,15 +88,17 @@ impl<'db> MatchCheckCtx<'db> { ) -> Self { let def_map = module.crate_def_map(db); let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns); - Self { module, body, db, exhaustive_patterns, env } + let interner = DbInterner::new_with(db, Some(env.krate), env.block); + let infcx = interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, body.into())); + Self { module, body, db, exhaustive_patterns, env, infcx } } - pub(crate) fn compute_match_usefulness( + pub(crate) fn compute_match_usefulness<'a>( &self, - arms: &[MatchArm<'db>], + arms: &[MatchArm<'a, 'db>], scrut_ty: Ty, known_valid_scrutinee: Option, - ) -> Result, ()> { + ) -> Result, ()> { if scrut_ty.contains_unknown() { return Err(()); } @@ -107,7 +115,12 @@ impl<'db> MatchCheckCtx<'db> { } fn is_uninhabited(&self, ty: &Ty) -> bool { - is_ty_uninhabited_from(self.db, ty, self.module, self.env.clone()) + is_ty_uninhabited_from( + &self.infcx, + ty.to_nextsolver(self.infcx.interner), + self.module, + self.env.clone(), + ) } /// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`. @@ -429,9 +442,9 @@ impl PatCx for MatchCheckCtx<'_> { let mut variants = IndexVec::with_capacity(enum_data.variants.len()); for &(variant, _, _) in enum_data.variants.iter() { let is_uninhabited = is_enum_variant_uninhabited_from( - cx.db, + &cx.infcx, variant, - subst, + subst.to_nextsolver(cx.infcx.interner), cx.module, self.env.clone(), ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs index 826f19cf0b68..7ebc2df6f75d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs @@ -1,60 +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, + TraitEnvironment, consteval::try_const_usize, db::HirDatabase, - next_solver::{DbInterner, mapping::ChalkToNextSolver}, + 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>, + env: Arc>, ) -> 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>, + env: Arc>, ) -> 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, + recursive_ty: FxHashSet>, // guard for preventing stack overflow in non trivial non terminating types max_depth: usize, - db: &'a dyn HirDatabase, - env: Arc>, + infcx: &'a InferCtxt<'db>, + env: Arc>, } const CONTINUE_OPAQUELY_INHABITED: ControlFlow = Continue(()); @@ -62,63 +64,73 @@ const BREAK_VISIBLY_UNINHABITED: ControlFlow = Break(Visibly #[derive(PartialEq, Eq)] struct VisiblyUninhabited; -impl TypeVisitor for UninhabitedFrom<'_> { - type BreakTy = VisiblyUninhabited; +impl<'db> TypeVisitor> for UninhabitedFrom<'_, 'db> { + type Result = ControlFlow; - fn as_dyn(&mut self) -> &mut dyn TypeVisitor { - self - } - - fn visit_ty( - &mut self, - ty: &Ty, - outer_binder: DebruijnIndex, - ) -> ControlFlow { - if self.recursive_ty.contains(ty) || self.max_depth == 0 { + fn visit_ty(&mut self, mut ty: Ty<'db>) -> ControlFlow { + 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 interner = DbInterner::new_with(self.db, None, None); - 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.to_nextsolver(interner)) { - Some(0) | None => CONTINUE_OPAQUELY_INHABITED, - Some(1..) => item_ty.super_visit_with(self, outer_binder), - } - } - 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) - } + 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), + }, _ => CONTINUE_OPAQUELY_INHABITED, }; - self.recursive_ty.remove(ty); + self.recursive_ty.remove(&ty); self.max_depth += 1; r } - - fn interner(&self) -> Interner { - Interner - } } -impl UninhabitedFrom<'_> { - fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow { +impl<'a, 'db> UninhabitedFrom<'a, 'db> { + fn new( + infcx: &'a InferCtxt<'db>, + target_mod: ModuleId, + env: Arc>, + ) -> Self { + Self { target_mod, recursive_ty: FxHashSet::default(), max_depth: 500, infcx, env } + } + + #[inline] + fn interner(&self) -> DbInterner<'db> { + self.infcx.interner + } + + #[inline] + fn db(&self) -> &'db dyn HirDatabase { + self.interner().db + } + + fn visit_adt( + &mut self, + adt: AdtId, + subst: GenericArgs<'db>, + ) -> ControlFlow { // 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); @@ -135,17 +147,17 @@ impl UninhabitedFrom<'_> { fn visit_variant( &mut self, variant: VariantId, - subst: &Substitution, + subst: GenericArgs<'db>, ) -> ControlFlow { - 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_ns(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)?; @@ -156,12 +168,12 @@ impl UninhabitedFrom<'_> { fn visit_field( &mut self, vis: Option, - ty: &Binders, - subst: &Substitution, + ty: &EarlyBinder<'db, Ty<'db>>, + subst: GenericArgs<'db>, ) -> ControlFlow { - 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 } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 92f9cd42615e..1439c43e99e8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -43,7 +43,6 @@ use crate::{ next_solver::{ Const, DbInterner, ParamConst, Region, TyKind, TypingMode, UnevaluatedConst, infer::{DbInternerInferExt, InferCtxt}, - mapping::NextSolverToChalk, }, traits::FnTrait, }; @@ -303,6 +302,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { let resolver = owner.resolver(db); let env = db.trait_environment_for_body(owner); let interner = DbInterner::new_with(db, Some(env.krate), env.block); + // FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body? let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); MirLowerCtx { @@ -1766,8 +1766,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn is_uninhabited(&self, expr_id: ExprId) -> bool { is_ty_uninhabited_from( - self.db, - &self.infer[expr_id].to_chalk(self.interner()), + &self.infcx, + self.infer[expr_id], self.owner.module(self.db), self.env.clone(), ) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs index 789be3b731b1..8525d4bc96e6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/def_id.rs @@ -1,8 +1,8 @@ //! Definition of `SolverDefId` use hir_def::{ - AdtId, CallableDefId, ConstId, EnumId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, - ImplId, StaticId, StructId, TraitId, TypeAliasId, UnionId, + AdtId, CallableDefId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FunctionId, + GeneralConstId, GenericDefId, ImplId, StaticId, StructId, TraitId, TypeAliasId, UnionId, }; use rustc_type_ir::inherent; use stdx::impl_from; @@ -29,6 +29,8 @@ pub enum SolverDefId { InternedClosureId(InternedClosureId), InternedCoroutineId(InternedCoroutineId), InternedOpaqueTyId(InternedOpaqueTyId), + EnumVariantId(EnumVariantId), + // FIXME(next-solver): Do we need the separation of `Ctor`? It duplicates some variants. Ctor(Ctor), } @@ -73,6 +75,16 @@ impl std::fmt::Debug for SolverDefId { SolverDefId::InternedOpaqueTyId(id) => { f.debug_tuple("InternedOpaqueTyId").field(&id).finish() } + SolverDefId::EnumVariantId(id) => { + let parent_enum = id.loc(db).parent; + f.debug_tuple("EnumVariantId") + .field(&format_args!( + "\"{}::{}\"", + db.enum_signature(parent_enum).name.as_str(), + parent_enum.enum_variants(db).variant_name_by_id(id).unwrap().as_str() + )) + .finish() + } SolverDefId::Ctor(Ctor::Struct(id)) => { f.debug_tuple("Ctor").field(&db.struct_signature(id).name.as_str()).finish() } @@ -101,6 +113,7 @@ impl_from!( InternedClosureId, InternedCoroutineId, InternedOpaqueTyId, + EnumVariantId, Ctor for SolverDefId ); @@ -129,6 +142,18 @@ impl From for SolverDefId { } } +impl From for SolverDefId { + #[inline] + fn from(value: DefWithBodyId) -> Self { + match value { + DefWithBodyId::FunctionId(id) => id.into(), + DefWithBodyId::StaticId(id) => id.into(), + DefWithBodyId::ConstId(id) => id.into(), + DefWithBodyId::VariantId(id) => id.into(), + } + } +} + impl TryFrom for GenericDefId { type Error = SolverDefId; @@ -141,10 +166,11 @@ impl TryFrom for GenericDefId { SolverDefId::StaticId(static_id) => GenericDefId::StaticId(static_id), SolverDefId::TraitId(trait_id) => GenericDefId::TraitId(trait_id), SolverDefId::TypeAliasId(type_alias_id) => GenericDefId::TypeAliasId(type_alias_id), - SolverDefId::InternedClosureId(_) => return Err(value), - SolverDefId::InternedCoroutineId(_) => return Err(value), - SolverDefId::InternedOpaqueTyId(_) => return Err(value), - SolverDefId::Ctor(_) => return Err(value), + SolverDefId::InternedClosureId(_) + | SolverDefId::InternedCoroutineId(_) + | SolverDefId::InternedOpaqueTyId(_) + | SolverDefId::EnumVariantId(_) + | SolverDefId::Ctor(_) => return Err(value), }) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 3fd8e7b39dd7..cfa8b5b8a7f7 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -1211,6 +1211,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { | SolverDefId::AdtId(_) | SolverDefId::TraitId(_) | SolverDefId::ImplId(_) + | SolverDefId::EnumVariantId(..) | SolverDefId::Ctor(..) | SolverDefId::InternedOpaqueTyId(..) => panic!(), }; @@ -1969,8 +1970,7 @@ impl<'db> rustc_type_ir::Interner for DbInterner<'db> { self, defining_anchor: Self::LocalDefId, ) -> Self::LocalDefIds { - // FIXME(next-solver) - unimplemented!() + Default::default() } type Probe = rustc_type_ir::solve::inspect::Probe>;