Skip to content

Commit 94e3826

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

7 files changed

Lines changed: 77 additions & 89 deletions

File tree

‎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)fnvariant_data(db:&dynDefDatabase,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+
fnvariant_data(&self,id:VariantId) -> Arc<VariantData>;
130133
#[salsa::transparent]
131134
#[salsa::invoke(ImplData::impl_data_query)]
132135
fnimpl_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
letmut 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
fnis_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-
fnis_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+
fnis_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
fnvariant_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-
letmut variants = FxHashMap::default();
445+
letmut 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
fnbug(&self,fmt: fmt::Arguments<'_>){
509-
debug!("{}", fmt)
502+
never!("{}", fmt)
510503
}
511504

512505
fncomplexity_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
usecrate::{
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)fnis_ty_uninhabited_from(ty:&Ty,target_mod:ModuleId,db:&dynHirDatabase) -> bool{
17+
pub(crate)fnis_ty_uninhabited_from(db:&dynHirDatabase,ty:&Ty,target_mod:ModuleId) -> bool{
18+
let _p = tracing::span!(tracing::Level::INFO,"is_ty_uninhabited_from", ?ty);
2019
letmut 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)fnis_enum_variant_uninhabited_from(
28+
db:&dynHirDatabase,
2829
variant:EnumVariantId,
2930
subst:&Substitution,
3031
target_mod:ModuleId,
31-
db:&dynHirDatabase,
3232
) -> bool{
33-
letis_local = variant.module(db.upcast()).krate() == target_mod.krate();
33+
let_p = tracing::span!(tracing::Level::INFO,"is_enum_variant_uninhabited_from",);
3434

3535
letmut 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
implUninhabitedFrom<'_>{
10094
fnvisit_adt(&mutself,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-
returnCONTINUE_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(()) => returnCONTINUE_OPAQUELY_INHABITED,
130108
}
131109
}
@@ -137,34 +115,36 @@ impl UninhabitedFrom<'_> {
137115
fnvisit_variant(
138116
&mutself,
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+
returnCONTINUE_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
returnCONTINUE_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, _)invariant_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
fnvisit_field(
161142
&mutself,
162-
vis:Visibility,
143+
vis:Option<Visibility>,
163144
ty:&Binders<Ty>,
164145
subst:&Substitution,
165-
is_enum:bool,
166146
) -> ControlFlow<VisiblyUninhabited>{
167-
ifis_enum || vis.is_visible_from(self.db.upcast(),self.target_mod){
147+
ifvis.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
fnis_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)