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