Make QueryDispatcher::Qcx an associated type

This commit is contained in:
Zalathar 2026-01-29 10:35:05 +11:00
parent de6d33c033
commit 2a96ea0bee
4 changed files with 86 additions and 92 deletions

View file

@ -67,11 +67,11 @@ impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDA
// This is `impl QueryDispatcher for SemiDynamicQueryDispatcher`.
impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool>
QueryDispatcher<QueryCtxt<'tcx>>
for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
QueryDispatcher for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE>
where
for<'a> C::Key: HashStable<StableHashingContext<'a>>,
{
type Qcx = QueryCtxt<'tcx>;
type Key = C::Key;
type Value = C::Value;
type Cache = C;
@ -104,10 +104,7 @@ where
}
#[inline(always)]
fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache
where
'tcx: 'a,
{
fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache {
// Safety:
// This is just manually doing the subfield referencing through pointer math.
unsafe {
@ -215,15 +212,13 @@ where
/// on the type `rustc_query_impl::query_impl::$name::QueryType`.
trait QueryDispatcherUnerased<'tcx> {
type UnerasedValue;
type Dispatcher: QueryDispatcher<QueryCtxt<'tcx>>;
type Dispatcher: QueryDispatcher<Qcx = QueryCtxt<'tcx>>;
const NAME: &'static &'static str;
fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher;
fn restore_val(
value: <Self::Dispatcher as QueryDispatcher<QueryCtxt<'tcx>>>::Value,
) -> Self::UnerasedValue;
fn restore_val(value: <Self::Dispatcher as QueryDispatcher>::Value) -> Self::UnerasedValue;
}
pub fn query_system<'a>(

View file

@ -414,7 +414,7 @@ pub(crate) fn encode_query_results<'a, 'tcx, Q>(
}
pub(crate) fn query_key_hash_verify<'tcx>(
query: impl QueryDispatcher<QueryCtxt<'tcx>>,
query: impl QueryDispatcher<Qcx = QueryCtxt<'tcx>>,
qcx: QueryCtxt<'tcx>,
) {
let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name());
@ -442,7 +442,7 @@ pub(crate) fn query_key_hash_verify<'tcx>(
fn try_load_from_on_disk_cache<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode)
where
Q: QueryDispatcher<QueryCtxt<'tcx>>,
Q: QueryDispatcher<Qcx = QueryCtxt<'tcx>>,
{
debug_assert!(tcx.dep_graph.is_green(&dep_node));
@ -488,7 +488,7 @@ where
fn force_from_dep_node<'tcx, Q>(query: Q, tcx: TyCtxt<'tcx>, dep_node: DepNode) -> bool
where
Q: QueryDispatcher<QueryCtxt<'tcx>>,
Q: QueryDispatcher<Qcx = QueryCtxt<'tcx>>,
{
// We must avoid ever having to call `force_from_dep_node()` for a
// `DepNode::codegen_unit`:
@ -523,8 +523,7 @@ pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>(
where
Q: QueryDispatcherUnerased<'tcx>,
{
let fingerprint_style =
<Q::Dispatcher as QueryDispatcher<QueryCtxt<'tcx>>>::Key::fingerprint_style();
let fingerprint_style = <Q::Dispatcher as QueryDispatcher>::Key::fingerprint_style();
if is_anon || !fingerprint_style.reconstructible() {
return DepKindVTable {
@ -731,7 +730,7 @@ macro_rules! define_queries {
}
#[inline(always)]
fn restore_val(value: <Self::Dispatcher as QueryDispatcher<QueryCtxt<'tcx>>>::Value) -> Self::UnerasedValue {
fn restore_val(value: <Self::Dispatcher as QueryDispatcher>::Value) -> Self::UnerasedValue {
restore::<queries::$name::Value<'tcx>>(value)
}
}

View file

@ -5,13 +5,18 @@ use rustc_data_structures::fingerprint::Fingerprint;
use rustc_span::ErrorGuaranteed;
use super::QueryStackFrameExtra;
use crate::dep_graph::{DepKind, DepNode, DepNodeParams, SerializedDepNodeIndex};
use crate::dep_graph::{DepKind, DepNode, DepNodeParams, HasDepContext, SerializedDepNodeIndex};
use crate::ich::StableHashingContext;
use crate::query::caches::QueryCache;
use crate::query::{CycleError, CycleErrorHandling, DepNodeIndex, QueryContext, QueryState};
pub type HashResult<V> = Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>;
/// Unambiguous shorthand for `<This::Qcx as HasDepContext>::DepContext`.
#[expect(type_alias_bounds)]
type DepContextOf<This: QueryDispatcher> =
<<This as QueryDispatcher>::Qcx as HasDepContext>::DepContext;
/// Trait that can be used as a vtable for a single query, providing operations
/// and metadata for that query.
///
@ -20,12 +25,15 @@ pub type HashResult<V> = Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerp
/// Those types are not visible from this `rustc_query_system` crate.
///
/// "Dispatcher" should be understood as a near-synonym of "vtable".
pub trait QueryDispatcher<Qcx: QueryContext>: Copy {
pub trait QueryDispatcher: Copy {
fn name(self) -> &'static str;
/// Query context used by this dispatcher, i.e. `rustc_query_impl::QueryCtxt`.
type Qcx: QueryContext;
// `Key` and `Value` are `Copy` instead of `Clone` to ensure copying them stays cheap,
// but it isn't necessary.
type Key: DepNodeParams<Qcx::DepContext> + Eq + Hash + Copy + Debug;
type Key: DepNodeParams<DepContextOf<Self>> + Eq + Hash + Copy + Debug;
type Value: Copy;
type Cache: QueryCache<Key = Self::Key, Value = Self::Value>;
@ -33,36 +41,40 @@ pub trait QueryDispatcher<Qcx: QueryContext>: Copy {
fn format_value(self) -> fn(&Self::Value) -> String;
// Don't use this method to access query results, instead use the methods on TyCtxt
fn query_state<'a>(self, tcx: Qcx) -> &'a QueryState<Self::Key, Qcx::QueryInfo>
where
Qcx: 'a;
fn query_state<'a>(
self,
tcx: Self::Qcx,
) -> &'a QueryState<Self::Key, <Self::Qcx as QueryContext>::QueryInfo>;
// Don't use this method to access query results, instead use the methods on TyCtxt
fn query_cache<'a>(self, tcx: Qcx) -> &'a Self::Cache
where
Qcx: 'a;
fn query_cache<'a>(self, tcx: Self::Qcx) -> &'a Self::Cache;
fn cache_on_disk(self, tcx: Qcx::DepContext, key: &Self::Key) -> bool;
fn cache_on_disk(self, tcx: DepContextOf<Self>, key: &Self::Key) -> bool;
// Don't use this method to compute query results, instead use the methods on TyCtxt
fn execute_query(self, tcx: Qcx::DepContext, k: Self::Key) -> Self::Value;
fn execute_query(self, tcx: DepContextOf<Self>, k: Self::Key) -> Self::Value;
fn compute(self, tcx: Qcx, key: Self::Key) -> Self::Value;
fn compute(self, tcx: Self::Qcx, key: Self::Key) -> Self::Value;
fn try_load_from_disk(
self,
tcx: Qcx,
tcx: Self::Qcx,
key: &Self::Key,
prev_index: SerializedDepNodeIndex,
index: DepNodeIndex,
) -> Option<Self::Value>;
fn loadable_from_disk(self, qcx: Qcx, key: &Self::Key, idx: SerializedDepNodeIndex) -> bool;
fn loadable_from_disk(
self,
qcx: Self::Qcx,
key: &Self::Key,
idx: SerializedDepNodeIndex,
) -> bool;
/// Synthesize an error value to let compilation continue after a cycle.
fn value_from_cycle_error(
self,
tcx: Qcx::DepContext,
tcx: DepContextOf<Self>,
cycle_error: &CycleError<QueryStackFrameExtra>,
guar: ErrorGuaranteed,
) -> Self::Value;
@ -77,7 +89,7 @@ pub trait QueryDispatcher<Qcx: QueryContext>: Copy {
fn hash_result(self) -> HashResult<Self::Value>;
// Just here for convenience and checking that the key matches the kind, don't override this.
fn construct_dep_node(self, tcx: Qcx::DepContext, key: &Self::Key) -> DepNode {
fn construct_dep_node(self, tcx: DepContextOf<Self>, key: &Self::Key) -> DepNode {
DepNode::construct(tcx, self.dep_kind(), key)
}
}

View file

@ -19,7 +19,9 @@ use rustc_span::{DUMMY_SP, Span};
use tracing::instrument;
use super::{QueryDispatcher, QueryStackFrameExtra};
use crate::dep_graph::{DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams};
use crate::dep_graph::{
DepContext, DepGraphData, DepNode, DepNodeIndex, DepNodeParams, HasDepContext,
};
use crate::ich::StableHashingContext;
use crate::query::caches::QueryCache;
use crate::query::job::{QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryLatch, report_cycle};
@ -124,24 +126,22 @@ where
#[cold]
#[inline(never)]
fn mk_cycle<Q, Qcx>(query: Q, qcx: Qcx, cycle_error: CycleError) -> Q::Value
fn mk_cycle<Q>(query: Q, qcx: Q::Qcx, cycle_error: CycleError) -> Q::Value
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
let error = report_cycle(qcx.dep_context().sess(), &cycle_error);
handle_cycle_error(query, qcx, &cycle_error, error)
}
fn handle_cycle_error<Q, Qcx>(
fn handle_cycle_error<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
cycle_error: &CycleError,
error: Diag<'_>,
) -> Q::Value
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
match query.cycle_error_handling() {
CycleErrorHandling::Error => {
@ -272,15 +272,14 @@ where
#[cold]
#[inline(never)]
fn cycle_error<Q, Qcx>(
fn cycle_error<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
try_execute: QueryJobId,
span: Span,
) -> (Q::Value, Option<DepNodeIndex>)
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
// Ensure there was no errors collecting all active jobs.
// We need the complete map to ensure we find a cycle to break.
@ -291,17 +290,16 @@ where
}
#[inline(always)]
fn wait_for_query<Q, Qcx>(
fn wait_for_query<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
span: Span,
key: Q::Key,
latch: QueryLatch<Qcx::QueryInfo>,
latch: QueryLatch<<Q::Qcx as QueryContext>::QueryInfo>,
current: Option<QueryJobId>,
) -> (Q::Value, Option<DepNodeIndex>)
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
// For parallel queries, we'll block and wait until the query running
// in another thread has completed. Record how long we wait in the
@ -341,16 +339,15 @@ where
}
#[inline(never)]
fn try_execute_query<Q, Qcx, const INCR: bool>(
fn try_execute_query<Q, const INCR: bool>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
span: Span,
key: Q::Key,
dep_node: Option<DepNode>,
) -> (Q::Value, Option<DepNodeIndex>)
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
let state = query.query_state(qcx);
let key_hash = sharded::make_hash(&key);
@ -382,7 +379,7 @@ where
// Drop the lock before we start executing the query
drop(state_lock);
execute_job::<_, _, INCR>(query, qcx, state, key, key_hash, id, dep_node)
execute_job::<Q, INCR>(query, qcx, state, key, key_hash, id, dep_node)
}
Entry::Occupied(mut entry) => {
match &mut entry.get_mut().1 {
@ -411,18 +408,17 @@ where
}
#[inline(always)]
fn execute_job<Q, Qcx, const INCR: bool>(
fn execute_job<Q, const INCR: bool>(
query: Q,
qcx: Qcx,
state: &QueryState<Q::Key, Qcx::QueryInfo>,
qcx: Q::Qcx,
state: &QueryState<Q::Key, <Q::Qcx as QueryContext>::QueryInfo>,
key: Q::Key,
key_hash: u64,
id: QueryJobId,
dep_node: Option<DepNode>,
) -> (Q::Value, Option<DepNodeIndex>)
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
// Use `JobOwner` so the query will be poisoned if executing it panics.
let job_owner = JobOwner { state, key };
@ -484,15 +480,14 @@ where
// Fast path for when incr. comp. is off.
#[inline(always)]
fn execute_job_non_incr<Q, Qcx>(
fn execute_job_non_incr<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
key: Q::Key,
job_id: QueryJobId,
) -> (Q::Value, DepNodeIndex)
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled());
@ -521,17 +516,16 @@ where
}
#[inline(always)]
fn execute_job_incr<Q, Qcx>(
fn execute_job_incr<Q>(
query: Q,
qcx: Qcx,
dep_graph_data: &DepGraphData<Qcx::Deps>,
qcx: Q::Qcx,
dep_graph_data: &DepGraphData<<Q::Qcx as HasDepContext>::Deps>,
key: Q::Key,
mut dep_node_opt: Option<DepNode>,
job_id: QueryJobId,
) -> (Q::Value, DepNodeIndex)
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
if !query.anon() && !query.eval_always() {
// `to_dep_node` is expensive for some `DepKind`s.
@ -577,16 +571,15 @@ where
}
#[inline(always)]
fn try_load_from_disk_and_cache_in_memory<Q, Qcx>(
fn try_load_from_disk_and_cache_in_memory<Q>(
query: Q,
dep_graph_data: &DepGraphData<Qcx::Deps>,
qcx: Qcx,
dep_graph_data: &DepGraphData<<Q::Qcx as HasDepContext>::Deps>,
qcx: Q::Qcx,
key: &Q::Key,
dep_node: &DepNode,
) -> Option<(Q::Value, DepNodeIndex)>
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
// Note this function can be called concurrently from the same query
// We must ensure that this is handled correctly.
@ -764,15 +757,14 @@ fn incremental_verify_ich_failed<Tcx>(
///
/// Note: The optimization is only available during incr. comp.
#[inline(never)]
fn ensure_must_run<Q, Qcx>(
fn ensure_must_run<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
key: &Q::Key,
check_cache: bool,
) -> (bool, Option<DepNode>)
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
if query.eval_always() {
return (true, None);
@ -817,27 +809,25 @@ pub enum QueryMode {
}
#[inline(always)]
pub fn get_query_non_incr<Q, Qcx>(query: Q, qcx: Qcx, span: Span, key: Q::Key) -> Q::Value
pub fn get_query_non_incr<Q>(query: Q, qcx: Q::Qcx, span: Span, key: Q::Key) -> Q::Value
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
debug_assert!(!qcx.dep_context().dep_graph().is_fully_enabled());
ensure_sufficient_stack(|| try_execute_query::<Q, Qcx, false>(query, qcx, span, key, None).0)
ensure_sufficient_stack(|| try_execute_query::<Q, false>(query, qcx, span, key, None).0)
}
#[inline(always)]
pub fn get_query_incr<Q, Qcx>(
pub fn get_query_incr<Q>(
query: Q,
qcx: Qcx,
qcx: Q::Qcx,
span: Span,
key: Q::Key,
mode: QueryMode,
) -> Option<Q::Value>
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
debug_assert!(qcx.dep_context().dep_graph().is_fully_enabled());
@ -851,19 +841,17 @@ where
None
};
let (result, dep_node_index) = ensure_sufficient_stack(|| {
try_execute_query::<_, _, true>(query, qcx, span, key, dep_node)
});
let (result, dep_node_index) =
ensure_sufficient_stack(|| try_execute_query::<Q, true>(query, qcx, span, key, dep_node));
if let Some(dep_node_index) = dep_node_index {
qcx.dep_context().dep_graph().read_index(dep_node_index)
}
Some(result)
}
pub fn force_query<Q, Qcx>(query: Q, qcx: Qcx, key: Q::Key, dep_node: DepNode)
pub fn force_query<Q>(query: Q, qcx: Q::Qcx, key: Q::Key, dep_node: DepNode)
where
Q: QueryDispatcher<Qcx>,
Qcx: QueryContext,
Q: QueryDispatcher,
{
// We may be concurrently trying both execute and force a query.
// Ensure that only one of them runs the query.
@ -875,6 +863,6 @@ where
debug_assert!(!query.anon());
ensure_sufficient_stack(|| {
try_execute_query::<_, _, true>(query, qcx, DUMMY_SP, key, Some(dep_node))
try_execute_query::<Q, true>(query, qcx, DUMMY_SP, key, Some(dep_node))
});
}