Don't use HasDepContext in DepGraph::with_task

The need for a `HasDepContext` impl on tuples can be avoided by passing the
query vtable as part of an argument tuple instead.
This commit is contained in:
Zalathar 2026-02-16 10:54:52 +11:00
parent fbb34d8c75
commit db58395a6b
4 changed files with 46 additions and 53 deletions

View file

@ -268,17 +268,17 @@ impl DepGraph {
}
#[inline(always)]
pub fn with_task<'tcx, Ctxt: HasDepContext<'tcx>, A: Debug, R>(
pub fn with_task<'tcx, A: Debug, R>(
&self,
key: DepNode,
cx: Ctxt,
arg: A,
task: fn(Ctxt, A) -> R,
dep_node: DepNode,
tcx: TyCtxt<'tcx>,
task_arg: A,
task_fn: fn(tcx: TyCtxt<'tcx>, task_arg: A) -> R,
hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
) -> (R, DepNodeIndex) {
match self.data() {
Some(data) => data.with_task(key, cx, arg, task, hash_result),
None => (task(cx, arg), self.next_virtual_depnode_index()),
Some(data) => data.with_task(dep_node, tcx, task_arg, task_fn, hash_result),
None => (task_fn(tcx, task_arg), self.next_virtual_depnode_index()),
}
}
@ -310,33 +310,21 @@ impl DepGraphData {
/// prevent implicit 'leaks' of tracked state into the task (which
/// could then be read without generating correct edges in the
/// dep-graph -- see the [rustc dev guide] for more details on
/// the dep-graph). To this end, the task function gets exactly two
/// pieces of state: the context `cx` and an argument `arg`. Both
/// of these bits of state must be of some type that implements
/// `DepGraphSafe` and hence does not leak.
/// the dep-graph).
///
/// The choice of two arguments is not fundamental. One argument
/// would work just as well, since multiple values can be
/// collected using tuples. However, using two arguments works out
/// to be quite convenient, since it is common to need a context
/// (`cx`) and some argument (e.g., a `DefId` identifying what
/// item to process).
///
/// For cases where you need some other number of arguments:
///
/// - If you only need one argument, just use `()` for the `arg`
/// parameter.
/// - If you need 3+ arguments, use a tuple for the
/// `arg` parameter.
/// Therefore, the task function takes a `TyCtxt`, plus exactly one
/// additional argument, `task_arg`. The additional argument type can be
/// `()` if no argument is needed, or a tuple if multiple arguments are
/// needed.
///
/// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/queries/incremental-compilation.html
#[inline(always)]
pub fn with_task<'tcx, Ctxt: HasDepContext<'tcx>, A: Debug, R>(
pub fn with_task<'tcx, A: Debug, R>(
&self,
key: DepNode,
cx: Ctxt,
arg: A,
task: fn(Ctxt, A) -> R,
dep_node: DepNode,
tcx: TyCtxt<'tcx>,
task_arg: A,
task_fn: fn(tcx: TyCtxt<'tcx>, task_arg: A) -> R,
hash_result: Option<fn(&mut StableHashingContext<'_>, &R) -> Fingerprint>,
) -> (R, DepNodeIndex) {
// If the following assertion triggers, it can have two reasons:
@ -344,32 +332,28 @@ impl DepGraphData {
// in `DepGraph::try_mark_green()`.
// 2. Two distinct query keys get mapped to the same `DepNode`
// (see for example #48923).
self.assert_dep_node_not_yet_allocated_in_current_session(
cx.dep_context().sess,
&key,
|| {
format!(
"forcing query with already existing `DepNode`\n\
- query-key: {arg:?}\n\
- dep-node: {key:?}"
)
},
);
self.assert_dep_node_not_yet_allocated_in_current_session(tcx.sess, &dep_node, || {
format!(
"forcing query with already existing `DepNode`\n\
- query-key: {task_arg:?}\n\
- dep-node: {dep_node:?}"
)
});
let with_deps = |task_deps| with_deps(task_deps, || task(cx, arg));
let (result, edges) = if cx.dep_context().is_eval_always(key.kind) {
let with_deps = |task_deps| with_deps(task_deps, || task_fn(tcx, task_arg));
let (result, edges) = if tcx.is_eval_always(dep_node.kind) {
(with_deps(TaskDepsRef::EvalAlways), EdgesVec::new())
} else {
let task_deps = Lock::new(TaskDeps::new(
#[cfg(debug_assertions)]
Some(key),
Some(dep_node),
0,
));
(with_deps(TaskDepsRef::Allow(&task_deps)), task_deps.into_inner().reads)
};
let dep_node_index =
self.hash_result_and_alloc_node(cx.dep_context(), key, edges, &result, hash_result);
self.hash_result_and_alloc_node(tcx, dep_node, edges, &result, hash_result);
(result, dep_node_index)
}

View file

@ -35,12 +35,6 @@ impl<'tcx> HasDepContext<'tcx> for TyCtxt<'tcx> {
}
}
impl<'tcx, T: HasDepContext<'tcx>, Q: Copy> HasDepContext<'tcx> for (T, Q) {
fn dep_context(&self) -> TyCtxt<'tcx> {
self.0.dep_context()
}
}
/// Describes the contents of the fingerprint generated by a given query.
///
/// This is mainly for determining whether and how we can reconstruct a key

View file

@ -475,9 +475,9 @@ fn execute_job_incr<'tcx, C: QueryCache, const FLAGS: QueryFlags>(
// Call the query provider.
dep_graph_data.with_task(
dep_node,
(tcx, query),
key,
|(tcx, query), key| query.invoke_provider(tcx, key),
tcx,
(query, key),
|tcx, (query, key)| query.invoke_provider(tcx, key),
query.hash_result(),
)
});

View file

@ -9,6 +9,7 @@
#![feature(try_blocks)]
// tidy-alphabetical-end
use std::fmt;
use std::marker::ConstParamTy;
use rustc_data_structures::sync::AtomicU64;
@ -76,6 +77,20 @@ impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Clone
}
}
impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> fmt::Debug
for SemiDynamicQueryDispatcher<'tcx, C, FLAGS>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// When debug-printing a query dispatcher (e.g. for ICE or tracing),
// just print the query name to know what query we're dealing with.
// The other fields and flags are probably just unhelpful noise.
//
// If there is need for a more detailed dump of all flags and fields,
// consider writing a separate dump method and calling it explicitly.
f.write_str(self.name())
}
}
impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> SemiDynamicQueryDispatcher<'tcx, C, FLAGS> {
#[inline(always)]
fn name(self) -> &'static str {