Move a lot of rustc_query_system::plumbing to rustc_query_impl::execution (part 1).

[Note: this commit conceptually moves 75% of a file to another location,
and leaves 25% of it behind. It's impossible to preserve all the git
history. To preserve git history of the moved 75%, in this commit we
rename the file and remove the 25% from it, leaving the code in an
incomplete (uncompilable) state. In the next commit we add back the
25% in the old location.]

We are in the process of eliminating `rustc_query_system`. Chunks of it
are unused by `rustc_middle`, and so can be moved into
`rustc_query_impl`. This commit does some of that.

Mostly it's just moving code from one file to a new file. There are a
couple of non-trivial changes.

- `QueryState` and `ActiveKeyStatus` must remain in `rustc_query_system`
  because they are used by `rustc_middle`. But their inherent methods
  are not used by `rustc_middle`. So these methods are moved and
  converted to free functions.

- The visibility of some things must increase. This includes `DepGraphData`
  and some of its methods, which are now used in `rustc_query_impl`.
  This is a bit annoying but seems hard to avoid.

What little is left behind in
`compiler/rustc_query_system/src/query/plumbing.rs` will be able to
moved into `rustc_query_impl` or `rustc_middle` in the future.
This commit is contained in:
Nicholas Nethercote 2026-02-08 15:14:17 +11:00
parent 25df79ea5d
commit 6527b3404c
8 changed files with 100 additions and 249 deletions

View file

@ -1,133 +1,83 @@
//! The implementation of the query system itself. This defines the macros that
//! generate the actual methods on tcx which find and execute the provider,
//! manage the caches, and so forth.
use std::cell::Cell;
use std::fmt::Debug;
use std::hash::Hash;
use std::mem;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_data_structures::hash_table::{self, Entry, HashTable};
use rustc_data_structures::sharded::{self, Sharded};
use rustc_data_structures::hash_table::{Entry, HashTable};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::{outline, sync};
use rustc_data_structures::{outline, sharded, sync};
use rustc_errors::{Diag, FatalError, StashKey};
use rustc_query_system::dep_graph::{DepGraphData, DepNodeKey, HasDepContext};
use rustc_query_system::query::{
ActiveKeyStatus, CycleError, CycleErrorHandling, QueryCache, QueryContext, QueryDispatcher,
QueryJob, QueryJobId, QueryJobInfo, QueryLatch, QueryMap, QueryMode, QueryStackDeferred,
QueryStackFrame, QueryState, incremental_verify_ich, report_cycle,
};
use rustc_span::{DUMMY_SP, Span};
use tracing::instrument;
use super::{QueryDispatcher, QueryStackDeferred, QueryStackFrameExtra};
use crate::dep_graph::{
DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeKey, HasDepContext,
};
use crate::ich::StableHashingContext;
use crate::query::caches::QueryCache;
use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, report_cycle};
use crate::query::{
CycleErrorHandling, QueryContext, QueryMap, QueryStackFrame, SerializedDepNodeIndex,
};
use crate::dep_graph::{DepContext, DepNode, DepNodeIndex};
#[inline]
fn equivalent_key<K: Eq, V>(k: &K) -> impl Fn(&(K, V)) -> bool + '_ {
move |x| x.0 == *k
}
/// For a particular query, keeps track of "active" keys, i.e. keys whose
/// evaluation has started but has not yet finished successfully.
/// Obtains the enclosed [`QueryJob`], or panics if this query evaluation
/// was poisoned by a panic.
fn expect_job<'tcx>(status: ActiveKeyStatus<'tcx>) -> QueryJob<'tcx> {
match status {
ActiveKeyStatus::Started(job) => job,
ActiveKeyStatus::Poisoned => {
panic!("job for query failed to start and was poisoned")
}
}
}
pub(crate) fn all_inactive<'tcx, K>(state: &QueryState<'tcx, K>) -> bool {
state.active.lock_shards().all(|shard| shard.is_empty())
}
/// Internal plumbing for collecting the set of active jobs for this query.
///
/// (Successful query evaluation for a key is represented by an entry in the
/// query's in-memory cache.)
pub struct QueryState<'tcx, K> {
active: Sharded<hash_table::HashTable<(K, ActiveKeyStatus<'tcx>)>>,
}
/// Should only be called from `gather_active_jobs`.
pub(crate) fn gather_active_jobs_inner<'tcx, Qcx: Copy, K: Copy>(
state: &QueryState<'tcx, K>,
qcx: Qcx,
make_frame: fn(Qcx, K) -> QueryStackFrame<QueryStackDeferred<'tcx>>,
jobs: &mut QueryMap<'tcx>,
require_complete: bool,
) -> Option<()> {
let mut active = Vec::new();
/// For a particular query and key, tracks the status of a query evaluation
/// that has started, but has not yet finished successfully.
///
/// (Successful query evaluation for a key is represented by an entry in the
/// query's in-memory cache.)
enum ActiveKeyStatus<'tcx> {
/// Some thread is already evaluating the query for this key.
///
/// The enclosed [`QueryJob`] can be used to wait for it to finish.
Started(QueryJob<'tcx>),
/// The query panicked. Queries trying to wait on this will raise a fatal error which will
/// silently panic.
Poisoned,
}
impl<'tcx> ActiveKeyStatus<'tcx> {
/// Obtains the enclosed [`QueryJob`], or panics if this query evaluation
/// was poisoned by a panic.
fn expect_job(self) -> QueryJob<'tcx> {
match self {
Self::Started(job) => job,
Self::Poisoned => {
panic!("job for query failed to start and was poisoned")
// Helper to gather active jobs from a single shard.
let mut gather_shard_jobs = |shard: &HashTable<(K, ActiveKeyStatus<'tcx>)>| {
for (k, v) in shard.iter() {
if let ActiveKeyStatus::Started(ref job) = *v {
active.push((*k, job.clone()));
}
}
}
}
};
impl<'tcx, K> QueryState<'tcx, K>
where
K: Eq + Hash + Copy + Debug,
{
pub fn all_inactive(&self) -> bool {
self.active.lock_shards().all(|shard| shard.is_empty())
}
/// Internal plumbing for collecting the set of active jobs for this query.
///
/// Should only be called from `gather_active_jobs`.
pub fn gather_active_jobs_inner<Qcx: Copy>(
&self,
qcx: Qcx,
make_frame: fn(Qcx, K) -> QueryStackFrame<QueryStackDeferred<'tcx>>,
jobs: &mut QueryMap<'tcx>,
require_complete: bool,
) -> Option<()> {
let mut active = Vec::new();
// Helper to gather active jobs from a single shard.
let mut gather_shard_jobs = |shard: &HashTable<(K, ActiveKeyStatus<'tcx>)>| {
for (k, v) in shard.iter() {
if let ActiveKeyStatus::Started(ref job) = *v {
active.push((*k, job.clone()));
}
}
};
// Lock shards and gather jobs from each shard.
if require_complete {
for shard in self.active.lock_shards() {
gather_shard_jobs(&shard);
}
} else {
// We use try_lock_shards here since we are called from the
// deadlock handler, and this shouldn't be locked.
for shard in self.active.try_lock_shards() {
let shard = shard?;
gather_shard_jobs(&shard);
}
// Lock shards and gather jobs from each shard.
if require_complete {
for shard in state.active.lock_shards() {
gather_shard_jobs(&shard);
}
// Call `make_frame` while we're not holding a `self.active` lock as `make_frame` may call
// queries leading to a deadlock.
for (key, job) in active {
let frame = make_frame(qcx, key);
jobs.insert(job.id, QueryJobInfo { frame, job });
} else {
// We use try_lock_shards here since we are called from the
// deadlock handler, and this shouldn't be locked.
for shard in state.active.try_lock_shards() {
let shard = shard?;
gather_shard_jobs(&shard);
}
Some(())
}
}
impl<'tcx, K> Default for QueryState<'tcx, K> {
fn default() -> QueryState<'tcx, K> {
QueryState { active: Default::default() }
// Call `make_frame` while we're not holding a `state.active` lock as `make_frame` may call
// queries leading to a deadlock.
for (key, job) in active {
let frame = make_frame(qcx, key);
jobs.insert(job.id, QueryJobInfo { frame, job });
}
Some(())
}
/// A type representing the responsibility to execute the job in the `job` field.
@ -217,7 +167,7 @@ where
Ok(occupied) => Some(occupied.remove().0.1),
}
};
let job = job.expect("active query job entry").expect_job();
let job = expect_job(job.expect("active query job entry"));
job.signal_complete();
}
@ -240,7 +190,7 @@ where
Ok(occupied) => {
let ((key, value), vacant) = occupied.remove();
vacant.insert((key, ActiveKeyStatus::Poisoned));
value.expect_job()
expect_job(value)
}
}
};
@ -250,22 +200,6 @@ where
}
}
#[derive(Clone, Debug)]
pub struct CycleError<I = QueryStackFrameExtra> {
/// The query and related span that uses the cycle.
pub usage: Option<(Span, QueryStackFrame<I>)>,
pub cycle: Vec<QueryInfo<I>>,
}
impl<'tcx> CycleError<QueryStackDeferred<'tcx>> {
fn lift(&self) -> CycleError<QueryStackFrameExtra> {
CycleError {
usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift())),
cycle: self.cycle.iter().map(|info| info.lift()).collect(),
}
}
}
#[cold]
#[inline(never)]
fn cycle_error<'tcx, Q>(
@ -664,89 +598,6 @@ where
Some((result, dep_node_index))
}
#[inline]
#[instrument(skip(tcx, dep_graph_data, result, hash_result, format_value), level = "debug")]
pub(crate) fn incremental_verify_ich<Tcx, V>(
tcx: Tcx,
dep_graph_data: &DepGraphData<Tcx::Deps>,
result: &V,
prev_index: SerializedDepNodeIndex,
hash_result: Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>,
format_value: fn(&V) -> String,
) where
Tcx: DepContext,
{
if !dep_graph_data.is_index_green(prev_index) {
incremental_verify_ich_not_green(tcx, prev_index)
}
let new_hash = hash_result.map_or(Fingerprint::ZERO, |f| {
tcx.with_stable_hashing_context(|mut hcx| f(&mut hcx, result))
});
let old_hash = dep_graph_data.prev_fingerprint_of(prev_index);
if new_hash != old_hash {
incremental_verify_ich_failed(tcx, prev_index, &|| format_value(result));
}
}
#[cold]
#[inline(never)]
fn incremental_verify_ich_not_green<Tcx>(tcx: Tcx, prev_index: SerializedDepNodeIndex)
where
Tcx: DepContext,
{
panic!(
"fingerprint for green query instance not loaded from cache: {:?}",
tcx.dep_graph().data().unwrap().prev_node_of(prev_index)
)
}
// Note that this is marked #[cold] and intentionally takes `dyn Debug` for `result`,
// as we want to avoid generating a bunch of different implementations for LLVM to
// chew on (and filling up the final binary, too).
#[cold]
#[inline(never)]
fn incremental_verify_ich_failed<Tcx>(
tcx: Tcx,
prev_index: SerializedDepNodeIndex,
result: &dyn Fn() -> String,
) where
Tcx: DepContext,
{
// When we emit an error message and panic, we try to debug-print the `DepNode`
// and query result. Unfortunately, this can cause us to run additional queries,
// which may result in another fingerprint mismatch while we're in the middle
// of processing this one. To avoid a double-panic (which kills the process
// before we can print out the query static), we print out a terse
// but 'safe' message if we detect a reentrant call to this method.
thread_local! {
static INSIDE_VERIFY_PANIC: Cell<bool> = const { Cell::new(false) };
};
let old_in_panic = INSIDE_VERIFY_PANIC.replace(true);
if old_in_panic {
tcx.sess().dcx().emit_err(crate::error::Reentrant);
} else {
let run_cmd = if let Some(crate_name) = &tcx.sess().opts.crate_name {
format!("`cargo clean -p {crate_name}` or `cargo clean`")
} else {
"`cargo clean`".to_string()
};
let dep_node = tcx.dep_graph().data().unwrap().prev_node_of(prev_index);
tcx.sess().dcx().emit_err(crate::error::IncrementCompilation {
run_cmd,
dep_node: format!("{dep_node:?}"),
});
panic!("Found unstable fingerprints for {dep_node:?}: {}", result());
}
INSIDE_VERIFY_PANIC.set(old_in_panic);
}
/// Ensure that either this query has all green inputs or been executed.
/// Executing `query::ensure(D)` is considered a read of the dep-node `D`.
/// Returns true if the query should still run.
@ -801,14 +652,13 @@ where
(!loadable, Some(dep_node))
}
#[derive(Debug)]
pub enum QueryMode {
Get,
Ensure { check_cache: bool },
}
#[inline(always)]
pub fn get_query_non_incr<'tcx, Q>(query: Q, qcx: Q::Qcx, span: Span, key: Q::Key) -> Q::Value
pub(super) fn get_query_non_incr<'tcx, Q>(
query: Q,
qcx: Q::Qcx,
span: Span,
key: Q::Key,
) -> Q::Value
where
Q: QueryDispatcher<'tcx>,
{
@ -818,7 +668,7 @@ where
}
#[inline(always)]
pub fn get_query_incr<'tcx, Q>(
pub(super) fn get_query_incr<'tcx, Q>(
query: Q,
qcx: Q::Qcx,
span: Span,
@ -848,7 +698,7 @@ where
Some(result)
}
pub fn force_query<'tcx, Q>(query: Q, qcx: Q::Qcx, key: Q::Key, dep_node: DepNode)
pub(super) fn force_query<'tcx, Q>(query: Q, qcx: Q::Qcx, key: Q::Key, dep_node: DepNode)
where
Q: QueryDispatcher<'tcx>,
{

View file

@ -3,6 +3,7 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![feature(adt_const_params)]
#![feature(core_intrinsics)]
#![feature(min_specialization)]
#![feature(rustc_attrs)]
// tidy-alphabetical-end
@ -25,21 +26,20 @@ use rustc_query_system::dep_graph::SerializedDepNodeIndex;
use rustc_query_system::ich::StableHashingContext;
use rustc_query_system::query::{
CycleError, CycleErrorHandling, HashResult, QueryCache, QueryDispatcher, QueryMap, QueryMode,
QueryState, get_query_incr, get_query_non_incr,
QueryState,
};
use rustc_span::{ErrorGuaranteed, Span};
pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all};
use crate::plumbing::{encode_all_query_results, try_mark_green};
use crate::profiling_support::QueryKeyStringCache;
#[macro_use]
mod plumbing;
pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all};
mod profiling_support;
pub use self::profiling_support::alloc_self_profile_query_strings;
pub use crate::profiling_support::alloc_self_profile_query_strings;
mod error;
mod execution;
#[macro_use]
mod plumbing;
mod profiling_support;
#[derive(ConstParamTy)] // Allow this struct to be used for const-generic values.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]

View file

@ -29,13 +29,14 @@ use rustc_query_system::dep_graph::{DepNodeKey, FingerprintStyle, HasDepContext}
use rustc_query_system::ich::StableHashingContext;
use rustc_query_system::query::{
QueryCache, QueryContext, QueryDispatcher, QueryJobId, QueryMap, QuerySideEffect,
QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra, force_query,
QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra,
};
use rustc_serialize::{Decodable, Encodable};
use rustc_span::def_id::LOCAL_CRATE;
use crate::QueryDispatcherUnerased;
use crate::error::{QueryOverflow, QueryOverflowNote};
use crate::execution::{all_inactive, force_query};
/// Implements [`QueryContext`] for use by [`rustc_query_system`], since that
/// crate does not have direct access to [`TyCtxt`].
@ -387,7 +388,7 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>(
{
let _timer = qcx.tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name());
assert!(query.query_state(qcx).all_inactive());
assert!(all_inactive(query.query_state(qcx)));
let cache = query.query_cache(qcx);
cache.iter(&mut |key, value, dep_node| {
if query.will_cache_on_disk_for_key(qcx.tcx, key) {
@ -594,7 +595,7 @@ macro_rules! define_queries {
) -> Option<Erased<queries::$name::Value<'tcx>>> {
#[cfg(debug_assertions)]
let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
get_query_incr(
execution::get_query_incr(
QueryType::query_dispatcher(tcx),
QueryCtxt::new(tcx),
span,
@ -614,7 +615,7 @@ macro_rules! define_queries {
key: queries::$name::Key<'tcx>,
__mode: QueryMode,
) -> Option<Erased<queries::$name::Value<'tcx>>> {
Some(get_query_non_incr(
Some(execution::get_query_non_incr(
QueryType::query_dispatcher(tcx),
QueryCtxt::new(tcx),
span,
@ -748,7 +749,7 @@ macro_rules! define_queries {
};
// Call `gather_active_jobs_inner` to do the actual work.
let res = tcx.query_system.states.$name.gather_active_jobs_inner(
let res = crate::execution::gather_active_jobs_inner(&tcx.query_system.states.$name,
tcx,
make_frame,
qmap,

View file

@ -82,7 +82,7 @@ pub(super) enum DepNodeColor {
Unknown,
}
pub(crate) struct DepGraphData<D: Deps> {
pub struct DepGraphData<D: Deps> {
/// The new encoding of the dependency graph, optimized for red/green
/// tracking. The `current` field is the dependency graph of only the
/// current compilation session: We don't merge the previous dep-graph into
@ -171,7 +171,7 @@ impl<D: Deps> DepGraph<D> {
}
#[inline]
pub(crate) fn data(&self) -> Option<&DepGraphData<D>> {
pub fn data(&self) -> Option<&DepGraphData<D>> {
self.data.as_deref()
}
@ -323,7 +323,7 @@ impl<D: Deps> DepGraphData<D> {
///
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html
#[inline(always)]
pub(crate) fn with_task<Ctxt: HasDepContext<Deps = D>, A: Debug, R>(
pub fn with_task<Ctxt: HasDepContext<Deps = D>, A: Debug, R>(
&self,
key: DepNode,
cx: Ctxt,
@ -373,7 +373,7 @@ impl<D: Deps> DepGraphData<D> {
/// FIXME: This could perhaps return a `WithDepNode` to ensure that the
/// user of this function actually performs the read; we'll have to see
/// how to make that work with `anon` in `execute_job_incr`, though.
pub(crate) fn with_anon_task_inner<Tcx: DepContext<Deps = D>, OP, R>(
pub fn with_anon_task_inner<Tcx: DepContext<Deps = D>, OP, R>(
&self,
cx: Tcx,
dep_kind: DepKind,
@ -653,12 +653,12 @@ impl<D: Deps> DepGraphData<D> {
/// Returns true if the given node has been marked as green during the
/// current compilation session. Used in various assertions
#[inline]
pub(crate) fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool {
pub fn is_index_green(&self, prev_index: SerializedDepNodeIndex) -> bool {
matches!(self.colors.get(prev_index), DepNodeColor::Green(_))
}
#[inline]
pub(crate) fn prev_fingerprint_of(&self, prev_index: SerializedDepNodeIndex) -> Fingerprint {
pub fn prev_fingerprint_of(&self, prev_index: SerializedDepNodeIndex) -> Fingerprint {
self.previous.fingerprint_by_index(prev_index)
}
@ -667,7 +667,7 @@ impl<D: Deps> DepGraphData<D> {
self.previous.index_to_node(prev_index)
}
pub(crate) fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) {
pub fn mark_debug_loaded_from_disk(&self, dep_node: DepNode) {
self.debug_loaded_from_disk.lock().insert(dep_node);
}
@ -863,7 +863,7 @@ impl<D: Deps> DepGraphData<D> {
/// A node will have an index, when it's already been marked green, or when we can mark it
/// green. This function will mark the current task as a reader of the specified node, when
/// a node index can be found for that node.
pub(crate) fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>(
pub fn try_mark_green<'tcx, Qcx: QueryContext<'tcx, Deps = D>>(
&self,
qcx: Qcx,
dep_node: &DepNode,
@ -1074,7 +1074,7 @@ impl<D: Deps> DepGraph<D> {
if let Some(data) = &self.data { data.current.encoder.finish(&data.current) } else { Ok(0) }
}
pub(crate) fn next_virtual_depnode_index(&self) -> DepNodeIndex {
pub fn next_virtual_depnode_index(&self) -> DepNodeIndex {
debug_assert!(self.data.is_none());
let index = self.virtual_dep_node_index.fetch_add(1, Ordering::Relaxed);
DepNodeIndex::from_u32(index)

View file

@ -8,8 +8,9 @@ mod serialized;
use std::panic;
pub use dep_node::{DepKind, DepKindVTable, DepNode, DepNodeKey, WorkProductId};
pub(crate) use graph::DepGraphData;
pub use graph::{DepGraph, DepNodeIndex, TaskDepsRef, WorkProduct, WorkProductMap, hash_result};
pub use graph::{
DepGraph, DepGraphData, DepNodeIndex, TaskDepsRef, WorkProduct, WorkProductMap, hash_result,
};
pub use query::DepGraphQuery;
use rustc_data_structures::profiling::SelfProfilerRef;
use rustc_data_structures::sync::DynSync;

View file

@ -1,7 +1,6 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![feature(assert_matches)]
#![feature(core_intrinsics)]
#![feature(min_specialization)]
// tidy-alphabetical-end

View file

@ -86,7 +86,7 @@ impl<'tcx> QueryJob<'tcx> {
QueryJob { id, span, parent, latch: None }
}
pub(super) fn latch(&mut self) -> QueryLatch<'tcx> {
pub fn latch(&mut self) -> QueryLatch<'tcx> {
if self.latch.is_none() {
self.latch = Some(QueryLatch::new());
}
@ -106,7 +106,7 @@ impl<'tcx> QueryJob<'tcx> {
}
impl QueryJobId {
pub(super) fn find_cycle_in_stack<'tcx>(
pub fn find_cycle_in_stack<'tcx>(
&self,
query_map: QueryMap<'tcx>,
current_job: &Option<QueryJobId>,
@ -182,7 +182,7 @@ struct QueryLatchInfo<'tcx> {
}
#[derive(Debug)]
pub(super) struct QueryLatch<'tcx> {
pub struct QueryLatch<'tcx> {
info: Arc<Mutex<QueryLatchInfo<'tcx>>>,
}
@ -200,7 +200,7 @@ impl<'tcx> QueryLatch<'tcx> {
}
/// Awaits for the query job to complete.
pub(super) fn wait_on(
pub fn wait_on(
&self,
qcx: impl QueryContext<'tcx>,
query: Option<QueryJobId>,

View file

@ -15,8 +15,8 @@ use rustc_span::def_id::DefId;
pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache};
pub use self::dispatcher::{HashResult, QueryDispatcher};
pub use self::job::{
QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, break_query_cycles, print_query_stack,
report_cycle,
QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, QueryMap, break_query_cycles,
print_query_stack, report_cycle,
};
pub use self::plumbing::*;
use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
@ -84,7 +84,7 @@ impl<'tcx> QueryStackFrame<QueryStackDeferred<'tcx>> {
#[derive(Clone, Debug)]
pub struct QueryStackFrameExtra {
pub description: String,
span: Option<Span>,
pub span: Option<Span>,
pub def_kind: Option<DefKind>,
}