Skip to content

Commit 3e4c332

Browse files
Do not check solver's cache validity on every access
This should help speed. Instead, we do it only when an `Interner` is constructed, since an interner cannot be held across revisions. There is a problem, though: an interner *can* be held across different databases. To solve that, we also reinit the cache when attaching databases, and take the assumption that everything significant (i.e. that can access the cache) will need to attach the db. This is somewhat risky, but we'll take it for the speed. Theoretically we could *only* reinit on db attach, but it's less risky this way, and with-crate interner construction is rare.
1 parent adef948 commit 3e4c332

2 files changed

Lines changed: 38 additions & 8 deletions

File tree

crates/hir-ty/src/next_solver/infer/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,10 @@ impl<'db> InferCtxtBuilder<'db> {
372372
}
373373

374374
pub fn build(&mut self, typing_mode: TypingMode<'db>) -> InferCtxt<'db> {
375+
// We do not allow creating an InferCtxt for an Interner without a crate, because this means
376+
// an interner without a crate cannot access the cache, therefore constructing it doesn't need
377+
// to reinit the cache, and we construct a lot of no-crate interners.
378+
self.interner.expect_crate();
375379
let InferCtxtBuilder { interner } = *self;
376380
InferCtxt {
377381
interner,

crates/hir-ty/src/next_solver/interner.rs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ unsafe impl Sync for DbInterner<'_> {}
327327
impl<'db> DbInterner<'db> {
328328
// FIXME(next-solver): remove this method
329329
pub fn conjure() -> DbInterner<'db> {
330+
// Here we can not reinit the cache since we do that when we attach the db.
330331
crate::with_attached_db(|db| DbInterner {
331332
db: unsafe { std::mem::transmute::<&dyn HirDatabase, &'db dyn HirDatabase>(db) },
332333
krate: None,
@@ -339,10 +340,13 @@ impl<'db> DbInterner<'db> {
339340
///
340341
/// Elaboration is a special kind: it needs lang items (for `Sized`), therefore it needs `new_with()`.
341342
pub fn new_no_crate(db: &'db dyn HirDatabase) -> Self {
343+
// We do not reinit the cache here, since anything accessing the cache needs an InferCtxt,
344+
// and we panic when trying to construct an InferCtxt for an Interner without a crate.
342345
DbInterner { db, krate: None, lang_items: None }
343346
}
344347

345348
pub fn new_with(db: &'db dyn HirDatabase, krate: Crate) -> DbInterner<'db> {
349+
tls_cache::reinit_cache(db);
346350
DbInterner {
347351
db,
348352
krate: Some(krate),
@@ -1120,7 +1124,8 @@ impl<'db> Interner for DbInterner<'db> {
11201124
self,
11211125
f: impl FnOnce(&mut rustc_type_ir::search_graph::GlobalCache<Self>) -> R,
11221126
) -> R {
1123-
tls_cache::with_cache(self.db, f)
1127+
// We make sure to reinit the cache when constructing the Interner.
1128+
tls_cache::borrow_assume_valid(self.db, f)
11241129
}
11251130

11261131
fn canonical_param_env_cache_get_or_insert<R>(
@@ -2475,6 +2480,7 @@ mod tls_db {
24752480
}
24762481

24772482
let _guard = DbGuard::new(self, db);
2483+
super::tls_cache::reinit_cache(db);
24782484
op()
24792485
}
24802486

@@ -2497,10 +2503,14 @@ mod tls_db {
24972503
#[inline]
24982504
fn drop(&mut self) {
24992505
self.state.database.set(self.prev);
2506+
if let Some(prev) = self.prev {
2507+
super::tls_cache::reinit_cache(unsafe { prev.as_ref() });
2508+
}
25002509
}
25012510
}
25022511

25032512
let _guard = DbGuard::new(self, db);
2513+
super::tls_cache::reinit_cache(db);
25042514
op()
25052515
}
25062516

@@ -2555,22 +2565,38 @@ mod tls_cache {
25552565
static GLOBAL_CACHE: RefCell<Option<Cache>> = const { RefCell::new(None) };
25562566
}
25572567

2558-
pub(super) fn with_cache<'db, T>(
2559-
db: &'db dyn HirDatabase,
2560-
f: impl FnOnce(&mut GlobalCache<DbInterner<'db>>) -> T,
2561-
) -> T {
2568+
pub(super) fn reinit_cache(db: &dyn HirDatabase) {
25622569
GLOBAL_CACHE.with_borrow_mut(|handle| {
25632570
let (db_nonce, revision) = db.nonce_and_revision();
2564-
let handle = match handle {
2571+
match handle {
25652572
Some(handle) => {
25662573
if handle.revision != revision || db_nonce != handle.db_nonce {
25672574
*handle = Cache { cache: GlobalCache::default(), revision, db_nonce };
25682575
}
2569-
handle
25702576
}
2571-
None => handle.insert(Cache { cache: GlobalCache::default(), revision, db_nonce }),
2577+
None => *handle = Some(Cache { cache: GlobalCache::default(), revision, db_nonce }),
2578+
}
2579+
})
2580+
}
2581+
2582+
pub(super) fn borrow_assume_valid<'db, T>(
2583+
db: &'db dyn HirDatabase,
2584+
f: impl FnOnce(&mut GlobalCache<DbInterner<'db>>) -> T,
2585+
) -> T {
2586+
if cfg!(debug_assertions) {
2587+
let get_state = || {
2588+
GLOBAL_CACHE.with_borrow(|handle| {
2589+
handle.as_ref().map(|handle| (handle.db_nonce, handle.revision))
2590+
})
25722591
};
2592+
let old_state = get_state();
2593+
reinit_cache(db);
2594+
let new_state = get_state();
2595+
assert_eq!(old_state, new_state, "you assumed the cache is valid!");
2596+
}
25732597

2598+
GLOBAL_CACHE.with_borrow_mut(|handle| {
2599+
let handle = handle.as_mut().expect("you assumed the cache is valid!");
25742600
// SAFETY: No idea
25752601
f(unsafe {
25762602
std::mem::transmute::<

0 commit comments

Comments
 (0)