Skip to content

Commit 6b49172

Browse files
authored
Merge pull request #19851 from ChayimFriedman2/normalize-exhaustiveness
fix: Normalize when checking for uninhabited types for pattern exhaustiveness checking
2 parents a493526 + f0096ae commit 6b49172

File tree

5 files changed

+81
-25
lines changed

5 files changed

+81
-25
lines changed

src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use triomphe::Arc;
2525
use typed_arena::Arena;
2626

2727
use crate::{
28-
Adjust, InferenceResult, Interner, Ty, TyExt, TyKind,
28+
Adjust, InferenceResult, Interner, TraitEnvironment, Ty, TyExt, TyKind,
2929
db::HirDatabase,
3030
diagnostics::match_check::{
3131
self,
@@ -74,8 +74,9 @@ impl BodyValidationDiagnostic {
7474
let _p = tracing::info_span!("BodyValidationDiagnostic::collect").entered();
7575
let infer = db.infer(owner);
7676
let body = db.body(owner);
77+
let env = db.trait_environment_for_body(owner);
7778
let mut validator =
78-
ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints };
79+
ExprValidator { owner, body, infer, diagnostics: Vec::new(), validate_lints, env };
7980
validator.validate_body(db);
8081
validator.diagnostics
8182
}
@@ -85,6 +86,7 @@ struct ExprValidator {
8586
owner: DefWithBodyId,
8687
body: Arc<Body>,
8788
infer: Arc<InferenceResult>,
89+
env: Arc<TraitEnvironment>,
8890
diagnostics: Vec<BodyValidationDiagnostic>,
8991
validate_lints: bool,
9092
}
@@ -190,7 +192,7 @@ impl ExprValidator {
190192
return;
191193
}
192194

193-
let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db);
195+
let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone());
194196

195197
let pattern_arena = Arena::new();
196198
let mut m_arms = Vec::with_capacity(arms.len());
@@ -317,7 +319,7 @@ impl ExprValidator {
317319
return;
318320
};
319321
let pattern_arena = Arena::new();
320-
let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db);
322+
let cx = MatchCheckCtx::new(self.owner.module(db), self.owner, db, self.env.clone());
321323
for stmt in &**statements {
322324
let &Statement::Let { pat, initializer, else_branch: None, .. } = stmt else {
323325
continue;

src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ use rustc_pattern_analysis::{
1212
};
1313
use smallvec::{SmallVec, smallvec};
1414
use stdx::never;
15+
use triomphe::Arc;
1516

1617
use crate::{
17-
AdtId, Interner, Scalar, Ty, TyExt, TyKind,
18+
AdtId, Interner, Scalar, TraitEnvironment, Ty, TyExt, TyKind,
1819
db::HirDatabase,
1920
infer::normalize,
2021
inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from},
@@ -69,13 +70,19 @@ pub(crate) struct MatchCheckCtx<'db> {
6970
body: DefWithBodyId,
7071
pub(crate) db: &'db dyn HirDatabase,
7172
exhaustive_patterns: bool,
73+
env: Arc<TraitEnvironment>,
7274
}
7375

7476
impl<'db> MatchCheckCtx<'db> {
75-
pub(crate) fn new(module: ModuleId, body: DefWithBodyId, db: &'db dyn HirDatabase) -> Self {
77+
pub(crate) fn new(
78+
module: ModuleId,
79+
body: DefWithBodyId,
80+
db: &'db dyn HirDatabase,
81+
env: Arc<TraitEnvironment>,
82+
) -> Self {
7683
let def_map = module.crate_def_map(db);
7784
let exhaustive_patterns = def_map.is_unstable_feature_enabled(&sym::exhaustive_patterns);
78-
Self { module, body, db, exhaustive_patterns }
85+
Self { module, body, db, exhaustive_patterns, env }
7986
}
8087

8188
pub(crate) fn compute_match_usefulness(
@@ -100,7 +107,7 @@ impl<'db> MatchCheckCtx<'db> {
100107
}
101108

102109
fn is_uninhabited(&self, ty: &Ty) -> bool {
103-
is_ty_uninhabited_from(self.db, ty, self.module)
110+
is_ty_uninhabited_from(self.db, ty, self.module, self.env.clone())
104111
}
105112

106113
/// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
@@ -459,8 +466,13 @@ impl PatCx for MatchCheckCtx<'_> {
459466
} else {
460467
let mut variants = IndexVec::with_capacity(enum_data.variants.len());
461468
for &(variant, _) in enum_data.variants.iter() {
462-
let is_uninhabited =
463-
is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module);
469+
let is_uninhabited = is_enum_variant_uninhabited_from(
470+
cx.db,
471+
variant,
472+
subst,
473+
cx.module,
474+
self.env.clone(),
475+
);
464476
let visibility = if is_uninhabited {
465477
VariantVisibility::Empty
466478
} else {

src/tools/rust-analyzer/crates/hir-ty/src/inhabitedness.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,24 @@ use chalk_ir::{
77
};
88
use hir_def::{AdtId, EnumVariantId, ModuleId, VariantId, visibility::Visibility};
99
use rustc_hash::FxHashSet;
10+
use triomphe::Arc;
1011

1112
use crate::{
12-
Binders, Interner, Substitution, Ty, TyKind, consteval::try_const_usize, db::HirDatabase,
13+
AliasTy, Binders, Interner, Substitution, TraitEnvironment, Ty, TyKind,
14+
consteval::try_const_usize, db::HirDatabase,
1315
};
1416

1517
// FIXME: Turn this into a query, it can be quite slow
1618
/// Checks whether a type is visibly uninhabited from a particular module.
17-
pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool {
19+
pub(crate) fn is_ty_uninhabited_from(
20+
db: &dyn HirDatabase,
21+
ty: &Ty,
22+
target_mod: ModuleId,
23+
env: Arc<TraitEnvironment>,
24+
) -> bool {
1825
let _p = tracing::info_span!("is_ty_uninhabited_from", ?ty).entered();
1926
let mut uninhabited_from =
20-
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
27+
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env };
2128
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
2229
inhabitedness == BREAK_VISIBLY_UNINHABITED
2330
}
@@ -29,11 +36,12 @@ pub(crate) fn is_enum_variant_uninhabited_from(
2936
variant: EnumVariantId,
3037
subst: &Substitution,
3138
target_mod: ModuleId,
39+
env: Arc<TraitEnvironment>,
3240
) -> bool {
3341
let _p = tracing::info_span!("is_enum_variant_uninhabited_from").entered();
3442

3543
let mut uninhabited_from =
36-
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
44+
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default(), env };
3745
let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst);
3846
inhabitedness == BREAK_VISIBLY_UNINHABITED
3947
}
@@ -44,6 +52,7 @@ struct UninhabitedFrom<'a> {
4452
// guard for preventing stack overflow in non trivial non terminating types
4553
max_depth: usize,
4654
db: &'a dyn HirDatabase,
55+
env: Arc<TraitEnvironment>,
4756
}
4857

4958
const CONTINUE_OPAQUELY_INHABITED: ControlFlow<VisiblyUninhabited> = Continue(());
@@ -78,6 +87,12 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
7887
Some(0) | None => CONTINUE_OPAQUELY_INHABITED,
7988
Some(1..) => item_ty.super_visit_with(self, outer_binder),
8089
},
90+
TyKind::Alias(AliasTy::Projection(projection)) => {
91+
// FIXME: I think this currently isn't used for monomorphized bodies, so there is no need to handle
92+
// `TyKind::AssociatedType`, but perhaps in the future it will.
93+
let normalized = self.db.normalize_projection(projection.clone(), self.env.clone());
94+
self.visit_ty(&normalized, outer_binder)
95+
}
8196
_ => CONTINUE_OPAQUELY_INHABITED,
8297
};
8398
self.recursive_ty.remove(ty);

src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use syntax::TextRange;
2525
use triomphe::Arc;
2626

2727
use crate::{
28-
Adjust, Adjustment, AutoBorrow, CallableDefId, TyBuilder, TyExt,
28+
Adjust, Adjustment, AutoBorrow, CallableDefId, TraitEnvironment, TyBuilder, TyExt,
2929
consteval::ConstEvalError,
3030
db::{HirDatabase, InternedClosure, InternedClosureId},
3131
display::{DisplayTarget, HirDisplay, hir_display_with_store},
@@ -79,6 +79,7 @@ struct MirLowerCtx<'db> {
7979
infer: &'db InferenceResult,
8080
resolver: Resolver<'db>,
8181
drop_scopes: Vec<DropScope>,
82+
env: Arc<TraitEnvironment>,
8283
}
8384

8485
// FIXME: Make this smaller, its stored in database queries
@@ -288,6 +289,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
288289
closures: vec![],
289290
};
290291
let resolver = owner.resolver(db);
292+
let env = db.trait_environment_for_body(owner);
291293

292294
MirLowerCtx {
293295
result: mir,
@@ -300,6 +302,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
300302
labeled_loop_blocks: Default::default(),
301303
discr_temp: None,
302304
drop_scopes: vec![DropScope::default()],
305+
env,
303306
}
304307
}
305308

@@ -944,10 +947,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
944947
let cast_kind = if source_ty.as_reference().is_some() {
945948
CastKind::PointerCoercion(PointerCast::ArrayToPointer)
946949
} else {
947-
let mut table = InferenceTable::new(
948-
self.db,
949-
self.db.trait_environment_for_body(self.owner),
950-
);
950+
let mut table = InferenceTable::new(self.db, self.env.clone());
951951
cast_kind(&mut table, &source_ty, &target_ty)?
952952
};
953953

@@ -1412,11 +1412,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
14121412
}
14131413

14141414
fn lower_literal_to_operand(&mut self, ty: Ty, l: &Literal) -> Result<Operand> {
1415-
let size = || {
1416-
self.db
1417-
.layout_of_ty(ty.clone(), self.db.trait_environment_for_body(self.owner))
1418-
.map(|it| it.size.bytes_usize())
1419-
};
1415+
let size =
1416+
|| self.db.layout_of_ty(ty.clone(), self.env.clone()).map(|it| it.size.bytes_usize());
14201417
const USIZE_SIZE: usize = size_of::<usize>();
14211418
let bytes: Box<[_]> = match l {
14221419
hir_def::hir::Literal::String(b) => {
@@ -1723,7 +1720,12 @@ impl<'ctx> MirLowerCtx<'ctx> {
17231720
}
17241721

17251722
fn is_uninhabited(&self, expr_id: ExprId) -> bool {
1726-
is_ty_uninhabited_from(self.db, &self.infer[expr_id], self.owner.module(self.db))
1723+
is_ty_uninhabited_from(
1724+
self.db,
1725+
&self.infer[expr_id],
1726+
self.owner.module(self.db),
1727+
self.env.clone(),
1728+
)
17271729
}
17281730

17291731
/// This function push `StorageLive` statement for the binding, and applies changes to add `StorageDead` and

src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/non_exhaustive_let.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,29 @@ fn test(x: Result<i32, &'static !>) {
106106
"#,
107107
);
108108
}
109+
110+
#[test]
111+
fn empty_patterns_normalize() {
112+
check_diagnostics(
113+
r#"
114+
enum Infallible {}
115+
116+
trait Foo {
117+
type Assoc;
118+
}
119+
enum Enum<T: Foo> {
120+
A,
121+
B(T::Assoc),
122+
}
123+
124+
impl Foo for () {
125+
type Assoc = Infallible;
126+
}
127+
128+
fn foo(v: Enum<()>) {
129+
let Enum::A = v;
130+
}
131+
"#,
132+
);
133+
}
109134
}

0 commit comments

Comments
 (0)