1use 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#[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 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 tls::enter_context(&icx, compute)
90 })
91}
92
93fn 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 let reduce_queries = with_reduced_queries();
106
107 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 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 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 query_result_index.push((dep_node, AbsoluteBytePos::new(encoder.position())));
167
168 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
201pub(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 !(query.will_cache_on_disk_for_key_fn)(tcx, key) {
219 return;
220 }
221
222 match query.cache.lookup(&key) {
223 Some(_) => {}
225
226 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 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
271pub(crate) fn force_from_dep_node_inner<'tcx, Q: GetQueryVTable<'tcx>>(
273 tcx: TyCtxt<'tcx>,
274 dep_node: DepNode,
275 _prev_index: SerializedDepNodeIndex,
277) -> bool {
278 let query = Q::query_vtable(tcx);
279
280 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 queries {
311 $(
312 $(#[$attr:meta])*
313 fn $name:ident($K:ty) -> $V:ty
314 {
315 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 { $($_: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 pub(crate) mod execute_query_incr {
342 use super::*;
343
344 #[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 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 #[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 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 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 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 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 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 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}