Skip to content

Commit 26c1717

Browse files
Migrate inhabitedness checking to the new solver
1 parent c5181db commit 26c1717

File tree

5 files changed

+138
-87
lines changed

5 files changed

+138
-87
lines changed

crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ use crate::{
1919
db::HirDatabase,
2020
infer::normalize,
2121
inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from},
22+
next_solver::{
23+
DbInterner, TypingMode,
24+
infer::{DbInternerInferExt, InferCtxt},
25+
mapping::ChalkToNextSolver,
26+
},
2227
};
2328

2429
use super::{FieldPat, Pat, PatKind};
@@ -28,7 +33,7 @@ use Constructor::*;
2833
// Re-export r-a-specific versions of all these types.
2934
pub(crate) type DeconstructedPat<'db> =
3035
rustc_pattern_analysis::pat::DeconstructedPat<MatchCheckCtx<'db>>;
31-
pub(crate) type MatchArm<'db> = rustc_pattern_analysis::MatchArm<'db, MatchCheckCtx<'db>>;
36+
pub(crate) type MatchArm<'a, 'db> = rustc_pattern_analysis::MatchArm<'a, MatchCheckCtx<'db>>;
3237
pub(crate) type WitnessPat<'db> = rustc_pattern_analysis::pat::WitnessPat<MatchCheckCtx<'db>>;
3338

3439
/// [Constructor] uses this in unimplemented variants.
@@ -71,6 +76,7 @@ pub(crate) struct MatchCheckCtx<'db> {
7176
pub(crate) db: &'db dyn HirDatabase,
7277
exhaustive_patterns: bool,
7378
env: Arc<TraitEnvironment<'db>>,
79+
infcx: InferCtxt<'db>,
7480
}
7581

7682
impl<'db> MatchCheckCtx<'db> {
@@ -82,15 +88,17 @@ impl<'db> MatchCheckCtx<'db> {
8288
) -> Self {
8389
let def_map = module.crate_def_map(db);
8490
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
85-
Self { module, body, db, exhaustive_patterns, env }
91+
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
92+
let infcx = interner.infer_ctxt().build(TypingMode::typeck_for_body(interner, body.into()));
93+
Self { module, body, db, exhaustive_patterns, env, infcx }
8694
}
8795

88-
pub(crate) fn compute_match_usefulness(
96+
pub(crate) fn compute_match_usefulness<'a>(
8997
&self,
90-
arms: &[MatchArm<'db>],
98+
arms: &[MatchArm<'a, 'db>],
9199
scrut_ty: Ty,
92100
known_valid_scrutinee: Option<bool>,
93-
) -> Result<UsefulnessReport<'db, Self>, ()> {
101+
) -> Result<UsefulnessReport<'a, Self>, ()> {
94102
if scrut_ty.contains_unknown() {
95103
return Err(());
96104
}
@@ -107,7 +115,12 @@ impl<'db> MatchCheckCtx<'db> {
107115
}
108116

109117
fn is_uninhabited(&self, ty: &Ty) -> bool {
110-
is_ty_uninhabited_from(self.db, ty, self.module, self.env.clone())
118+
is_ty_uninhabited_from(
119+
&self.infcx,
120+
ty.to_nextsolver(self.infcx.interner),
121+
self.module,
122+
self.env.clone(),
123+
)
111124
}
112125

113126
/// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
@@ -429,9 +442,9 @@ impl PatCx for MatchCheckCtx<'_> {
429442
let mut variants = IndexVec::with_capacity(enum_data.variants.len());
430443
for &(variant, _, _) in enum_data.variants.iter() {
431444
let is_uninhabited = is_enum_variant_uninhabited_from(
432-
cx.db,
445+
&cx.infcx,
433446
variant,
434-
subst,
447+
subst.to_nextsolver(cx.infcx.interner),
435448
cx.module,
436449
self.env.clone(),
437450
);

crates/hir-ty/src/inhabitedness.rs

Lines changed: 80 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,136 @@
11
//! Type inhabitedness logic.
22
use std::ops::ControlFlow::{self, Break, Continue};
33

4-
use chalk_ir::{
5-
DebruijnIndex,
6-
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
7-
};
84
use hir_def::{AdtId, EnumVariantId, ModuleId, VariantId, visibility::Visibility};
95
use rustc_hash::FxHashSet;
6+
use rustc_type_ir::{
7+
TypeSuperVisitable, TypeVisitable, TypeVisitor,
8+
inherent::{AdtDef, IntoKind},
9+
};
1010
use triomphe::Arc;
1111

1212
use crate::{
13-
AliasTy, Binders, Interner, Substitution, TraitEnvironment, Ty, TyKind,
13+
TraitEnvironment,
1414
consteval::try_const_usize,
1515
db::HirDatabase,
16-
next_solver::{DbInterner, mapping::ChalkToNextSolver},
16+
next_solver::{
17+
DbInterner, EarlyBinder, GenericArgs, Ty, TyKind,
18+
infer::{InferCtxt, traits::ObligationCause},
19+
obligation_ctxt::ObligationCtxt,
20+
},
1721
};
1822

1923
// FIXME: Turn this into a query, it can be quite slow
2024
/// Checks whether a type is visibly uninhabited from a particular module.
21-
pub(crate) fn is_ty_uninhabited_from(
22-
db: &dyn HirDatabase,
23-
ty: &Ty,
25+
pub(crate) fn is_ty_uninhabited_from<'db>(
26+
infcx: &InferCtxt<'db>,
27+
ty: Ty<'db>,
2428
target_mod: ModuleId,
25-
env: Arc<TraitEnvironment<'_>>,
29+
env: Arc<TraitEnvironment<'db>>,
2630
) -> bool {
2731
let _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered();
28-
let mut uninhabited_from =
29-
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env };
30-
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
32+
let mut uninhabited_from = UninhabitedFrom::new(infcx, target_mod, env);
33+
let inhabitedness = ty.visit_with(&mut uninhabited_from);
3134
inhabitedness == BREAK_VISIBLY_UNINHABITED
3235
}
3336

3437
// FIXME: Turn this into a query, it can be quite slow
3538
/// Checks whether a variant is visibly uninhabited from a particular module.
36-
pub(crate) fn is_enum_variant_uninhabited_from(
37-
db: &dyn HirDatabase,
39+
pub(crate) fn is_enum_variant_uninhabited_from<'db>(
40+
infcx: &InferCtxt<'db>,
3841
variant: EnumVariantId,
39-
subst: &Substitution,
42+
subst: GenericArgs<'db>,
4043
target_mod: ModuleId,
41-
env: Arc<TraitEnvironment<'_>>,
44+
env: Arc<TraitEnvironment<'db>>,
4245
) -> bool {
4346
let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered();
4447

45-
let mut uninhabited_from =
46-
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env };
48+
let mut uninhabited_from = UninhabitedFrom::new(infcx, target_mod, env);
4749
let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst);
4850
inhabitedness == BREAK_VISIBLY_UNINHABITED
4951
}
5052

51-
struct UninhabitedFrom<'a> {
53+
struct UninhabitedFrom<'a, 'db> {
5254
target_mod: ModuleId,
53-
recursive_ty: FxHashSet<Ty>,
55+
recursive_ty: FxHashSet<Ty<'db>>,
5456
// guard for preventing stack overflow in non trivial non terminating types
5557
max_depth: usize,
56-
db: &'a dyn HirDatabase,
57-
env: Arc<TraitEnvironment<'a>>,
58+
infcx: &'a InferCtxt<'db>,
59+
env: Arc<TraitEnvironment<'db>>,
5860
}
5961

6062
const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
6163
const BREAK_VISIBLY_UNINHABITED: ControlFlow<VisiblyUninhabited> = Break(VisiblyUninhabited);
6264
#[derive(PartialEq, Eq)]
6365
struct VisiblyUninhabited;
6466

65-
impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
66-
type BreakTy = VisiblyUninhabited;
67-
68-
fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = VisiblyUninhabited> {
69-
self
70-
}
67+
impl<'db> TypeVisitor<DbInterner<'db>> for UninhabitedFrom<'_, 'db> {
68+
type Result = ControlFlow<VisiblyUninhabited>;
7169

72-
fn visit_ty(
73-
&mut self,
74-
ty: &Ty,
75-
outer_binder: DebruijnIndex,
76-
) -> ControlFlow<VisiblyUninhabited> {
77-
if self.recursive_ty.contains(ty) || self.max_depth == 0 {
70+
fn visit_ty(&mut self, mut ty: Ty<'db>) -> ControlFlow<VisiblyUninhabited> {
71+
if self.recursive_ty.contains(&ty) || self.max_depth == 0 {
7872
// rustc considers recursive types always inhabited. I think it is valid to consider
7973
// recursive types as always uninhabited, but we should do what rustc is doing.
8074
return CONTINUE_OPAQUELY_INHABITED;
8175
}
82-
self.recursive_ty.insert(ty.clone());
76+
self.recursive_ty.insert(ty);
8377
self.max_depth -= 1;
84-
let interner = DbInterner::new_with(self.db, None, None);
85-
let r = match ty.kind(Interner) {
86-
TyKind::Adt(adt, subst) => self.visit_adt(adt.0, subst),
87-
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
88-
TyKind::Tuple(..) => ty.super_visit_with(self, outer_binder),
89-
TyKind::Array(item_ty, len) => {
90-
match try_const_usize(self.db, len.to_nextsolver(interner)) {
91-
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
92-
Some(1..) => item_ty.super_visit_with(self, outer_binder),
93-
}
94-
}
95-
TyKind::Alias(AliasTy::Projection(projection)) => {
96-
// FIXME: I think this currently isn't used for monomorphized bodies, so there is no need to handle
97-
// `TyKind::AssociatedType`, but perhaps in the future it will.
98-
let normalized = self.db.normalize_projection(projection.clone(), self.env.clone());
99-
self.visit_ty(&normalized, outer_binder)
78+
79+
if matches!(ty.kind(), TyKind::Alias(..)) {
80+
let mut ocx = ObligationCtxt::new(self.infcx);
81+
match ocx.structurally_normalize_ty(&ObligationCause::dummy(), self.env.env, ty) {
82+
Ok(it) => ty = it,
83+
Err(_) => return CONTINUE_OPAQUELY_INHABITED,
10084
}
85+
}
86+
87+
let r = match ty.kind() {
88+
TyKind::Adt(adt, subst) => self.visit_adt(adt.def_id().0, subst),
89+
TyKind::Never => BREAK_VISIBLY_UNINHABITED,
90+
TyKind::Tuple(..) => ty.super_visit_with(self),
91+
TyKind::Array(item_ty, len) => match try_const_usize(self.infcx.interner.db, len) {
92+
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
93+
Some(1..) => item_ty.super_visit_with(self),
94+
},
10195
_ => CONTINUE_OPAQUELY_INHABITED,
10296
};
103-
self.recursive_ty.remove(ty);
97+
self.recursive_ty.remove(&ty);
10498
self.max_depth += 1;
10599
r
106100
}
101+
}
107102

108-
fn interner(&self) -> Interner {
109-
Interner
103+
impl<'a, 'db> UninhabitedFrom<'a, 'db> {
104+
fn new(
105+
infcx: &'a InferCtxt<'db>,
106+
target_mod: ModuleId,
107+
env: Arc<TraitEnvironment<'db>>,
108+
) -> Self {
109+
Self { target_mod, recursive_ty: FxHashSet::default(), max_depth: 500, infcx, env }
110+
}
111+
112+
#[inline]
113+
fn interner(&self) -> DbInterner<'db> {
114+
self.infcx.interner
115+
}
116+
117+
#[inline]
118+
fn db(&self) -> &'db dyn HirDatabase {
119+
self.interner().db
110120
}
111-
}
112121

113-
impl UninhabitedFrom<'_> {
114-
fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> {
122+
fn visit_adt(
123+
&mut self,
124+
adt: AdtId,
125+
subst: GenericArgs<'db>,
126+
) -> ControlFlow<VisiblyUninhabited> {
115127
// An ADT is uninhabited iff all its variants uninhabited.
116128
match adt {
117129
// rustc: For now, `union`s are never considered uninhabited.
118130
AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
119131
AdtId::StructId(s) => self.visit_variant(s.into(), subst),
120132
AdtId::EnumId(e) => {
121-
let enum_data = e.enum_variants(self.db);
133+
let enum_data = e.enum_variants(self.db());
122134

123135
for &(variant, _, _) in enum_data.variants.iter() {
124136
let variant_inhabitedness = self.visit_variant(variant.into(), subst);
@@ -135,17 +147,17 @@ impl UninhabitedFrom<'_> {
135147
fn visit_variant(
136148
&mut self,
137149
variant: VariantId,
138-
subst: &Substitution,
150+
subst: GenericArgs<'db>,
139151
) -> ControlFlow<VisiblyUninhabited> {
140-
let variant_data = variant.fields(self.db);
152+
let variant_data = variant.fields(self.db());
141153
let fields = variant_data.fields();
142154
if fields.is_empty() {
143155
return CONTINUE_OPAQUELY_INHABITED;
144156
}
145157

146158
let is_enum = matches!(variant, VariantId::EnumVariantId(..));
147-
let field_tys = self.db.field_types(variant);
148-
let field_vis = if is_enum { None } else { Some(self.db.field_visibilities(variant)) };
159+
let field_tys = self.db().field_types_ns(variant);
160+
let field_vis = if is_enum { None } else { Some(self.db().field_visibilities(variant)) };
149161

150162
for (fid, _) in fields.iter() {
151163
self.visit_field(field_vis.as_ref().map(|it| it[fid]), &field_tys[fid], subst)?;
@@ -156,12 +168,12 @@ impl UninhabitedFrom<'_> {
156168
fn visit_field(
157169
&mut self,
158170
vis: Option<Visibility>,
159-
ty: &Binders<Ty>,
160-
subst: &Substitution,
171+
ty: &EarlyBinder<'db, Ty<'db>>,
172+
subst: GenericArgs<'db>,
161173
) -> ControlFlow<VisiblyUninhabited> {
162-
if vis.is_none_or(|it| it.is_visible_from(self.db, self.target_mod)) {
163-
let ty = ty.clone().substitute(Interner, subst);
164-
ty.visit_with(self, DebruijnIndex::INNERMOST)
174+
if vis.is_none_or(|it| it.is_visible_from(self.db(), self.target_mod)) {
175+
let ty = ty.instantiate(self.interner(), subst);
176+
ty.visit_with(self)
165177
} else {
166178
CONTINUE_OPAQUELY_INHABITED
167179
}

crates/hir-ty/src/mir/lower.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ use crate::{
4343
next_solver::{
4444
Const, DbInterner, ParamConst, Region, TyKind, TypingMode, UnevaluatedConst,
4545
infer::{DbInternerInferExt, InferCtxt},
46-
mapping::NextSolverToChalk,
4746
},
4847
traits::FnTrait,
4948
};
@@ -303,6 +302,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
303302
let resolver = owner.resolver(db);
304303
let env = db.trait_environment_for_body(owner);
305304
let interner = DbInterner::new_with(db, Some(env.krate), env.block);
305+
// FIXME(next-solver): Is `non_body_analysis()` correct here? Don't we want to reveal opaque types defined by this body?
306306
let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
307307

308308
MirLowerCtx {
@@ -1766,8 +1766,8 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
17661766

17671767
fn is_uninhabited(&self, expr_id: ExprId) -> bool {
17681768
is_ty_uninhabited_from(
1769-
self.db,
1770-
&self.infer[expr_id].to_chalk(self.interner()),
1769+
&self.infcx,
1770+
self.infer[expr_id],
17711771
self.owner.module(self.db),
17721772
self.env.clone(),
17731773
)

0 commit comments

Comments
 (0)