Skip to content

Commit 94e3826

Browse files
committed
Optimize exhaustiveness checking perf a bit
1 parent 597c293 commit 94e3826

File tree

7 files changed

+77
-89
lines changed

7 files changed

+77
-89
lines changed

crates/hir-def/src/data/adt.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ use crate::{
2626
tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree},
2727
type_ref::TypeRef,
2828
visibility::RawVisibility,
29-
EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId,
29+
AdtId, EnumId, EnumVariantId, LocalFieldId, LocalModuleId, Lookup, StructId, UnionId,
30+
VariantId,
3031
};
3132

3233
/// Note that we use `StructData` for unions as well!
@@ -378,6 +379,14 @@ impl VariantData {
378379
VariantData::Unit => StructKind::Unit,
379380
}
380381
}
382+
383+
pub(crate) fn variant_data(db: &dyn DefDatabase, id: VariantId) -> Arc<VariantData> {
384+
match id {
385+
VariantId::StructId(it) => db.struct_data(it).variant_data.clone(),
386+
VariantId::EnumVariantId(it) => db.enum_variant_data(it).variant_data.clone(),
387+
VariantId::UnionId(it) => db.union_data(it).variant_data.clone(),
388+
}
389+
}
381390
}
382391

383392
#[derive(Debug, Copy, Clone, PartialEq, Eq)]

crates/hir-def/src/db.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
attr::{Attrs, AttrsWithOwner},
1313
body::{scope::ExprScopes, Body, BodySourceMap},
1414
data::{
15-
adt::{EnumData, EnumVariantData, StructData},
15+
adt::{EnumData, EnumVariantData, StructData, VariantData},
1616
ConstData, ExternCrateDeclData, FunctionData, ImplData, Macro2Data, MacroRulesData,
1717
ProcMacroData, StaticData, TraitAliasData, TraitData, TypeAliasData,
1818
},
@@ -22,13 +22,13 @@ use crate::{
2222
lang_item::{self, LangItem, LangItemTarget, LangItems},
2323
nameres::{diagnostics::DefDiagnostics, DefMap},
2424
visibility::{self, Visibility},
25-
AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc, DefWithBodyId,
26-
EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc, ExternCrateId,
27-
ExternCrateLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc, InTypeConstId,
28-
InTypeConstLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroId, MacroRulesId, MacroRulesLoc,
29-
MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId, StructLoc,
30-
TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId, UnionLoc,
31-
UseId, UseLoc, VariantId,
25+
AdtId, AttrDefId, BlockId, BlockLoc, ConstBlockId, ConstBlockLoc, ConstId, ConstLoc,
26+
DefWithBodyId, EnumId, EnumLoc, EnumVariantId, EnumVariantLoc, ExternBlockId, ExternBlockLoc,
27+
ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, GenericDefId, ImplId, ImplLoc,
28+
InTypeConstId, InTypeConstLoc, LocalFieldId, Macro2Id, Macro2Loc, MacroId, MacroRulesId,
29+
MacroRulesLoc, MacroRulesLocFlags, ProcMacroId, ProcMacroLoc, StaticId, StaticLoc, StructId,
30+
StructLoc, TraitAliasId, TraitAliasLoc, TraitId, TraitLoc, TypeAliasId, TypeAliasLoc, UnionId,
31+
UnionLoc, UseId, UseLoc, VariantId,
3232
};
3333

3434
#[salsa::query_group(InternDatabaseStorage)]
@@ -127,6 +127,9 @@ pub trait DefDatabase: InternDatabase + ExpandDatabase + Upcast<dyn ExpandDataba
127127
id: EnumVariantId,
128128
) -> (Arc<EnumVariantData>, DefDiagnostics);
129129

130+
#[salsa::transparent]
131+
#[salsa::invoke(VariantData::variant_data)]
132+
fn variant_data(&self, id: VariantId) -> Arc<VariantData>;
130133
#[salsa::transparent]
131134
#[salsa::invoke(ImplData::impl_data_query)]
132135
fn impl_data(&self, e: ImplId) -> Arc<ImplData>;

crates/hir-ty/src/diagnostics/expr.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use hir_def::{ItemContainerId, Lookup};
1111
use hir_expand::name;
1212
use itertools::Itertools;
1313
use rustc_hash::FxHashSet;
14+
use rustc_pattern_analysis::constructor::Constructor;
1415
use syntax::{ast, AstNode};
1516
use tracing::debug;
1617
use triomphe::Arc;
@@ -266,15 +267,17 @@ impl ExprValidator {
266267

267268
let mut have_errors = false;
268269
let deconstructed_pat = self.lower_pattern(&cx, pat, db, &mut have_errors);
270+
271+
// optimization, wildcard trivially hold
272+
if have_errors || matches!(deconstructed_pat.ctor(), Constructor::Wildcard) {
273+
continue;
274+
}
275+
269276
let match_arm = rustc_pattern_analysis::MatchArm {
270277
pat: pattern_arena.alloc(deconstructed_pat),
271278
has_guard: false,
272279
arm_data: (),
273280
};
274-
if have_errors {
275-
continue;
276-
}
277-
278281
let report = match cx.compute_match_usefulness(&[match_arm], ty.clone()) {
279282
Ok(v) => v,
280283
Err(e) => {

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

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
//! Interface with `rustc_pattern_analysis`.
22
33
use std::fmt;
4-
use tracing::debug;
54

65
use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
6+
use once_cell::unsync::Lazy;
77
use rustc_hash::FxHashMap;
88
use rustc_pattern_analysis::{
99
constructor::{Constructor, ConstructorSet, VariantVisibility},
@@ -91,20 +91,13 @@ impl<'p> MatchCheckCtx<'p> {
9191
}
9292

9393
fn is_uninhabited(&self, ty: &Ty) -> bool {
94-
is_ty_uninhabited_from(ty, self.module, self.db)
94+
is_ty_uninhabited_from(self.db, ty, self.module)
9595
}
9696

97-
/// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
98-
fn is_foreign_non_exhaustive_enum(&self, ty: &Ty) -> bool {
99-
match ty.as_adt() {
100-
Some((adt @ hir_def::AdtId::EnumId(_), _)) => {
101-
let has_non_exhaustive_attr =
102-
self.db.attrs(adt.into()).by_key("non_exhaustive").exists();
103-
let is_local = adt.module(self.db.upcast()).krate() == self.module.krate();
104-
has_non_exhaustive_attr && !is_local
105-
}
106-
_ => false,
107-
}
97+
/// Returns whether the given ADT is from another crate declared `#[non_exhaustive]`.
98+
fn is_foreign_non_exhaustive(&self, adt: hir_def::AdtId) -> bool {
99+
let is_local = adt.krate(self.db.upcast()) == self.module.krate();
100+
!is_local && self.db.attrs(adt.into()).by_key("non_exhaustive").exists()
108101
}
109102

110103
fn variant_id_for_adt(
@@ -376,24 +369,21 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
376369
single(subst_ty)
377370
} else {
378371
let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
379-
let (adt, _) = ty.as_adt().unwrap();
380372

381-
let adt_is_local =
382-
variant.module(self.db.upcast()).krate() == self.module.krate();
383373
// Whether we must not match the fields of this variant exhaustively.
384-
let is_non_exhaustive =
385-
self.db.attrs(variant.into()).by_key("non_exhaustive").exists()
386-
&& !adt_is_local;
387-
let visibilities = self.db.field_visibilities(variant);
374+
let is_non_exhaustive = Lazy::new(|| self.is_foreign_non_exhaustive(adt));
375+
let visibilities = Lazy::new(|| self.db.field_visibilities(variant));
388376

389377
self.list_variant_fields(ty, variant)
390378
.map(move |(fid, ty)| {
391-
let is_visible = matches!(adt, hir_def::AdtId::EnumId(..))
392-
|| visibilities[fid]
393-
.is_visible_from(self.db.upcast(), self.module);
379+
let is_visible = || {
380+
matches!(adt, hir_def::AdtId::EnumId(..))
381+
|| visibilities[fid]
382+
.is_visible_from(self.db.upcast(), self.module)
383+
};
394384
let is_uninhabited = self.is_uninhabited(&ty);
395385
let private_uninhabited =
396-
is_uninhabited && (!is_visible || is_non_exhaustive);
386+
is_uninhabited && (!is_visible() || *is_non_exhaustive);
397387
(ty, PrivateUninhabitedField(private_uninhabited))
398388
})
399389
.collect()
@@ -445,17 +435,20 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
445435
TyKind::Scalar(Scalar::Char) => unhandled(),
446436
TyKind::Scalar(Scalar::Int(..) | Scalar::Uint(..)) => unhandled(),
447437
TyKind::Array(..) | TyKind::Slice(..) => unhandled(),
448-
TyKind::Adt(AdtId(hir_def::AdtId::EnumId(enum_id)), subst) => {
449-
let enum_data = cx.db.enum_data(*enum_id);
450-
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(ty);
438+
&TyKind::Adt(AdtId(adt @ hir_def::AdtId::EnumId(enum_id)), ref subst) => {
439+
let enum_data = cx.db.enum_data(enum_id);
440+
let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive(adt);
451441

452442
if enum_data.variants.is_empty() && !is_declared_nonexhaustive {
453443
ConstructorSet::NoConstructors
454444
} else {
455-
let mut variants = FxHashMap::default();
445+
let mut variants = FxHashMap::with_capacity_and_hasher(
446+
enum_data.variants.len(),
447+
Default::default(),
448+
);
456449
for (i, &(variant, _)) in enum_data.variants.iter().enumerate() {
457450
let is_uninhabited =
458-
is_enum_variant_uninhabited_from(variant, subst, cx.module, cx.db);
451+
is_enum_variant_uninhabited_from(cx.db, variant, subst, cx.module);
459452
let visibility = if is_uninhabited {
460453
VariantVisibility::Empty
461454
} else {
@@ -506,7 +499,7 @@ impl<'p> PatCx for MatchCheckCtx<'p> {
506499
}
507500

508501
fn bug(&self, fmt: fmt::Arguments<'_>) {
509-
debug!("{}", fmt)
502+
never!("{}", fmt)
510503
}
511504

512505
fn complexity_exceeded(&self) -> Result<(), Self::Error> {

crates/hir-ty/src/inhabitedness.rs

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,36 @@ use chalk_ir::{
55
visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor},
66
DebruijnIndex,
77
};
8-
use hir_def::{
9-
attr::Attrs, data::adt::VariantData, visibility::Visibility, AdtId, EnumVariantId, HasModule,
10-
ModuleId, VariantId,
11-
};
8+
use hir_def::{visibility::Visibility, AdtId, EnumVariantId, HasModule, ModuleId, VariantId};
129
use rustc_hash::FxHashSet;
1310

1411
use crate::{
1512
consteval::try_const_usize, db::HirDatabase, Binders, Interner, Substitution, Ty, TyKind,
1613
};
1714

15+
// FIXME: Turn this into a query, it can be quite slow
1816
/// Checks whether a type is visibly uninhabited from a particular module.
19-
pub(crate) fn is_ty_uninhabited_from(ty: &Ty, target_mod: ModuleId, db: &dyn HirDatabase) -> bool {
17+
pub(crate) fn is_ty_uninhabited_from(db: &dyn HirDatabase, ty: &Ty, target_mod: ModuleId) -> bool {
18+
let _p = tracing::span!(tracing::Level::INFO, "is_ty_uninhabited_from", ?ty);
2019
let mut uninhabited_from =
2120
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
2221
let inhabitedness = ty.visit_with(&mut uninhabited_from, DebruijnIndex::INNERMOST);
2322
inhabitedness == BREAK_VISIBLY_UNINHABITED
2423
}
2524

25+
// FIXME: Turn this into a query, it can be quite slow
2626
/// Checks whether a variant is visibly uninhabited from a particular module.
2727
pub(crate) fn is_enum_variant_uninhabited_from(
28+
db: &dyn HirDatabase,
2829
variant: EnumVariantId,
2930
subst: &Substitution,
3031
target_mod: ModuleId,
31-
db: &dyn HirDatabase,
3232
) -> bool {
33-
let is_local = variant.module(db.upcast()).krate() == target_mod.krate();
33+
let _p = tracing::span!(tracing::Level::INFO, "is_enum_variant_uninhabited_from",);
3434

3535
let mut uninhabited_from =
3636
UninhabitedFrom { target_mod, db, max_depth: 500, recursive_ty: FxHashSet::default() };
37-
let inhabitedness = uninhabited_from.visit_variant(
38-
variant.into(),
39-
&db.enum_variant_data(variant).variant_data,
40-
subst,
41-
&db.attrs(variant.into()),
42-
is_local,
43-
);
37+
let inhabitedness = uninhabited_from.visit_variant(variant.into(), subst);
4438
inhabitedness == BREAK_VISIBLY_UNINHABITED
4539
}
4640

@@ -98,34 +92,18 @@ impl TypeVisitor<Interner> for UninhabitedFrom<'_> {
9892

9993
impl UninhabitedFrom<'_> {
10094
fn visit_adt(&mut self, adt: AdtId, subst: &Substitution) -> ControlFlow<VisiblyUninhabited> {
101-
let attrs = self.db.attrs(adt.into());
102-
let adt_non_exhaustive = attrs.by_key("non_exhaustive").exists();
103-
let is_local = adt.module(self.db.upcast()).krate() == self.target_mod.krate();
104-
if adt_non_exhaustive && !is_local {
105-
return CONTINUE_OPAQUELY_INHABITED;
106-
}
107-
10895
// An ADT is uninhabited iff all its variants uninhabited.
10996
match adt {
11097
// rustc: For now, `union`s are never considered uninhabited.
11198
AdtId::UnionId(_) => CONTINUE_OPAQUELY_INHABITED,
112-
AdtId::StructId(s) => {
113-
let struct_data = self.db.struct_data(s);
114-
self.visit_variant(s.into(), &struct_data.variant_data, subst, &attrs, is_local)
115-
}
99+
AdtId::StructId(s) => self.visit_variant(s.into(), subst),
116100
AdtId::EnumId(e) => {
117101
let enum_data = self.db.enum_data(e);
118102

119103
for &(variant, _) in enum_data.variants.iter() {
120-
let variant_inhabitedness = self.visit_variant(
121-
variant.into(),
122-
&self.db.enum_variant_data(variant).variant_data,
123-
subst,
124-
&self.db.attrs(variant.into()),
125-
is_local,
126-
);
104+
let variant_inhabitedness = self.visit_variant(variant.into(), subst);
127105
match variant_inhabitedness {
128-
Break(VisiblyUninhabited) => continue,
106+
Break(VisiblyUninhabited) => (),
129107
Continue(()) => return CONTINUE_OPAQUELY_INHABITED,
130108
}
131109
}
@@ -137,34 +115,36 @@ impl UninhabitedFrom<'_> {
137115
fn visit_variant(
138116
&mut self,
139117
variant: VariantId,
140-
variant_data: &VariantData,
141118
subst: &Substitution,
142-
attrs: &Attrs,
143-
is_local: bool,
144119
) -> ControlFlow<VisiblyUninhabited> {
145-
let non_exhaustive_field_list = attrs.by_key("non_exhaustive").exists();
146-
if non_exhaustive_field_list && !is_local {
120+
let is_local = variant.krate(self.db.upcast()) == self.target_mod.krate();
121+
if !is_local && self.db.attrs(variant.into()).by_key("non_exhaustive").exists() {
122+
return CONTINUE_OPAQUELY_INHABITED;
123+
}
124+
125+
let variant_data = self.db.variant_data(variant);
126+
let fields = variant_data.fields();
127+
if fields.is_empty() {
147128
return CONTINUE_OPAQUELY_INHABITED;
148129
}
149130

150131
let is_enum = matches!(variant, VariantId::EnumVariantId(..));
151132
let field_tys = self.db.field_types(variant);
152-
let field_vis = self.db.field_visibilities(variant);
133+
let field_vis = if is_enum { None } else { Some(self.db.field_visibilities(variant)) };
153134

154-
for (fid, _) in variant_data.fields().iter() {
155-
self.visit_field(field_vis[fid], &field_tys[fid], subst, is_enum)?;
135+
for (fid, _) in fields.iter() {
136+
self.visit_field(field_vis.as_ref().map(|it| it[fid]), &field_tys[fid], subst)?;
156137
}
157138
CONTINUE_OPAQUELY_INHABITED
158139
}
159140

160141
fn visit_field(
161142
&mut self,
162-
vis: Visibility,
143+
vis: Option<Visibility>,
163144
ty: &Binders<Ty>,
164145
subst: &Substitution,
165-
is_enum: bool,
166146
) -> ControlFlow<VisiblyUninhabited> {
167-
if is_enum || vis.is_visible_from(self.db.upcast(), self.target_mod) {
147+
if vis.map_or(true, |it| it.is_visible_from(self.db.upcast(), self.target_mod)) {
168148
let ty = ty.clone().substitute(Interner, subst);
169149
ty.visit_with(self, DebruijnIndex::INNERMOST)
170150
} else {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1706,7 +1706,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
17061706
}
17071707

17081708
fn is_uninhabited(&self, expr_id: ExprId) -> bool {
1709-
is_ty_uninhabited_from(&self.infer[expr_id], self.owner.module(self.db.upcast()), self.db)
1709+
is_ty_uninhabited_from(self.db, &self.infer[expr_id], self.owner.module(self.db.upcast()))
17101710
}
17111711

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

crates/rust-analyzer/src/integrated_benchmarks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ fn integrated_diagnostics_benchmark() {
300300
.diagnostics(&diagnostics_config, ide::AssistResolveStrategy::None, file_id)
301301
.unwrap();
302302

303-
let _g = crate::tracing::hprof::init("*>1");
303+
let _g = crate::tracing::hprof::init("*");
304304

305305
{
306306
let _it = stdx::timeit("change");

0 commit comments

Comments
 (0)