Skip to main content

rustc_query_impl/
plumbing.rs

1//! The implementation of the query system itself. This defines the macros that
2//! generate the actual methods on tcx which find and execute the provider,
3//! manage the caches, and so forth.
4
5use std::num::NonZero;
6
7use rustc_data_structures::sync::{DynSend, DynSync};
8use rustc_data_structures::unord::UnordMap;
9use rustc_hir::def_id::DefId;
10use rustc_hir::limit::Limit;
11use rustc_index::Idx;
12use rustc_middle::bug;
13#[expect(unused_imports, reason = "used by doc comments")]
14use rustc_middle::dep_graph::DepKindVTable;
15use rustc_middle::dep_graph::{DepKind, DepNode, DepNodeIndex, DepNodeKey, SerializedDepNodeIndex};
16use rustc_middle::query::erase::{Erasable, Erased};
17use rustc_middle::query::on_disk_cache::{
18    AbsoluteBytePos, CacheDecoder, CacheEncoder, EncodedDepNodeIndex,
19};
20use rustc_middle::query::plumbing::QueryVTable;
21use rustc_middle::query::{
22    QueryCache, QueryJobId, QueryKey, QueryStackDeferred, QueryStackFrame, QueryStackFrameExtra,
23    erase,
24};
25use rustc_middle::ty::codec::TyEncoder;
26use rustc_middle::ty::print::with_reduced_queries;
27use rustc_middle::ty::tls::{self, ImplicitCtxt};
28use rustc_middle::ty::{self, TyCtxt};
29use rustc_serialize::{Decodable, Encodable};
30use rustc_span::def_id::LOCAL_CRATE;
31
32use crate::error::{QueryOverflow, QueryOverflowNote};
33use crate::execution::{all_inactive, force_query};
34use crate::job::find_dep_kind_root;
35use crate::{GetQueryVTable, collect_active_jobs_from_all_queries};
36
37fn depth_limit_error<'tcx>(tcx: TyCtxt<'tcx>, job: QueryJobId) {
38    let job_map =
39        collect_active_jobs_from_all_queries(tcx, true).expect("failed to collect active queries");
40    let (info, depth) = find_dep_kind_root(job, job_map);
41
42    let suggested_limit = match tcx.recursion_limit() {
43        Limit(0) => Limit(2),
44        limit => limit * 2,
45    };
46
47    tcx.sess.dcx().emit_fatal(QueryOverflow {
48        span: info.job.span,
49        note: QueryOverflowNote { desc: info.frame.info.extract().description, depth },
50        suggested_limit,
51        crate_name: tcx.crate_name(LOCAL_CRATE),
52    });
53}
54
55#[inline]
56pub(crate) fn next_job_id<'tcx>(tcx: TyCtxt<'tcx>) -> QueryJobId {
57    QueryJobId(
58        NonZero::new(tcx.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
59            .unwrap(),
60    )
61}
62
63#[inline]
64pub(crate) fn current_query_job<'tcx>(tcx: TyCtxt<'tcx>) -> Option<QueryJobId> {
65    tls::with_related_context(tcx, |icx| icx.query)
66}
67
68/// Executes a job by changing the `ImplicitCtxt` to point to the
69/// new query job while it executes.
70#[inline(always)]
71pub(crate) fn start_query<'tcx, R>(
72    tcx: TyCtxt<'tcx>,
73    token: QueryJobId,
74    depth_limit: bool,
75    compute: impl FnOnce() -> R,
76) -> R {
77    // The `TyCtxt` stored in TLS has the same global interner lifetime
78    // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes
79    // when accessing the `ImplicitCtxt`.
80    tls::with_related_context(tcx, move |current_icx| {
81        if depth_limit && !tcx.recursion_limit().value_within_limit(current_icx.query_depth) {
82            depth_limit_error(tcx, token);
83        }
84
85        // Update the `ImplicitCtxt` to point to our new query job.
86        let new_icx = ImplicitCtxt {
87            tcx,
88            query: Some(token),
89            query_depth: current_icx.query_depth + depth_limit as usize,
90            task_deps: current_icx.task_deps,
91        };
92
93        // Use the `ImplicitCtxt` while we execute the query.
94        tls::enter_context(&new_icx, compute)
95    })
96}
97
98pub(super) fn try_mark_green<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool {
99    tcx.dep_graph.try_mark_green(tcx, dep_node).is_some()
100}
101
102/// The deferred part of a deferred query stack frame.
103fn mk_query_stack_frame_extra<'tcx, Cache>(
104    (tcx, vtable, key): (TyCtxt<'tcx>, &'tcx QueryVTable<'tcx, Cache>, Cache::Key),
105) -> QueryStackFrameExtra
106where
107    Cache: QueryCache,
108    Cache::Key: QueryKey,
109{
110    let def_id = key.key_as_def_id();
111
112    // If reduced queries are requested, we may be printing a query stack due
113    // to a panic. Avoid using `default_span` and `def_kind` in that case.
114    let reduce_queries = with_reduced_queries();
115
116    // Avoid calling queries while formatting the description
117    let description = {
    {
        let _guard = ReducedQueriesGuard::new();
        {
            let _guard = ForcedImplGuard::new();
            {
                let _guard = NoTrimmedGuard::new();
                {
                    let _guard = NoVisibleGuard::new();
                    (vtable.description_fn)(tcx, key)
                }
            }
        }
    }
}ty::print::with_no_queries!((vtable.description_fn)(tcx, key));
118    let description = if tcx.sess.verbose_internals() {
119        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{1} [{0:?}]", vtable.name,
                description))
    })format!("{description} [{name:?}]", name = vtable.name)
120    } else {
121        description
122    };
123    let span = if vtable.dep_kind == DepKind::def_span || reduce_queries {
124        // The `def_span` query is used to calculate `default_span`,
125        // so exit to avoid infinite recursion.
126        None
127    } else {
128        Some(key.default_span(tcx))
129    };
130
131    let def_kind = if vtable.dep_kind == DepKind::def_kind || reduce_queries {
132        // Try to avoid infinite recursion.
133        None
134    } else {
135        def_id.and_then(|def_id| def_id.as_local()).map(|def_id| tcx.def_kind(def_id))
136    };
137    QueryStackFrameExtra::new(description, span, def_kind)
138}
139
140pub(crate) fn create_deferred_query_stack_frame<'tcx, C>(
141    tcx: TyCtxt<'tcx>,
142    vtable: &'tcx QueryVTable<'tcx, C>,
143    key: C::Key,
144) -> QueryStackFrame<QueryStackDeferred<'tcx>>
145where
146    C: QueryCache<Key: QueryKey + DynSend + DynSync>,
147    QueryVTable<'tcx, C>: DynSync,
148{
149    let kind = vtable.dep_kind;
150
151    let def_id: Option<DefId> = key.key_as_def_id();
152    let def_id_for_ty_in_cycle: Option<DefId> = key.def_id_for_ty_in_cycle();
153
154    let info = QueryStackDeferred::new((tcx, vtable, key), mk_query_stack_frame_extra);
155    QueryStackFrame::new(info, kind, def_id, def_id_for_ty_in_cycle)
156}
157
158pub(crate) fn encode_query_results<'a, 'tcx, C, V>(
159    tcx: TyCtxt<'tcx>,
160    query: &'tcx QueryVTable<'tcx, C>,
161    encoder: &mut CacheEncoder<'a, 'tcx>,
162    query_result_index: &mut EncodedDepNodeIndex,
163) where
164    C: QueryCache<Value = Erased<V>>,
165    V: Erasable + Encodable<CacheEncoder<'a, 'tcx>>,
166{
167    let _timer = tcx.prof.generic_activity_with_arg("encode_query_results_for", query.name);
168
169    if !all_inactive(&query.state) {
    ::core::panicking::panic("assertion failed: all_inactive(&query.state)")
};assert!(all_inactive(&query.state));
170    query.cache.iter(&mut |key, value, dep_node| {
171        if query.will_cache_on_disk_for_key(tcx, key) {
172            let dep_node = SerializedDepNodeIndex::new(dep_node.index());
173
174            // Record position of the cache entry.
175            query_result_index.push((dep_node, AbsoluteBytePos::new(encoder.position())));
176
177            // Encode the type check tables with the `SerializedDepNodeIndex`
178            // as tag.
179            encoder.encode_tagged(dep_node, &erase::restore_val::<V>(*value));
180        }
181    });
182}
183
184pub(crate) fn query_key_hash_verify<'tcx, C: QueryCache>(
185    query: &'tcx QueryVTable<'tcx, C>,
186    tcx: TyCtxt<'tcx>,
187) {
188    let _timer = tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name);
189
190    let cache = &query.cache;
191    let mut map = UnordMap::with_capacity(cache.len());
192    cache.iter(&mut |key, _, _| {
193        let node = DepNode::construct(tcx, query.dep_kind, key);
194        if let Some(other_key) = map.insert(node, *key) {
195            ::rustc_middle::util::bug::bug_fmt(format_args!("query key:\n`{0:?}`\nand key:\n`{1:?}`\nmapped to the same dep node:\n{2:?}",
        key, other_key, node));bug!(
196                "query key:\n\
197                `{:?}`\n\
198                and key:\n\
199                `{:?}`\n\
200                mapped to the same dep node:\n\
201                {:?}",
202                key,
203                other_key,
204                node
205            );
206        }
207    });
208}
209
210/// Implementation of [`DepKindVTable::promote_from_disk_fn`] for queries.
211pub(crate) fn promote_from_disk_inner<'tcx, Q: GetQueryVTable<'tcx>>(
212    tcx: TyCtxt<'tcx>,
213    dep_node: DepNode,
214) {
215    let query = Q::query_vtable(tcx);
216    if true {
    if !tcx.dep_graph.is_green(&dep_node) {
        ::core::panicking::panic("assertion failed: tcx.dep_graph.is_green(&dep_node)")
    };
};debug_assert!(tcx.dep_graph.is_green(&dep_node));
217
218    let key = <Q::Cache as QueryCache>::Key::try_recover_key(tcx, &dep_node).unwrap_or_else(|| {
219        {
    ::core::panicking::panic_fmt(format_args!("Failed to recover key for {1:?} with key fingerprint {0}",
            dep_node.key_fingerprint, dep_node));
}panic!(
220            "Failed to recover key for {dep_node:?} with key fingerprint {}",
221            dep_node.key_fingerprint
222        )
223    });
224    if query.will_cache_on_disk_for_key(tcx, &key) {
225        // Call `tcx.$query(key)` for its side-effect of loading the disk-cached
226        // value into memory.
227        (query.call_query_method_fn)(tcx, key);
228    }
229}
230
231pub(crate) fn loadable_from_disk<'tcx>(tcx: TyCtxt<'tcx>, id: SerializedDepNodeIndex) -> bool {
232    if let Some(cache) = tcx.query_system.on_disk_cache.as_ref() {
233        cache.loadable_from_disk(id)
234    } else {
235        false
236    }
237}
238
239pub(crate) fn try_load_from_disk<'tcx, V>(
240    tcx: TyCtxt<'tcx>,
241    prev_index: SerializedDepNodeIndex,
242    index: DepNodeIndex,
243) -> Option<V>
244where
245    V: for<'a> Decodable<CacheDecoder<'a, 'tcx>>,
246{
247    let on_disk_cache = tcx.query_system.on_disk_cache.as_ref()?;
248
249    let prof_timer = tcx.prof.incr_cache_loading();
250
251    // The call to `with_query_deserialization` enforces that no new `DepNodes`
252    // are created during deserialization. See the docs of that method for more
253    // details.
254    let value = tcx
255        .dep_graph
256        .with_query_deserialization(|| on_disk_cache.try_load_query_result(tcx, prev_index));
257
258    prof_timer.finish_with_query_invocation_id(index.into());
259
260    value
261}
262
263/// Implementation of [`DepKindVTable::force_from_dep_node_fn`] for queries.
264pub(crate) fn force_from_dep_node_inner<'tcx, Q: GetQueryVTable<'tcx>>(
265    tcx: TyCtxt<'tcx>,
266    dep_node: DepNode,
267    // Needed by the vtable function signature, but not used when forcing queries.
268    _prev_index: SerializedDepNodeIndex,
269) -> bool {
270    let query = Q::query_vtable(tcx);
271
272    // We must avoid ever having to call `force_from_dep_node()` for a
273    // `DepNode::codegen_unit`:
274    // Since we cannot reconstruct the query key of a `DepNode::codegen_unit`, we
275    // would always end up having to evaluate the first caller of the
276    // `codegen_unit` query that *is* reconstructible. This might very well be
277    // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just
278    // to re-trigger calling the `codegen_unit` query with the right key. At
279    // that point we would already have re-done all the work we are trying to
280    // avoid doing in the first place.
281    // The solution is simple: Just explicitly call the `codegen_unit` query for
282    // each CGU, right after partitioning. This way `try_mark_green` will always
283    // hit the cache instead of having to go through `force_from_dep_node`.
284    // This assertion makes sure, we actually keep applying the solution above.
285    if true {
    if !(dep_node.kind != DepKind::codegen_unit) {
        {
            ::core::panicking::panic_fmt(format_args!("calling force_from_dep_node() on dep_kinds::codegen_unit"));
        }
    };
};debug_assert!(
286        dep_node.kind != DepKind::codegen_unit,
287        "calling force_from_dep_node() on dep_kinds::codegen_unit"
288    );
289
290    if let Some(key) = <Q::Cache as QueryCache>::Key::try_recover_key(tcx, &dep_node) {
291        force_query(query, tcx, key, dep_node);
292        true
293    } else {
294        false
295    }
296}
297
298macro_rules! define_queries {
299    (
300        // Note: `$K` and `$V` are unused but present so this can be called by
301        // `rustc_with_all_queries`.
302        queries {
303            $(
304                $(#[$attr:meta])*
305                fn $name:ident($K:ty) -> $V:ty
306                {
307                    // Search for (QMODLIST) to find all occurrences of this query modifier list.
308                    anon: $anon:literal,
309                    arena_cache: $arena_cache:literal,
310                    cache_on_disk: $cache_on_disk:literal,
311                    cycle_error_handling: $cycle_error_handling:ident,
312                    depth_limit: $depth_limit:literal,
313                    eval_always: $eval_always:literal,
314                    feedable: $feedable:literal,
315                    no_hash: $no_hash:literal,
316                    return_result_from_ensure_ok: $return_result_from_ensure_ok:literal,
317                    separate_provide_extern: $separate_provide_extern:literal,
318                }
319            )*
320        }
321        // Non-queries are unused here.
322        non_queries { $($_:tt)* }
323    ) => {
324        pub(crate) mod query_impl { $(pub(crate) mod $name {
325            use super::super::*;
326            use ::rustc_middle::query::erase::{self, Erased};
327
328            // It seems to be important that every query has its own monomorphic
329            // copy of `execute_query_incr` and `execute_query_non_incr`.
330            // Trying to inline these wrapper functions into their generic
331            // "inner" helpers tends to break `tests/run-make/short-ice`.
332
333            pub(crate) mod execute_query_incr {
334                use super::*;
335
336                // Adding `__rust_end_short_backtrace` marker to backtraces so that we emit the frames
337                // when `RUST_BACKTRACE=1`, add a new mod with `$name` here is to allow duplicate naming
338                #[inline(never)]
339                pub(crate) fn __rust_end_short_backtrace<'tcx>(
340                    tcx: TyCtxt<'tcx>,
341                    span: Span,
342                    key: queries::$name::Key<'tcx>,
343                    mode: QueryMode,
344                ) -> Option<Erased<queries::$name::Value<'tcx>>> {
345                    #[cfg(debug_assertions)]
346                    let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
347                    execution::execute_query_incr_inner(
348                        &tcx.query_system.query_vtables.$name,
349                        tcx,
350                        span,
351                        key,
352                        mode
353                    )
354                }
355            }
356
357            pub(crate) mod execute_query_non_incr {
358                use super::*;
359
360                #[inline(never)]
361                pub(crate) fn __rust_end_short_backtrace<'tcx>(
362                    tcx: TyCtxt<'tcx>,
363                    span: Span,
364                    key: queries::$name::Key<'tcx>,
365                    __mode: QueryMode,
366                ) -> Option<Erased<queries::$name::Value<'tcx>>> {
367                    Some(execution::execute_query_non_incr_inner(
368                        &tcx.query_system.query_vtables.$name,
369                        tcx,
370                        span,
371                        key,
372                    ))
373                }
374            }
375
376            /// Defines an `invoke_provider` function that calls the query's provider,
377            /// to be used as a function pointer in the query's vtable.
378            ///
379            /// To mark a short-backtrace boundary, the function's actual name
380            /// (after demangling) must be `__rust_begin_short_backtrace`.
381            mod invoke_provider_fn {
382                use super::*;
383                use ::rustc_middle::queries::$name::{Key, Value, provided_to_erased};
384
385                #[inline(never)]
386                pub(crate) fn __rust_begin_short_backtrace<'tcx>(
387                    tcx: TyCtxt<'tcx>,
388                    key: Key<'tcx>,
389                ) -> Erased<Value<'tcx>> {
390                    #[cfg(debug_assertions)]
391                    let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
392
393                    // Call the actual provider function for this query.
394
395                    #[cfg($separate_provide_extern)]
396                    let provided_value = if let Some(local_key) = key.as_local_key() {
397                        (tcx.query_system.local_providers.$name)(tcx, local_key)
398                    } else {
399                        (tcx.query_system.extern_providers.$name)(tcx, key)
400                    };
401
402                    #[cfg(not($separate_provide_extern))]
403                    let provided_value = (tcx.query_system.local_providers.$name)(tcx, key);
404
405                    rustc_middle::ty::print::with_reduced_queries!({
406                        tracing::trace!(?provided_value);
407                    });
408
409                    // Erase the returned value, because `QueryVTable` uses erased values.
410                    // For queries with `arena_cache`, this also arena-allocates the value.
411                    provided_to_erased(tcx, provided_value)
412                }
413            }
414
415            pub(crate) fn make_query_vtable<'tcx>(incremental: bool)
416                -> QueryVTable<'tcx, queries::$name::Cache<'tcx>>
417            {
418                QueryVTable {
419                    name: stringify!($name),
420                    anon: $anon,
421                    eval_always: $eval_always,
422                    depth_limit: $depth_limit,
423                    feedable: $feedable,
424                    dep_kind: dep_graph::DepKind::$name,
425                    cycle_error_handling:
426                        rustc_middle::query::CycleErrorHandling::$cycle_error_handling,
427                    state: Default::default(),
428                    cache: Default::default(),
429
430                    #[cfg($cache_on_disk)]
431                    will_cache_on_disk_for_key_fn:
432                        Some(rustc_middle::queries::_cache_on_disk_if_fns::$name),
433                    #[cfg(not($cache_on_disk))]
434                    will_cache_on_disk_for_key_fn: None,
435
436                    call_query_method_fn: |tcx, key| {
437                        // Call the query method for its side-effect of loading a value
438                        // from disk-cache; the caller doesn't need the value.
439                        let _ = tcx.$name(key);
440                    },
441                    invoke_provider_fn: self::invoke_provider_fn::__rust_begin_short_backtrace,
442
443                    #[cfg($cache_on_disk)]
444                    try_load_from_disk_fn: Some(|tcx, key, prev_index, index| {
445                        // Check the `cache_on_disk_if` condition for this key.
446                        if !rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) {
447                            return None;
448                        }
449
450                        let value: queries::$name::ProvidedValue<'tcx> =
451                            $crate::plumbing::try_load_from_disk(tcx, prev_index, index)?;
452
453                        // Arena-alloc the value if appropriate, and erase it.
454                        Some(queries::$name::provided_to_erased(tcx, value))
455                    }),
456                    #[cfg(not($cache_on_disk))]
457                    try_load_from_disk_fn: None,
458
459                    #[cfg($cache_on_disk)]
460                    is_loadable_from_disk_fn: Some(|tcx, key, index| -> bool {
461                        rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) &&
462                            $crate::plumbing::loadable_from_disk(tcx, index)
463                    }),
464                    #[cfg(not($cache_on_disk))]
465                    is_loadable_from_disk_fn: None,
466
467                    value_from_cycle_error: |tcx, cycle, guar| {
468                        let result: queries::$name::Value<'tcx> =
469                            FromCycleError::from_cycle_error(tcx, cycle, guar);
470                        erase::erase_val(result)
471                    },
472
473                    #[cfg($no_hash)]
474                    hash_value_fn: None,
475                    #[cfg(not($no_hash))]
476                    hash_value_fn: Some(|hcx, erased_value: &erase::Erased<queries::$name::Value<'tcx>>| {
477                        let value = erase::restore_val(*erased_value);
478                        rustc_middle::dep_graph::hash_result(hcx, &value)
479                    }),
480
481                    format_value: |value| format!("{:?}", erase::restore_val::<queries::$name::Value<'tcx>>(*value)),
482                    description_fn: $crate::queries::_description_fns::$name,
483                    execute_query_fn: if incremental {
484                        query_impl::$name::execute_query_incr::__rust_end_short_backtrace
485                    } else {
486                        query_impl::$name::execute_query_non_incr::__rust_end_short_backtrace
487                    },
488                }
489            }
490
491            /// Marker type that implements [`GetQueryVTable`] for this query.
492            pub(crate) enum VTableGetter {}
493
494            impl<'tcx> GetQueryVTable<'tcx> for VTableGetter {
495                type Cache = rustc_middle::queries::$name::Cache<'tcx>;
496
497                #[inline(always)]
498                fn query_vtable(tcx: TyCtxt<'tcx>) -> &'tcx QueryVTable<'tcx, Self::Cache> {
499                    &tcx.query_system.query_vtables.$name
500                }
501            }
502        })*}
503
504        pub fn make_query_vtables<'tcx>(incremental: bool) -> queries::QueryVTables<'tcx> {
505            queries::QueryVTables {
506                $(
507                    $name: query_impl::$name::make_query_vtable(incremental),
508                )*
509            }
510        }
511
512        /// Returns a map of currently active query jobs, collected from all queries.
513        ///
514        /// If `require_complete` is `true`, this function locks all shards of the
515        /// query results to produce a complete map, which always returns `Ok`.
516        /// Otherwise, it may return an incomplete map as an error if any shard
517        /// lock cannot be acquired.
518        ///
519        /// Prefer passing `false` to `require_complete` to avoid potential deadlocks,
520        /// especially when called from within a deadlock handler, unless a
521        /// complete map is needed and no deadlock is possible at this call site.
522        pub fn collect_active_jobs_from_all_queries<'tcx>(
523            tcx: TyCtxt<'tcx>,
524            require_complete: bool,
525        ) -> Result<QueryJobMap<'tcx>, QueryJobMap<'tcx>> {
526            let mut job_map_out = QueryJobMap::default();
527            let mut complete = true;
528
529            $(
530                let res = crate::execution::gather_active_jobs(
531                    &tcx.query_system.query_vtables.$name,
532                    tcx,
533                    require_complete,
534                    &mut job_map_out,
535                );
536                if res.is_none() {
537                    complete = false;
538                }
539            )*
540
541            if complete { Ok(job_map_out) } else { Err(job_map_out) }
542        }
543
544        /// All self-profiling events generated by the query engine use
545        /// virtual `StringId`s for their `event_id`. This method makes all
546        /// those virtual `StringId`s point to actual strings.
547        ///
548        /// If we are recording only summary data, the ids will point to
549        /// just the query names. If we are recording query keys too, we
550        /// allocate the corresponding strings here.
551        pub fn alloc_self_profile_query_strings(tcx: TyCtxt<'_>) {
552            if !tcx.prof.enabled() {
553                return;
554            }
555
556            let _prof_timer = tcx.sess.prof.generic_activity("self_profile_alloc_query_strings");
557
558            let mut string_cache = QueryKeyStringCache::new();
559
560            $(
561                $crate::profiling_support::alloc_self_profile_query_strings_for_query_cache(
562                    tcx,
563                    stringify!($name),
564                    &tcx.query_system.query_vtables.$name.cache,
565                    &mut string_cache,
566                );
567            )*
568
569            tcx.sess.prof.store_query_cache_hits();
570        }
571
572        fn encode_all_query_results<'tcx>(
573            tcx: TyCtxt<'tcx>,
574            encoder: &mut CacheEncoder<'_, 'tcx>,
575            query_result_index: &mut EncodedDepNodeIndex,
576        ) {
577            $(
578                #[cfg($cache_on_disk)]
579                {
580                    $crate::plumbing::encode_query_results(
581                        tcx,
582                        &tcx.query_system.query_vtables.$name,
583                        encoder,
584                        query_result_index,
585                    )
586                }
587            )*
588        }
589
590        pub fn query_key_hash_verify_all<'tcx>(tcx: TyCtxt<'tcx>) {
591            if tcx.sess.opts.unstable_opts.incremental_verify_ich || cfg!(debug_assertions) {
592                tcx.sess.time("query_key_hash_verify_all", || {
593                    $(
594                        $crate::plumbing::query_key_hash_verify(
595                            &tcx.query_system.query_vtables.$name,
596                            tcx
597                        );
598                    )*
599                })
600            }
601        }
602    }
603}