Skip to content

Commit 5471381

Browse files
committed
Allow to set a query's result as a side effect.
1 parent ca42dd6 commit 5471381

File tree

4 files changed

+171
-1
lines changed

4 files changed

+171
-1
lines changed

compiler/rustc_macros/src/query.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ struct QueryModifiers {
114114

115115
/// Always remap the ParamEnv's constness before hashing.
116116
remap_env_constness: Option<Ident>,
117+
118+
/// Generate a `feed` method to set the query's value from another query.
119+
feedable: Option<Ident>,
117120
}
118121

119122
fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
@@ -128,6 +131,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
128131
let mut depth_limit = None;
129132
let mut separate_provide_extern = None;
130133
let mut remap_env_constness = None;
134+
let mut feedable = None;
131135

132136
while !input.is_empty() {
133137
let modifier: Ident = input.parse()?;
@@ -187,6 +191,8 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
187191
try_insert!(separate_provide_extern = modifier);
188192
} else if modifier == "remap_env_constness" {
189193
try_insert!(remap_env_constness = modifier);
194+
} else if modifier == "feedable" {
195+
try_insert!(feedable = modifier);
190196
} else {
191197
return Err(Error::new(modifier.span(), "unknown query modifier"));
192198
}
@@ -206,6 +212,7 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
206212
depth_limit,
207213
separate_provide_extern,
208214
remap_env_constness,
215+
feedable,
209216
})
210217
}
211218

@@ -296,6 +303,7 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
296303
let mut query_stream = quote! {};
297304
let mut query_description_stream = quote! {};
298305
let mut query_cached_stream = quote! {};
306+
let mut feedable_queries = quote! {};
299307

300308
for query in queries.0 {
301309
let Query { name, arg, modifiers, .. } = &query;
@@ -350,6 +358,13 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
350358
[#attribute_stream] fn #name(#arg) #result,
351359
});
352360

361+
if modifiers.feedable.is_some() {
362+
feedable_queries.extend(quote! {
363+
#(#doc_comments)*
364+
[#attribute_stream] fn #name(#arg) #result,
365+
});
366+
}
367+
353368
add_query_desc_cached_impl(&query, &mut query_description_stream, &mut query_cached_stream);
354369
}
355370

@@ -363,7 +378,11 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
363378
}
364379
}
365380
}
366-
381+
macro_rules! rustc_feedable_queries {
382+
( $macro:ident! ) => {
383+
$macro!(#feedable_queries);
384+
}
385+
}
367386
pub mod descs {
368387
use super::*;
369388
#query_description_stream

compiler/rustc_middle/src/query/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ rustc_queries! {
165165
}
166166
cache_on_disk_if { key.is_local() }
167167
separate_provide_extern
168+
feedable
168169
}
169170

170171
query collect_trait_impl_trait_tys(key: DefId)

compiler/rustc_middle/src/ty/query.rs

+68
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ pub struct TyCtxtEnsure<'tcx> {
8585
pub tcx: TyCtxt<'tcx>,
8686
}
8787

88+
#[derive(Copy, Clone)]
89+
pub struct TyCtxtFeed<'tcx> {
90+
pub tcx: TyCtxt<'tcx>,
91+
}
92+
8893
impl<'tcx> TyCtxt<'tcx> {
8994
/// Returns a transparent wrapper for `TyCtxt`, which ensures queries
9095
/// are executed instead of just returning their results.
@@ -93,6 +98,12 @@ impl<'tcx> TyCtxt<'tcx> {
9398
TyCtxtEnsure { tcx: self }
9499
}
95100

101+
/// Returns a transparent wrapper for `TyCtxt`, for setting a result into a query.
102+
#[inline(always)]
103+
pub fn feed(self) -> TyCtxtFeed<'tcx> {
104+
TyCtxtFeed { tcx: self }
105+
}
106+
96107
/// Returns a transparent wrapper for `TyCtxt` which uses
97108
/// `span` as the location of queries performed through it.
98109
#[inline(always)]
@@ -175,6 +186,18 @@ macro_rules! opt_remap_env_constness {
175186
};
176187
}
177188

189+
macro_rules! hash_result {
190+
([]) => {{
191+
Some(dep_graph::hash_result)
192+
}};
193+
([(no_hash) $($rest:tt)*]) => {{
194+
None
195+
}};
196+
([$other:tt $($modifiers:tt)*]) => {
197+
hash_result!([$($modifiers)*])
198+
};
199+
}
200+
178201
macro_rules! define_callbacks {
179202
(
180203
$($(#[$attr:meta])*
@@ -327,6 +350,50 @@ macro_rules! define_callbacks {
327350
};
328351
}
329352

353+
macro_rules! define_feedable {
354+
($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
355+
impl<'tcx> TyCtxtFeed<'tcx> {
356+
$($(#[$attr])*
357+
#[inline(always)]
358+
pub fn $name(
359+
self,
360+
key: query_helper_param_ty!($($K)*),
361+
value: $V,
362+
) -> query_stored::$name<'tcx> {
363+
let key = key.into_query_param();
364+
opt_remap_env_constness!([$($modifiers)*][key]);
365+
366+
let tcx = self.tcx;
367+
let cache = &tcx.query_caches.$name;
368+
369+
let cached = try_get_cached(tcx, cache, &key, copy);
370+
371+
match cached {
372+
Ok(old) => {
373+
assert_eq!(
374+
value, old,
375+
"Trying to feed an already recorded value for query {} key={key:?}",
376+
stringify!($name),
377+
);
378+
return old;
379+
}
380+
Err(()) => (),
381+
}
382+
383+
let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::DepKind::$name, &key);
384+
let dep_node_index = tcx.dep_graph.with_feed_task(
385+
dep_node,
386+
tcx,
387+
key,
388+
&value,
389+
hash_result!([$($modifiers)*]).unwrap(),
390+
);
391+
cache.complete(key, value, dep_node_index)
392+
})*
393+
}
394+
}
395+
}
396+
330397
// Each of these queries corresponds to a function pointer field in the
331398
// `Providers` struct for requesting a value of that type, and a method
332399
// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way
@@ -340,6 +407,7 @@ macro_rules! define_callbacks {
340407
// as they will raise an fatal error on query cycles instead.
341408

342409
rustc_query_append! { define_callbacks! }
410+
rustc_feedable_queries! { define_feedable! }
343411

344412
mod sealed {
345413
use super::{DefId, LocalDefId, OwnerId};

compiler/rustc_query_system/src/dep_graph/graph.rs

+82
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,88 @@ impl<K: DepKind> DepGraph<K> {
489489
}
490490
}
491491

492+
/// Create a node when we force-feed a value into the query cache.
493+
/// This is used to remove cycles during type-checking const generic parameters.
494+
///
495+
/// As usual in the query system, we consider the current state of the calling query
496+
/// only depends on the list of dependencies up to now. As a consequence, the value
497+
/// that this query gives us can only depend on those dependencies too. Therefore,
498+
/// it is sound to use the current dependency set for the created node.
499+
///
500+
/// During replay, the order of the nodes is relevant in the dependency graph.
501+
/// So the unchanged replay will mark the caller query before trying to mark this one.
502+
/// If there is a change to report, the caller query will be re-executed before this one.
503+
///
504+
/// FIXME: If the code is changed enough for this node to be marked before requiring the
505+
/// caller's node, we suppose that those changes will be enough to mark this node red and
506+
/// force a recomputation using the "normal" way.
507+
pub fn with_feed_task<Ctxt: DepContext<DepKind = K>, A: Debug, R: Debug>(
508+
&self,
509+
node: DepNode<K>,
510+
cx: Ctxt,
511+
key: A,
512+
result: &R,
513+
hash_result: fn(&mut StableHashingContext<'_>, &R) -> Fingerprint,
514+
) -> DepNodeIndex {
515+
if let Some(data) = self.data.as_ref() {
516+
if let Some(dep_node_index) = self.dep_node_index_of_opt(&node) {
517+
#[cfg(debug_assertions)]
518+
{
519+
let hashing_timer = cx.profiler().incr_result_hashing();
520+
let current_fingerprint =
521+
cx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result));
522+
hashing_timer.finish_with_query_invocation_id(dep_node_index.into());
523+
data.current.record_edge(dep_node_index, node, current_fingerprint);
524+
}
525+
526+
return dep_node_index;
527+
}
528+
529+
let mut edges = SmallVec::new();
530+
K::read_deps(|task_deps| match task_deps {
531+
TaskDepsRef::Allow(deps) => edges.extend(deps.lock().reads.iter().copied()),
532+
TaskDepsRef::Ignore | TaskDepsRef::Forbid => {
533+
panic!("Cannot summarize when dependencies are not recorded.")
534+
}
535+
});
536+
537+
let hashing_timer = cx.profiler().incr_result_hashing();
538+
let current_fingerprint =
539+
cx.with_stable_hashing_context(|mut hcx| hash_result(&mut hcx, result));
540+
541+
let print_status = cfg!(debug_assertions) && cx.sess().opts.unstable_opts.dep_tasks;
542+
543+
// Intern the new `DepNode` with the dependencies up-to-now.
544+
let (dep_node_index, prev_and_color) = data.current.intern_node(
545+
cx.profiler(),
546+
&data.previous,
547+
node,
548+
edges,
549+
Some(current_fingerprint),
550+
print_status,
551+
);
552+
553+
hashing_timer.finish_with_query_invocation_id(dep_node_index.into());
554+
555+
if let Some((prev_index, color)) = prev_and_color {
556+
debug_assert!(
557+
data.colors.get(prev_index).is_none(),
558+
"DepGraph::with_task() - Duplicate DepNodeColor insertion for {key:?}",
559+
);
560+
561+
data.colors.insert(prev_index, color);
562+
}
563+
564+
dep_node_index
565+
} else {
566+
// Incremental compilation is turned off. We just execute the task
567+
// without tracking. We still provide a dep-node index that uniquely
568+
// identifies the task so that we have a cheap way of referring to
569+
// the query for self-profiling.
570+
self.next_virtual_depnode_index()
571+
}
572+
}
573+
492574
#[inline]
493575
pub fn dep_node_index_of(&self, dep_node: &DepNode<K>) -> DepNodeIndex {
494576
self.dep_node_index_of_opt(dep_node).unwrap()

0 commit comments

Comments
 (0)