Skip to content

Commit 54b616f

Browse files
Rollup merge of rust-lang#153685 - Zalathar:for-each-query, r=nnethercote
Introduce `for_each_query_vtable!` to move more code out of query macros After rust-lang#153114 moved a few for-each-query functions into the big `rustc_query_impl::plumbing` macro, I have found that those functions became much harder to navigate and modify, because they no longer have access to ordinary IDE features in rust-analyzer. Even *finding* the functions is considerably harder, because a plain go-to-definition no longer works smoothly. This PR therefore tries to move as much of that code back out of the macro as possible, with the aid of a smaller `for_each_query_vtable!` helper macro. A typical use of that macro looks like this: ```rust for_each_query_vtable!(ALL, tcx, |query| { query_key_hash_verify(query, tcx); }); ``` The result is an outer function consisting almost entirely of plain Rust code, with all of the usual IDE affordances expected of normal Rust code. Because it uses plain Rust syntax, it can also be formatted automatically by rustfmt. Adding another layer of macro-defined macros is not something I propose lightly, but in this case I think the improvement is well worth it: - The outer functions can once again be defined as “normal” Rust functions, right next to their corresponding inner functions, making navigation and modification much easier. - The closure expression is ordinary Rust code that simply gets repeated ~300 times in the expansion, once for each query, in order to account for the variety of key/value/cache types used by different queries. Even within the closure expression, IDE features still *mostly* work, which is an improvement over the status quo. - For future maintainers looking at the call site, the macro's effect should hopefully be pretty obvious and intuitive, reducing the need to even look at the helper macro. And the helper macro itself is largely straightforward, with its biggest complication being that it necessarily uses the `$name` metavar from the outer macro. There should be no change to compiler behaviour. r? nnethercote (or compiler)
2 parents 73ff2bf + d066ff7 commit 54b616f

5 files changed

Lines changed: 129 additions & 103 deletions

File tree

compiler/rustc_query_impl/src/execution.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use rustc_middle::ty::TyCtxt;
1616
use rustc_middle::verify_ich::incremental_verify_ich;
1717
use rustc_span::{DUMMY_SP, Span};
1818

19-
use crate::collect_active_jobs_from_all_queries;
2019
use crate::dep_graph::{DepNode, DepNodeIndex};
20+
use crate::for_each_query_vtable;
2121
use crate::job::{QueryJobInfo, QueryJobMap, find_cycle_in_stack, report_cycle};
2222
use crate::plumbing::{current_query_job, next_job_id, start_query};
2323

@@ -30,14 +30,41 @@ pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool {
3030
state.active.lock_shards().all(|shard| shard.is_empty())
3131
}
3232

33+
/// Returns a map of currently active query jobs, collected from all queries.
34+
///
35+
/// If `require_complete` is `true`, this function locks all shards of the
36+
/// query results to produce a complete map, which always returns `Ok`.
37+
/// Otherwise, it may return an incomplete map as an error if any shard
38+
/// lock cannot be acquired.
39+
///
40+
/// Prefer passing `false` to `require_complete` to avoid potential deadlocks,
41+
/// especially when called from within a deadlock handler, unless a
42+
/// complete map is needed and no deadlock is possible at this call site.
43+
pub fn collect_active_jobs_from_all_queries<'tcx>(
44+
tcx: TyCtxt<'tcx>,
45+
require_complete: bool,
46+
) -> Result<QueryJobMap<'tcx>, QueryJobMap<'tcx>> {
47+
let mut job_map_out = QueryJobMap::default();
48+
let mut complete = true;
49+
50+
for_each_query_vtable!(ALL, tcx, |query| {
51+
let res = gather_active_jobs(query, tcx, require_complete, &mut job_map_out);
52+
if res.is_none() {
53+
complete = false;
54+
}
55+
});
56+
57+
if complete { Ok(job_map_out) } else { Err(job_map_out) }
58+
}
59+
3360
/// Internal plumbing for collecting the set of active jobs for this query.
3461
///
3562
/// Should only be called from `collect_active_jobs_from_all_queries`.
3663
///
3764
/// (We arbitrarily use the word "gather" when collecting the jobs for
3865
/// each individual query, so that we have distinct function names to
3966
/// grep for.)
40-
pub(crate) fn gather_active_jobs<'tcx, C>(
67+
fn gather_active_jobs<'tcx, C>(
4168
query: &'tcx QueryVTable<'tcx, C>,
4269
tcx: TyCtxt<'tcx>,
4370
require_complete: bool,

compiler/rustc_query_impl/src/job.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_middle::ty::TyCtxt;
1414
use rustc_session::Session;
1515
use rustc_span::{DUMMY_SP, Span};
1616

17-
use crate::collect_active_jobs_from_all_queries;
17+
use crate::execution::collect_active_jobs_from_all_queries;
1818

1919
/// Map from query job IDs to job information collected by
2020
/// `collect_active_jobs_from_all_queries`.

compiler/rustc_query_impl/src/lib.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
use rustc_data_structures::sync::AtomicU64;
1212
use rustc_middle::dep_graph;
1313
use rustc_middle::queries::{self, ExternProviders, Providers};
14-
use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache};
14+
use rustc_middle::query::on_disk_cache::OnDiskCache;
1515
use rustc_middle::query::plumbing::{QuerySystem, QueryVTable};
1616
use rustc_middle::query::{AsLocalQueryKey, QueryCache, QueryMode};
1717
use rustc_middle::ty::TyCtxt;
1818
use rustc_span::Span;
1919

2020
pub use crate::dep_kind_vtables::make_dep_kind_vtables;
21+
pub use crate::execution::collect_active_jobs_from_all_queries;
2122
pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack};
22-
use crate::profiling_support::QueryKeyStringCache;
2323

2424
#[macro_use]
2525
mod plumbing;
@@ -66,7 +66,8 @@ pub fn query_system<'tcx>(
6666
rustc_middle::rustc_with_all_queries! { define_queries! }
6767

6868
pub fn provide(providers: &mut rustc_middle::util::Providers) {
69-
providers.hooks.alloc_self_profile_query_strings = alloc_self_profile_query_strings;
70-
providers.hooks.query_key_hash_verify_all = query_key_hash_verify_all;
71-
providers.hooks.encode_all_query_results = encode_all_query_results;
69+
providers.hooks.alloc_self_profile_query_strings =
70+
profiling_support::alloc_self_profile_query_strings;
71+
providers.hooks.query_key_hash_verify_all = plumbing::query_key_hash_verify_all;
72+
providers.hooks.encode_all_query_results = plumbing::encode_all_query_results;
7273
}

compiler/rustc_query_impl/src/plumbing.rs

Lines changed: 61 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use rustc_span::def_id::LOCAL_CRATE;
3333
use crate::error::{QueryOverflow, QueryOverflowNote};
3434
use crate::execution::{all_inactive, force_query};
3535
use crate::job::find_dep_kind_root;
36-
use crate::{GetQueryVTable, collect_active_jobs_from_all_queries};
36+
use crate::{GetQueryVTable, collect_active_jobs_from_all_queries, for_each_query_vtable};
3737

3838
fn depth_limit_error<'tcx>(tcx: TyCtxt<'tcx>, job: QueryJobId) {
3939
let job_map =
@@ -146,7 +146,17 @@ where
146146
QueryStackFrame::new(info, kind, def_id, def_id_for_ty_in_cycle)
147147
}
148148

149-
pub(crate) fn encode_query_results<'a, 'tcx, C, V>(
149+
pub(crate) fn encode_all_query_results<'tcx>(
150+
tcx: TyCtxt<'tcx>,
151+
encoder: &mut CacheEncoder<'_, 'tcx>,
152+
query_result_index: &mut EncodedDepNodeIndex,
153+
) {
154+
for_each_query_vtable!(CACHE_ON_DISK, tcx, |query| {
155+
encode_query_results(tcx, query, encoder, query_result_index)
156+
});
157+
}
158+
159+
fn encode_query_results<'a, 'tcx, C, V>(
150160
tcx: TyCtxt<'tcx>,
151161
query: &'tcx QueryVTable<'tcx, C>,
152162
encoder: &mut CacheEncoder<'a, 'tcx>,
@@ -172,7 +182,17 @@ pub(crate) fn encode_query_results<'a, 'tcx, C, V>(
172182
});
173183
}
174184

175-
pub(crate) fn query_key_hash_verify<'tcx, C: QueryCache>(
185+
pub(crate) fn query_key_hash_verify_all<'tcx>(tcx: TyCtxt<'tcx>) {
186+
if tcx.sess.opts.unstable_opts.incremental_verify_ich || cfg!(debug_assertions) {
187+
tcx.sess.time("query_key_hash_verify_all", || {
188+
for_each_query_vtable!(ALL, tcx, |query| {
189+
query_key_hash_verify(query, tcx);
190+
});
191+
});
192+
}
193+
}
194+
195+
fn query_key_hash_verify<'tcx, C: QueryCache>(
176196
query: &'tcx QueryVTable<'tcx, C>,
177197
tcx: TyCtxt<'tcx>,
178198
) {
@@ -510,95 +530,48 @@ macro_rules! define_queries {
510530
}
511531
}
512532

513-
/// Returns a map of currently active query jobs, collected from all queries.
533+
/// Given a filter condition (e.g. `ALL` or `CACHE_ON_DISK`), a `tcx`,
534+
/// and a closure expression that accepts `&QueryVTable`, this macro
535+
/// calls that closure with each query vtable that satisfies the filter
536+
/// condition.
514537
///
515-
/// If `require_complete` is `true`, this function locks all shards of the
516-
/// query results to produce a complete map, which always returns `Ok`.
517-
/// Otherwise, it may return an incomplete map as an error if any shard
518-
/// lock cannot be acquired.
538+
/// This needs to be a macro, because the vtables can have different
539+
/// key/value/cache types for different queries.
519540
///
520-
/// Prefer passing `false` to `require_complete` to avoid potential deadlocks,
521-
/// especially when called from within a deadlock handler, unless a
522-
/// complete map is needed and no deadlock is possible at this call site.
523-
pub fn collect_active_jobs_from_all_queries<'tcx>(
524-
tcx: TyCtxt<'tcx>,
525-
require_complete: bool,
526-
) -> Result<QueryJobMap<'tcx>, QueryJobMap<'tcx>> {
527-
let mut job_map_out = QueryJobMap::default();
528-
let mut complete = true;
529-
530-
$(
531-
let res = crate::execution::gather_active_jobs(
532-
&tcx.query_system.query_vtables.$name,
533-
tcx,
534-
require_complete,
535-
&mut job_map_out,
536-
);
537-
if res.is_none() {
538-
complete = false;
539-
}
540-
)*
541-
542-
if complete { Ok(job_map_out) } else { Err(job_map_out) }
543-
}
544-
545-
/// All self-profiling events generated by the query engine use
546-
/// virtual `StringId`s for their `event_id`. This method makes all
547-
/// those virtual `StringId`s point to actual strings.
541+
/// This macro's argument syntax is specifically intended to look like
542+
/// plain Rust code, so that `for_each_query_vtable!(..)` calls will be
543+
/// formatted by rustfmt.
548544
///
549-
/// If we are recording only summary data, the ids will point to
550-
/// just the query names. If we are recording query keys too, we
551-
/// allocate the corresponding strings here.
552-
pub fn alloc_self_profile_query_strings(tcx: TyCtxt<'_>) {
553-
if !tcx.prof.enabled() {
554-
return;
555-
}
556-
557-
let _prof_timer = tcx.sess.prof.generic_activity("self_profile_alloc_query_strings");
558-
559-
let mut string_cache = QueryKeyStringCache::new();
560-
561-
$(
562-
$crate::profiling_support::alloc_self_profile_query_strings_for_query_cache(
563-
tcx,
564-
stringify!($name),
565-
&tcx.query_system.query_vtables.$name.cache,
566-
&mut string_cache,
567-
);
568-
)*
569-
570-
tcx.sess.prof.store_query_cache_hits();
571-
}
572-
573-
fn encode_all_query_results<'tcx>(
574-
tcx: TyCtxt<'tcx>,
575-
encoder: &mut CacheEncoder<'_, 'tcx>,
576-
query_result_index: &mut EncodedDepNodeIndex,
577-
) {
578-
$(
579-
#[cfg($cache_on_disk)]
580-
{
581-
$crate::plumbing::encode_query_results(
582-
tcx,
583-
&tcx.query_system.query_vtables.$name,
584-
encoder,
585-
query_result_index,
586-
)
587-
}
588-
)*
545+
/// To avoid too much nested-macro complication, filter conditions are
546+
/// implemented by hand as needed.
547+
macro_rules! for_each_query_vtable {
548+
// Call with all queries.
549+
(ALL, $tcx:expr, $closure:expr) => {{
550+
let tcx: rustc_middle::ty::TyCtxt<'_> = $tcx;
551+
$(
552+
let query: &rustc_middle::query::plumbing::QueryVTable<'_, _> =
553+
&tcx.query_system.query_vtables.$name;
554+
$closure(query);
555+
)*
556+
}};
557+
558+
// Only call with queries that can potentially cache to disk.
559+
//
560+
// This allows the use of trait bounds that only need to be satisfied
561+
// by the subset of queries that actually cache to disk.
562+
(CACHE_ON_DISK, $tcx:expr, $closure:expr) => {{
563+
let tcx: rustc_middle::ty::TyCtxt<'_> = $tcx;
564+
$(
565+
#[cfg($cache_on_disk)]
566+
{
567+
let query: &rustc_middle::query::plumbing::QueryVTable<'_, _> =
568+
&tcx.query_system.query_vtables.$name;
569+
$closure(query);
570+
}
571+
)*
572+
}}
589573
}
590574

591-
pub fn query_key_hash_verify_all<'tcx>(tcx: TyCtxt<'tcx>) {
592-
if tcx.sess.opts.unstable_opts.incremental_verify_ich || cfg!(debug_assertions) {
593-
tcx.sess.time("query_key_hash_verify_all", || {
594-
$(
595-
$crate::plumbing::query_key_hash_verify(
596-
&tcx.query_system.query_vtables.$name,
597-
tcx
598-
);
599-
)*
600-
})
601-
}
602-
}
575+
pub(crate) use for_each_query_vtable;
603576
}
604577
}

compiler/rustc_query_impl/src/profiling_support.rs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ use rustc_data_structures::profiling::SelfProfiler;
77
use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE, LocalDefId};
88
use rustc_hir::definitions::DefPathData;
99
use rustc_middle::query::QueryCache;
10+
use rustc_middle::query::plumbing::QueryVTable;
1011
use rustc_middle::ty::TyCtxt;
1112

13+
use crate::for_each_query_vtable;
14+
1215
pub(crate) struct QueryKeyStringCache {
1316
def_id_cache: FxHashMap<DefId, StringId>,
1417
}
@@ -172,13 +175,35 @@ where
172175
}
173176
}
174177

178+
/// All self-profiling events generated by the query engine use
179+
/// virtual `StringId`s for their `event_id`. This method makes all
180+
/// those virtual `StringId`s point to actual strings.
181+
///
182+
/// If we are recording only summary data, the ids will point to
183+
/// just the query names. If we are recording query keys too, we
184+
/// allocate the corresponding strings here.
185+
pub(crate) fn alloc_self_profile_query_strings(tcx: TyCtxt<'_>) {
186+
if !tcx.prof.enabled() {
187+
return;
188+
}
189+
190+
let _prof_timer = tcx.sess.prof.generic_activity("self_profile_alloc_query_strings");
191+
192+
let mut string_cache = QueryKeyStringCache::new();
193+
194+
for_each_query_vtable!(ALL, tcx, |query| {
195+
alloc_self_profile_query_strings_for_query_cache(tcx, query, &mut string_cache);
196+
});
197+
198+
tcx.sess.prof.store_query_cache_hits();
199+
}
200+
175201
/// Allocate the self-profiling query strings for a single query cache. This
176202
/// method is called from `alloc_self_profile_query_strings` which knows all
177203
/// the queries via macro magic.
178-
pub(crate) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>(
204+
fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>(
179205
tcx: TyCtxt<'tcx>,
180-
query_name: &'static str,
181-
query_cache: &C,
206+
query: &'tcx QueryVTable<'tcx, C>,
182207
string_cache: &mut QueryKeyStringCache,
183208
) where
184209
C: QueryCache,
@@ -193,14 +218,14 @@ pub(crate) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>(
193218
if profiler.query_key_recording_enabled() {
194219
let mut query_string_builder = QueryKeyStringBuilder::new(profiler, tcx, string_cache);
195220

196-
let query_name = profiler.get_or_alloc_cached_string(query_name);
221+
let query_name = profiler.get_or_alloc_cached_string(query.name);
197222

198223
// Since building the string representation of query keys might
199224
// need to invoke queries itself, we cannot keep the query caches
200225
// locked while doing so. Instead we copy out the
201226
// `(query_key, dep_node_index)` pairs and release the lock again.
202227
let mut query_keys_and_indices = Vec::new();
203-
query_cache.for_each(&mut |k, _, i| query_keys_and_indices.push((*k, i)));
228+
query.cache.for_each(&mut |k, _, i| query_keys_and_indices.push((*k, i)));
204229

205230
// Now actually allocate the strings. If allocating the strings
206231
// generates new entries in the query cache, we'll miss them but
@@ -221,14 +246,14 @@ pub(crate) fn alloc_self_profile_query_strings_for_query_cache<'tcx, C>(
221246
}
222247
} else {
223248
// In this branch we don't allocate query keys
224-
let query_name = profiler.get_or_alloc_cached_string(query_name);
249+
let query_name = profiler.get_or_alloc_cached_string(query.name);
225250
let event_id = event_id_builder.from_label(query_name).to_string_id();
226251

227252
// FIXME(eddyb) make this O(1) by using a pre-cached query name `EventId`,
228253
// instead of passing the `DepNodeIndex` to `finish_with_query_invocation_id`,
229254
// when recording the event in the first place.
230255
let mut query_invocation_ids = Vec::new();
231-
query_cache.for_each(&mut |_, _, i| {
256+
query.cache.for_each(&mut |_, _, i| {
232257
query_invocation_ids.push(i.into());
233258
});
234259

0 commit comments

Comments
 (0)