1use std::hash::Hash;
2use std::mem::ManuallyDrop;
3use std::num::NonZero;
4use std::sync::Arc;
5
6use parking_lot::{Condvar, Mutex};
7use rustc_data_structures::hash_table::Entry;
8use rustc_data_structures::{defer, outline, sharded, sync};
9use rustc_errors::FatalError;
10use rustc_middle::dep_graph::{
11 DepGraphData, DepNode, DepNodeIndex, DepNodeKey, SerializedDepNodeIndex,
12};
13use rustc_middle::query::{
14 ActiveKeyStatus, QueryCache, QueryCycle, QueryJob, QueryJobId, QueryLatch, QueryMode,
15 QueryState, QueryVTable, QueryWaiter,
16};
17use rustc_middle::ty::TyCtxt;
18use rustc_middle::ty::tls::{self, ImplicitCtxt};
19use rustc_middle::verify_ich::incremental_verify_ich;
20use rustc_span::def_id::LOCAL_CRATE;
21use rustc_span::{DUMMY_SP, Span};
22use rustc_structures::Limit;
23
24use crate::diagnostics::{QueryOverflow, QueryOverflowNote};
25use crate::handle_cycle_error;
26use crate::incremental::should_verify_loaded_value;
27use crate::job::{
28 CollectActiveJobsKind, collect_active_query_jobs, find_cycle_in_stack, find_dep_kind_root,
29};
30
31#[inline]
32fn equivalent_key<K: Eq, V>(k: K) -> impl Fn(&(K, V)) -> bool {
33 move |x| x.0 == k
34}
35
36#[cold]
37#[inline(never)]
38fn handle_cycle<'tcx, C: QueryCache>(
39 query: &'tcx QueryVTable<'tcx, C>,
40 tcx: TyCtxt<'tcx>,
41 key: C::Key,
42 cycle: QueryCycle<'tcx>,
43) -> C::Value {
44 let nested;
45 {
46 let mut nesting = tcx.query_system.cycle_handler_nesting.lock();
47 nested = match *nesting {
48 0 => false,
49 1 => true,
50 _ => {
51 tcx.dcx().delayed_bug("doubly nested cycle error").raise_fatal()
53 }
54 };
55 *nesting += 1;
56 }
57 let _guard = defer(|| *tcx.query_system.cycle_handler_nesting.lock() -= 1);
58
59 let error = handle_cycle_error::create_cycle_error(tcx, &cycle, nested);
60
61 if nested {
62 handle_cycle_error::default(error)
64 } else {
65 (query.handle_cycle_error_fn)(tcx, key, cycle, error)
66 }
67}
68
69#[inline]
74fn signal_complete(job: QueryJob<'_>) {
75 if let Some(latch) = job.latch {
76 let mut waiters_guard = latch.waiters.lock();
78 let waiters = waiters_guard.take().unwrap(); let registry = rustc_thread_pool::Registry::current();
80 for waiter in waiters {
81 rustc_thread_pool::mark_unblocked(®istry);
82 waiter.condvar.notify_one();
83 }
84 }
85}
86
87struct ActiveJobGuard<'tcx, K>
93where
94 K: Eq + Hash + Copy,
95{
96 state: &'tcx QueryState<'tcx, K>,
97 key: K,
98 key_hash: u64,
99}
100
101impl<'tcx, K> ActiveJobGuard<'tcx, K>
102where
103 K: Eq + Hash + Copy,
104{
105 fn complete<C>(self, cache: &C, value: C::Value, dep_node_index: DepNodeIndex)
108 where
109 C: QueryCache<Key = K>,
110 {
111 cache.complete(self.key, value, dep_node_index);
114
115 let mut this = ManuallyDrop::new(self);
116
117 this.drop_and_maybe_poison(false);
119 }
120
121 fn drop_and_maybe_poison(&mut self, poison: bool) {
122 let status = {
123 let mut shard = self.state.active.lock_shard_by_hash(self.key_hash);
124 match shard.find_entry(self.key_hash, equivalent_key(self.key)) {
125 Err(_) => {
126 drop(shard);
129 ::core::panicking::panic("explicit panic");panic!();
130 }
131 Ok(occupied) => {
132 let ((key, status), vacant) = occupied.remove();
133 if poison {
134 vacant.insert((key, ActiveKeyStatus::Poisoned));
135 }
136 status
137 }
138 }
139 };
140
141 match status {
143 ActiveKeyStatus::Started(job) => signal_complete(job),
144 ActiveKeyStatus::Poisoned => ::core::panicking::panic("explicit panic")panic!(),
145 }
146 }
147}
148
149impl<'tcx, K> Drop for ActiveJobGuard<'tcx, K>
150where
151 K: Eq + Hash + Copy,
152{
153 #[inline(never)]
154 #[cold]
155 fn drop(&mut self) {
156 self.drop_and_maybe_poison(true);
158 }
159}
160
161#[cold]
162#[inline(never)]
163fn find_and_handle_cycle<'tcx, C: QueryCache>(
164 query: &'tcx QueryVTable<'tcx, C>,
165 tcx: TyCtxt<'tcx>,
166 key: C::Key,
167 try_execute: QueryJobId,
168 span: Span,
169) -> (C::Value, Option<DepNodeIndex>) {
170 let job_map = collect_active_query_jobs(tcx, CollectActiveJobsKind::FullNoContention);
173
174 let cycle = find_cycle_in_stack(try_execute, job_map, ¤t_query_job(), span);
175 (handle_cycle(query, tcx, key, cycle), None)
176}
177
178fn latch_wait_on<'tcx>(
180 latch: &QueryLatch<'tcx>,
181 query: Option<QueryJobId>,
182 span: Span,
183) -> Result<(), QueryCycle<'tcx>> {
184 let mut waiters_guard = latch.waiters.lock();
185 let Some(waiters) = &mut *waiters_guard else {
186 return Ok(()); };
188
189 let waiter = Arc::new(QueryWaiter {
190 parent: query,
191 span,
192 cycle: Mutex::new(None),
193 condvar: Condvar::new(),
194 });
195
196 waiters.push(Arc::clone(&waiter));
200
201 rustc_thread_pool::mark_blocked_and_wait(|| {
205 waiter.condvar.wait(&mut waiters_guard);
206 drop(waiters_guard);
208 });
209
210 let mut cycle = waiter.cycle.lock();
213 match cycle.take() {
214 None => Ok(()),
215 Some(cycle) => Err(cycle),
216 }
217}
218
219#[inline(always)]
220fn wait_for_query<'tcx, C: QueryCache>(
221 query: &'tcx QueryVTable<'tcx, C>,
222 tcx: TyCtxt<'tcx>,
223 span: Span,
224 key: C::Key,
225 key_hash: u64,
226 latch: QueryLatch<'tcx>,
227 current: Option<QueryJobId>,
228) -> (C::Value, Option<DepNodeIndex>) {
229 let query_blocked_prof_timer = tcx.prof.query_blocked();
233
234 let result = latch_wait_on(&latch, current, span);
236
237 match result {
238 Ok(()) => {
239 let Some((v, index)) = query.cache.lookup(&key) else {
240 outline(|| {
241 let shard = query.state.active.lock_shard_by_hash(key_hash);
244 match shard.find(key_hash, equivalent_key(key)) {
245 Some((_, ActiveKeyStatus::Poisoned)) => FatalError.raise(),
247 _ => {
::core::panicking::panic_fmt(format_args!("query \'{0}\' result must be in the cache or the query must be poisoned after a wait",
query.name));
}panic!(
248 "query '{}' result must be in the cache or the query must be poisoned after a wait",
249 query.name
250 ),
251 }
252 })
253 };
254
255 tcx.prof.query_cache_hit(index.into());
256 query_blocked_prof_timer.finish_with_query_invocation_id(index.into());
257
258 (v, Some(index))
259 }
260 Err(cycle) => (handle_cycle(query, tcx, key, cycle), None),
261 }
262}
263
264#[inline]
265fn next_job_id<'tcx>(tcx: TyCtxt<'tcx>) -> QueryJobId {
266 QueryJobId(
267 NonZero::new(tcx.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
268 .unwrap(),
269 )
270}
271
272#[inline]
273fn current_query_job() -> Option<QueryJobId> {
274 tls::with_context(|icx| icx.query)
275}
276
277#[inline(never)]
279fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>(
280 query: &'tcx QueryVTable<'tcx, C>,
281 tcx: TyCtxt<'tcx>,
282 span: Span,
283 key: C::Key,
284 dep_node: Option<DepNode>, ) -> (C::Value, Option<DepNodeIndex>) {
286 let key_hash = sharded::make_hash(&key);
287 let mut state_lock = query.state.active.lock_shard_by_hash(key_hash);
288
289 if tcx.sess.opts.jobs.frontend.is_some() {
296 if let Some((value, index)) = query.cache.lookup(&key) {
297 tcx.prof.query_cache_hit(index.into());
298 return (value, Some(index));
299 }
300 }
301
302 let current_job_id = current_query_job();
303
304 match state_lock.entry(key_hash, equivalent_key(key), |(k, _)| sharded::make_hash(k)) {
305 Entry::Vacant(entry) => {
306 let id = next_job_id(tcx);
309 let job = QueryJob::new(id, span, current_job_id);
310 entry.insert((key, ActiveKeyStatus::Started(job)));
311
312 drop(state_lock);
314
315 let job_guard = ActiveJobGuard { state: &query.state, key, key_hash };
318
319 let (value, dep_node_index) = if INCR {
321 execute_job_incr(query, tcx, key, dep_node.unwrap(), id)
322 } else {
323 execute_job_non_incr(query, tcx, key, id)
324 };
325
326 if query.feedable {
327 check_feedable_consistency(tcx, query, key, &value);
328 }
329
330 job_guard.complete(&query.cache, value, dep_node_index);
333
334 (value, Some(dep_node_index))
335 }
336 Entry::Occupied(mut entry) => {
337 match &mut entry.get_mut().1 {
338 ActiveKeyStatus::Started(job) => {
339 if sync::is_dyn_thread_safe() {
340 let latch = job.latch.get_or_insert_with(QueryLatch::new).clone();
342 drop(state_lock);
343
344 wait_for_query(query, tcx, span, key, key_hash, latch, current_job_id)
347 } else {
348 let id = job.id;
349 drop(state_lock);
350
351 find_and_handle_cycle(query, tcx, key, id, span)
354 }
355 }
356 ActiveKeyStatus::Poisoned => FatalError.raise(),
357 }
358 }
359 }
360}
361
362#[inline(always)]
363fn check_feedable_consistency<'tcx, C: QueryCache>(
364 tcx: TyCtxt<'tcx>,
365 query: &'tcx QueryVTable<'tcx, C>,
366 key: C::Key,
367 value: &C::Value,
368) {
369 let Some((cached_value, _)) = query.cache.lookup(&key) else { return };
374
375 let Some(hash_value_fn) = query.hash_value_fn else {
376 {
::core::panicking::panic_fmt(format_args!("no_hash fed query later has its value computed.\nRemove `no_hash` modifier to allow recomputation.\nThe already cached value: {0}",
(query.format_value)(&cached_value)));
};panic!(
377 "no_hash fed query later has its value computed.\n\
378 Remove `no_hash` modifier to allow recomputation.\n\
379 The already cached value: {}",
380 (query.format_value)(&cached_value)
381 );
382 };
383
384 let (old_hash, new_hash) = tcx.with_stable_hashing_context(|mut hcx| {
385 (hash_value_fn(&mut hcx, &cached_value), hash_value_fn(&mut hcx, value))
386 });
387 let formatter = query.format_value;
388 if old_hash != new_hash {
389 if !tcx.dcx().has_errors().is_some() {
{
::core::panicking::panic_fmt(format_args!("Computed query value for {0:?}({1:?}) is inconsistent with fed value,\ncomputed={2:#?}\nfed={3:#?}",
query.dep_kind, key, formatter(value),
formatter(&cached_value)));
}
};assert!(
392 tcx.dcx().has_errors().is_some(),
393 "Computed query value for {:?}({:?}) is inconsistent with fed value,\n\
394 computed={:#?}\nfed={:#?}",
395 query.dep_kind,
396 key,
397 formatter(value),
398 formatter(&cached_value),
399 );
400 }
401}
402
403fn depth_limit_error<'tcx>(tcx: TyCtxt<'tcx>, job: QueryJobId) {
404 let job_map = collect_active_query_jobs(tcx, CollectActiveJobsKind::Full);
405 let (span, desc, depth) = find_dep_kind_root(tcx, job, job_map);
406
407 let suggested_limit = match tcx.recursion_limit() {
408 Limit(0) => Limit(2),
409 limit => limit * 2,
410 };
411
412 tcx.dcx().emit_fatal(QueryOverflow {
413 span,
414 note: QueryOverflowNote { desc, depth },
415 suggested_limit,
416 crate_name: tcx.crate_name(LOCAL_CRATE),
417 });
418}
419
420#[inline(always)]
422fn start_query<R>(job_id: QueryJobId, depth_limit: bool, compute: impl FnOnce() -> R) -> R {
423 tls::with_context(move |icx| {
424 if depth_limit && !icx.tcx.recursion_limit().value_within_limit(icx.query_depth) {
425 depth_limit_error(icx.tcx, job_id);
426 }
427
428 let icx = ImplicitCtxt {
430 query: Some(job_id),
431 query_depth: icx.query_depth + if depth_limit { 1 } else { 0 },
432 ..*icx
433 };
434
435 tls::enter_context(&icx, compute)
437 })
438}
439
440#[inline(always)]
442fn execute_job_non_incr<'tcx, C: QueryCache>(
443 query: &'tcx QueryVTable<'tcx, C>,
444 tcx: TyCtxt<'tcx>,
445 key: C::Key,
446 job_id: QueryJobId,
447) -> (C::Value, DepNodeIndex) {
448 if true {
if !!tcx.dep_graph.is_fully_enabled() {
::core::panicking::panic("assertion failed: !tcx.dep_graph.is_fully_enabled()")
};
};debug_assert!(!tcx.dep_graph.is_fully_enabled());
449
450 let prof_timer = tcx.prof.query_provider();
451 let value = start_query(job_id, query.depth_limit, || (query.invoke_provider_fn)(tcx, key));
453 let dep_node_index = tcx.dep_graph.next_virtual_depnode_index();
454 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
455
456 if truecfg!(debug_assertions) {
458 let _ = key.to_fingerprint(tcx);
459 if let Some(hash_value_fn) = query.hash_value_fn {
460 tcx.with_stable_hashing_context(|mut hcx| {
461 hash_value_fn(&mut hcx, &value);
462 });
463 }
464 }
465
466 (value, dep_node_index)
467}
468
469#[inline(always)]
470fn execute_job_incr<'tcx, C: QueryCache>(
471 query: &'tcx QueryVTable<'tcx, C>,
472 tcx: TyCtxt<'tcx>,
473 key: C::Key,
474 dep_node: DepNode,
475 job_id: QueryJobId,
476) -> (C::Value, DepNodeIndex) {
477 let dep_graph_data =
478 tcx.dep_graph.data().expect("should always be present in incremental mode");
479
480 if !query.eval_always {
481 if let Some(ret) = start_query(job_id, false, || try {
484 let (prev_index, dep_node_index) = dep_graph_data.try_mark_green(tcx, &dep_node)?;
485 let value = load_from_disk_or_invoke_provider_green(
486 tcx,
487 dep_graph_data,
488 query,
489 key,
490 &dep_node,
491 prev_index,
492 dep_node_index,
493 );
494 (value, dep_node_index)
495 }) {
496 return ret;
497 }
498 }
499
500 let prof_timer = tcx.prof.query_provider();
501
502 let (result, dep_node_index) = start_query(job_id, query.depth_limit, || {
503 dep_graph_data.with_task(
505 dep_node,
506 tcx,
507 || (query.invoke_provider_fn)(tcx, key),
508 query.hash_value_fn,
509 )
510 });
511
512 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
513
514 (result, dep_node_index)
515}
516
517#[inline(always)]
521fn load_from_disk_or_invoke_provider_green<'tcx, C: QueryCache>(
522 tcx: TyCtxt<'tcx>,
523 dep_graph_data: &DepGraphData,
524 query: &'tcx QueryVTable<'tcx, C>,
525 key: C::Key,
526 dep_node: &DepNode,
527 prev_index: SerializedDepNodeIndex,
528 dep_node_index: DepNodeIndex,
529) -> C::Value {
530 if true {
if !dep_graph_data.is_index_green(prev_index) {
::core::panicking::panic("assertion failed: dep_graph_data.is_index_green(prev_index)")
};
};debug_assert!(dep_graph_data.is_index_green(prev_index));
534
535 let try_value = if query.will_cache_on_disk_for_key(key) {
537 let prof_timer = tcx.prof.incr_cache_loading();
538 let value = (query.try_load_from_disk_fn)(tcx, prev_index);
539 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
540 value
541 } else {
542 None
543 };
544 let (value, verify) = match try_value {
545 Some(value) => {
546 if std::intrinsics::unlikely(tcx.sess.opts.unstable_opts.query_dep_graph) {
547 dep_graph_data.mark_debug_loaded_from_disk(*dep_node)
548 }
549
550 let verify = should_verify_loaded_value(tcx, dep_graph_data, dep_node.key_fingerprint);
551
552 (value, verify)
553 }
554 None => {
555 let prof_timer = tcx.prof.query_provider();
558 let value = tcx.dep_graph.with_ignore(|| (query.invoke_provider_fn)(tcx, key));
559 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
560
561 (value, true)
562 }
563 };
564
565 if verify {
566 incremental_verify_ich(
576 tcx,
577 dep_graph_data,
578 &value,
579 prev_index,
580 query.hash_value_fn,
581 query.format_value,
582 );
583 }
584
585 value
586}
587
588#[inline(never)]
595fn ensure_can_skip_execution<'tcx, C: QueryCache>(
596 query: &'tcx QueryVTable<'tcx, C>,
597 tcx: TyCtxt<'tcx>,
598 dep_node: DepNode,
599) -> bool {
600 if query.eval_always {
602 return false;
603 }
604
605 match tcx.dep_graph.try_mark_green(tcx, &dep_node) {
606 None => {
607 false
614 }
615 Some((_, dep_node_index)) => {
616 tcx.dep_graph.read_index(dep_node_index);
617 tcx.prof.query_cache_hit(dep_node_index.into());
618
619 true
624 }
625 }
626}
627
628#[inline(always)]
631pub(super) fn execute_query_non_incr_inner<'tcx, C: QueryCache>(
632 query: &'tcx QueryVTable<'tcx, C>,
633 tcx: TyCtxt<'tcx>,
634 span: Span,
635 key: C::Key,
636) -> C::Value {
637 try_execute_query::<C, false>(query, tcx, span, key, None).0
638}
639
640#[inline(always)]
643pub(super) fn execute_query_incr_inner<'tcx, C: QueryCache>(
644 query: &'tcx QueryVTable<'tcx, C>,
645 tcx: TyCtxt<'tcx>,
646 span: Span,
647 key: C::Key,
648 mode: QueryMode,
649) -> Option<C::Value> {
650 let dep_node = DepNode::construct(tcx, query.dep_kind, &key);
651
652 if let QueryMode::EnsureOk = mode
654 && ensure_can_skip_execution(query, tcx, dep_node)
655 {
656 return None;
657 }
658
659 let (result, dep_node_index) =
660 try_execute_query::<C, true>(query, tcx, span, key, Some(dep_node));
661 if let Some(dep_node_index) = dep_node_index {
662 tcx.dep_graph.read_index(dep_node_index)
663 }
664 Some(result)
665}
666
667pub(crate) fn force_query_dep_node<'tcx, C: QueryCache>(
672 tcx: TyCtxt<'tcx>,
673 query: &'tcx QueryVTable<'tcx, C>,
674 dep_node: DepNode,
675) -> bool {
676 let Some(key) = C::Key::try_recover_key(tcx, &dep_node) else {
677 return false;
680 };
681
682 try_execute_query::<C, true>(query, tcx, DUMMY_SP, key, Some(dep_node));
683
684 true
687}