1use std::hash::Hash;
2use std::mem::ManuallyDrop;
3use std::num::NonZero;
4
5use rustc_data_structures::hash_table::Entry;
6use rustc_data_structures::{defer, outline, sharded, sync};
7use rustc_errors::FatalError;
8use rustc_middle::dep_graph::{
9 DepGraphData, DepNode, DepNodeIndex, DepNodeKey, SerializedDepNodeIndex,
10};
11use rustc_middle::query::{
12 ActiveKeyStatus, QueryCache, QueryCycle, QueryJob, QueryJobId, QueryLatch, QueryMode,
13 QueryState, QueryVTable,
14};
15use rustc_middle::ty::TyCtxt;
16use rustc_middle::ty::tls::{self, ImplicitCtxt};
17use rustc_middle::verify_ich::incremental_verify_ich;
18use rustc_span::def_id::LOCAL_CRATE;
19use rustc_span::{DUMMY_SP, Span};
20use rustc_structures::Limit;
21
22use crate::diagnostics::{QueryOverflow, QueryOverflowNote};
23use crate::handle_cycle_error;
24use crate::incremental::should_verify_loaded_value;
25use crate::job::{
26 CollectActiveJobsKind, collect_active_query_jobs, find_cycle_in_stack, find_dep_kind_root,
27};
28
29#[inline]
30fn equivalent_key<K: Eq, V>(k: K) -> impl Fn(&(K, V)) -> bool {
31 move |x| x.0 == k
32}
33
34#[cold]
35#[inline(never)]
36fn handle_cycle<'tcx, C: QueryCache>(
37 query: &'tcx QueryVTable<'tcx, C>,
38 tcx: TyCtxt<'tcx>,
39 key: C::Key,
40 cycle: QueryCycle<'tcx>,
41) -> C::Value {
42 let nested;
43 {
44 let mut nesting = tcx.query_system.cycle_handler_nesting.lock();
45 nested = match *nesting {
46 0 => false,
47 1 => true,
48 _ => {
49 tcx.dcx().delayed_bug("doubly nested cycle error").raise_fatal()
51 }
52 };
53 *nesting += 1;
54 }
55 let _guard = defer(|| *tcx.query_system.cycle_handler_nesting.lock() -= 1);
56
57 let error = handle_cycle_error::create_cycle_error(tcx, &cycle, nested);
58
59 if nested {
60 handle_cycle_error::default(error)
62 } else {
63 (query.handle_cycle_error_fn)(tcx, key, cycle, error)
64 }
65}
66
67struct ActiveJobGuard<'tcx, K>
73where
74 K: Eq + Hash + Copy,
75{
76 state: &'tcx QueryState<'tcx, K>,
77 key: K,
78 key_hash: u64,
79}
80
81impl<'tcx, K> ActiveJobGuard<'tcx, K>
82where
83 K: Eq + Hash + Copy,
84{
85 fn complete<C>(self, cache: &C, value: C::Value, dep_node_index: DepNodeIndex)
88 where
89 C: QueryCache<Key = K>,
90 {
91 cache.complete(self.key, value, dep_node_index);
94
95 let mut this = ManuallyDrop::new(self);
96
97 this.drop_and_maybe_poison(false);
99 }
100
101 fn drop_and_maybe_poison(&mut self, poison: bool) {
102 let status = {
103 let mut shard = self.state.active.lock_shard_by_hash(self.key_hash);
104 match shard.find_entry(self.key_hash, equivalent_key(self.key)) {
105 Err(_) => {
106 drop(shard);
109 ::core::panicking::panic("explicit panic");panic!();
110 }
111 Ok(occupied) => {
112 let ((key, status), vacant) = occupied.remove();
113 if poison {
114 vacant.insert((key, ActiveKeyStatus::Poisoned));
115 }
116 status
117 }
118 }
119 };
120
121 match status {
123 ActiveKeyStatus::Started(job) => job.signal_complete(),
124 ActiveKeyStatus::Poisoned => ::core::panicking::panic("explicit panic")panic!(),
125 }
126 }
127}
128
129impl<'tcx, K> Drop for ActiveJobGuard<'tcx, K>
130where
131 K: Eq + Hash + Copy,
132{
133 #[inline(never)]
134 #[cold]
135 fn drop(&mut self) {
136 self.drop_and_maybe_poison(true);
138 }
139}
140
141#[cold]
142#[inline(never)]
143fn find_and_handle_cycle<'tcx, C: QueryCache>(
144 query: &'tcx QueryVTable<'tcx, C>,
145 tcx: TyCtxt<'tcx>,
146 key: C::Key,
147 try_execute: QueryJobId,
148 span: Span,
149) -> (C::Value, Option<DepNodeIndex>) {
150 let job_map = collect_active_query_jobs(tcx, CollectActiveJobsKind::FullNoContention);
153
154 let cycle = find_cycle_in_stack(try_execute, job_map, ¤t_query_job(), span);
155 (handle_cycle(query, tcx, key, cycle), None)
156}
157
158#[inline(always)]
159fn wait_for_query<'tcx, C: QueryCache>(
160 query: &'tcx QueryVTable<'tcx, C>,
161 tcx: TyCtxt<'tcx>,
162 span: Span,
163 key: C::Key,
164 key_hash: u64,
165 latch: QueryLatch<'tcx>,
166 current: Option<QueryJobId>,
167) -> (C::Value, Option<DepNodeIndex>) {
168 let query_blocked_prof_timer = tcx.prof.query_blocked();
172
173 let result = latch.wait_on(current, span);
175
176 match result {
177 Ok(()) => {
178 let Some((v, index)) = query.cache.lookup(&key) else {
179 outline(|| {
180 let shard = query.state.active.lock_shard_by_hash(key_hash);
183 match shard.find(key_hash, equivalent_key(key)) {
184 Some((_, ActiveKeyStatus::Poisoned)) => FatalError.raise(),
186 _ => {
::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!(
187 "query '{}' result must be in the cache or the query must be poisoned after a wait",
188 query.name
189 ),
190 }
191 })
192 };
193
194 tcx.prof.query_cache_hit(index.into());
195 query_blocked_prof_timer.finish_with_query_invocation_id(index.into());
196
197 (v, Some(index))
198 }
199 Err(cycle) => (handle_cycle(query, tcx, key, cycle), None),
200 }
201}
202
203#[inline]
204fn next_job_id<'tcx>(tcx: TyCtxt<'tcx>) -> QueryJobId {
205 QueryJobId(
206 NonZero::new(tcx.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed))
207 .unwrap(),
208 )
209}
210
211#[inline]
212fn current_query_job() -> Option<QueryJobId> {
213 tls::with_context(|icx| icx.query)
214}
215
216#[inline(never)]
218fn try_execute_query<'tcx, C: QueryCache, const INCR: bool>(
219 query: &'tcx QueryVTable<'tcx, C>,
220 tcx: TyCtxt<'tcx>,
221 span: Span,
222 key: C::Key,
223 dep_node: Option<DepNode>, ) -> (C::Value, Option<DepNodeIndex>) {
225 let key_hash = sharded::make_hash(&key);
226 let mut state_lock = query.state.active.lock_shard_by_hash(key_hash);
227
228 if tcx.sess.opts.jobs.frontend.is_some() {
235 if let Some((value, index)) = query.cache.lookup(&key) {
236 tcx.prof.query_cache_hit(index.into());
237 return (value, Some(index));
238 }
239 }
240
241 let current_job_id = current_query_job();
242
243 match state_lock.entry(key_hash, equivalent_key(key), |(k, _)| sharded::make_hash(k)) {
244 Entry::Vacant(entry) => {
245 let id = next_job_id(tcx);
248 let job = QueryJob::new(id, span, current_job_id);
249 entry.insert((key, ActiveKeyStatus::Started(job)));
250
251 drop(state_lock);
253
254 let job_guard = ActiveJobGuard { state: &query.state, key, key_hash };
257
258 let (value, dep_node_index) = if INCR {
260 execute_job_incr(query, tcx, key, dep_node.unwrap(), id)
261 } else {
262 execute_job_non_incr(query, tcx, key, id)
263 };
264
265 if query.feedable {
266 check_feedable_consistency(tcx, query, key, &value);
267 }
268
269 job_guard.complete(&query.cache, value, dep_node_index);
272
273 (value, Some(dep_node_index))
274 }
275 Entry::Occupied(mut entry) => {
276 match &mut entry.get_mut().1 {
277 ActiveKeyStatus::Started(job) => {
278 if sync::is_dyn_thread_safe() {
279 let latch = job.latch();
281 drop(state_lock);
282
283 wait_for_query(query, tcx, span, key, key_hash, latch, current_job_id)
286 } else {
287 let id = job.id;
288 drop(state_lock);
289
290 find_and_handle_cycle(query, tcx, key, id, span)
293 }
294 }
295 ActiveKeyStatus::Poisoned => FatalError.raise(),
296 }
297 }
298 }
299}
300
301#[inline(always)]
302fn check_feedable_consistency<'tcx, C: QueryCache>(
303 tcx: TyCtxt<'tcx>,
304 query: &'tcx QueryVTable<'tcx, C>,
305 key: C::Key,
306 value: &C::Value,
307) {
308 let Some((cached_value, _)) = query.cache.lookup(&key) else { return };
313
314 let Some(hash_value_fn) = query.hash_value_fn else {
315 {
::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!(
316 "no_hash fed query later has its value computed.\n\
317 Remove `no_hash` modifier to allow recomputation.\n\
318 The already cached value: {}",
319 (query.format_value)(&cached_value)
320 );
321 };
322
323 let (old_hash, new_hash) = tcx.with_stable_hashing_context(|mut hcx| {
324 (hash_value_fn(&mut hcx, &cached_value), hash_value_fn(&mut hcx, value))
325 });
326 let formatter = query.format_value;
327 if old_hash != new_hash {
328 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!(
331 tcx.dcx().has_errors().is_some(),
332 "Computed query value for {:?}({:?}) is inconsistent with fed value,\n\
333 computed={:#?}\nfed={:#?}",
334 query.dep_kind,
335 key,
336 formatter(value),
337 formatter(&cached_value),
338 );
339 }
340}
341
342fn depth_limit_error<'tcx>(tcx: TyCtxt<'tcx>, job: QueryJobId) {
343 let job_map = collect_active_query_jobs(tcx, CollectActiveJobsKind::Full);
344 let (span, desc, depth) = find_dep_kind_root(tcx, job, job_map);
345
346 let suggested_limit = match tcx.recursion_limit() {
347 Limit(0) => Limit(2),
348 limit => limit * 2,
349 };
350
351 tcx.dcx().emit_fatal(QueryOverflow {
352 span,
353 note: QueryOverflowNote { desc, depth },
354 suggested_limit,
355 crate_name: tcx.crate_name(LOCAL_CRATE),
356 });
357}
358
359#[inline(always)]
361fn start_query<R>(job_id: QueryJobId, depth_limit: bool, compute: impl FnOnce() -> R) -> R {
362 tls::with_context(move |icx| {
363 if depth_limit && !icx.tcx.recursion_limit().value_within_limit(icx.query_depth) {
364 depth_limit_error(icx.tcx, job_id);
365 }
366
367 let icx = ImplicitCtxt {
369 query: Some(job_id),
370 query_depth: icx.query_depth + if depth_limit { 1 } else { 0 },
371 ..*icx
372 };
373
374 tls::enter_context(&icx, compute)
376 })
377}
378
379#[inline(always)]
381fn execute_job_non_incr<'tcx, C: QueryCache>(
382 query: &'tcx QueryVTable<'tcx, C>,
383 tcx: TyCtxt<'tcx>,
384 key: C::Key,
385 job_id: QueryJobId,
386) -> (C::Value, DepNodeIndex) {
387 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());
388
389 let prof_timer = tcx.prof.query_provider();
390 let value = start_query(job_id, query.depth_limit, || (query.invoke_provider_fn)(tcx, key));
392 let dep_node_index = tcx.dep_graph.next_virtual_depnode_index();
393 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
394
395 if truecfg!(debug_assertions) {
397 let _ = key.to_fingerprint(tcx);
398 if let Some(hash_value_fn) = query.hash_value_fn {
399 tcx.with_stable_hashing_context(|mut hcx| {
400 hash_value_fn(&mut hcx, &value);
401 });
402 }
403 }
404
405 (value, dep_node_index)
406}
407
408#[inline(always)]
409fn execute_job_incr<'tcx, C: QueryCache>(
410 query: &'tcx QueryVTable<'tcx, C>,
411 tcx: TyCtxt<'tcx>,
412 key: C::Key,
413 dep_node: DepNode,
414 job_id: QueryJobId,
415) -> (C::Value, DepNodeIndex) {
416 let dep_graph_data =
417 tcx.dep_graph.data().expect("should always be present in incremental mode");
418
419 if !query.eval_always {
420 if let Some(ret) = start_query(job_id, false, || try {
423 let (prev_index, dep_node_index) = dep_graph_data.try_mark_green(tcx, &dep_node)?;
424 let value = load_from_disk_or_invoke_provider_green(
425 tcx,
426 dep_graph_data,
427 query,
428 key,
429 &dep_node,
430 prev_index,
431 dep_node_index,
432 );
433 (value, dep_node_index)
434 }) {
435 return ret;
436 }
437 }
438
439 let prof_timer = tcx.prof.query_provider();
440
441 let (result, dep_node_index) = start_query(job_id, query.depth_limit, || {
442 dep_graph_data.with_task(
444 dep_node,
445 tcx,
446 || (query.invoke_provider_fn)(tcx, key),
447 query.hash_value_fn,
448 )
449 });
450
451 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
452
453 (result, dep_node_index)
454}
455
456#[inline(always)]
460fn load_from_disk_or_invoke_provider_green<'tcx, C: QueryCache>(
461 tcx: TyCtxt<'tcx>,
462 dep_graph_data: &DepGraphData,
463 query: &'tcx QueryVTable<'tcx, C>,
464 key: C::Key,
465 dep_node: &DepNode,
466 prev_index: SerializedDepNodeIndex,
467 dep_node_index: DepNodeIndex,
468) -> C::Value {
469 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));
473
474 let try_value = if query.will_cache_on_disk_for_key(key) {
476 let prof_timer = tcx.prof.incr_cache_loading();
477 let value = (query.try_load_from_disk_fn)(tcx, prev_index);
478 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
479 value
480 } else {
481 None
482 };
483 let (value, verify) = match try_value {
484 Some(value) => {
485 if std::intrinsics::unlikely(tcx.sess.opts.unstable_opts.query_dep_graph) {
486 dep_graph_data.mark_debug_loaded_from_disk(*dep_node)
487 }
488
489 let verify = should_verify_loaded_value(tcx, dep_graph_data, dep_node.key_fingerprint);
490
491 (value, verify)
492 }
493 None => {
494 let prof_timer = tcx.prof.query_provider();
497 let value = tcx.dep_graph.with_ignore(|| (query.invoke_provider_fn)(tcx, key));
498 prof_timer.finish_with_query_invocation_id(dep_node_index.into());
499
500 (value, true)
501 }
502 };
503
504 if verify {
505 incremental_verify_ich(
515 tcx,
516 dep_graph_data,
517 &value,
518 prev_index,
519 query.hash_value_fn,
520 query.format_value,
521 );
522 }
523
524 value
525}
526
527#[inline(never)]
534fn ensure_can_skip_execution<'tcx, C: QueryCache>(
535 query: &'tcx QueryVTable<'tcx, C>,
536 tcx: TyCtxt<'tcx>,
537 dep_node: DepNode,
538) -> bool {
539 if query.eval_always {
541 return false;
542 }
543
544 match tcx.dep_graph.try_mark_green(tcx, &dep_node) {
545 None => {
546 false
553 }
554 Some((_, dep_node_index)) => {
555 tcx.dep_graph.read_index(dep_node_index);
556 tcx.prof.query_cache_hit(dep_node_index.into());
557
558 true
563 }
564 }
565}
566
567#[inline(always)]
570pub(super) fn execute_query_non_incr_inner<'tcx, C: QueryCache>(
571 query: &'tcx QueryVTable<'tcx, C>,
572 tcx: TyCtxt<'tcx>,
573 span: Span,
574 key: C::Key,
575) -> C::Value {
576 try_execute_query::<C, false>(query, tcx, span, key, None).0
577}
578
579#[inline(always)]
582pub(super) fn execute_query_incr_inner<'tcx, C: QueryCache>(
583 query: &'tcx QueryVTable<'tcx, C>,
584 tcx: TyCtxt<'tcx>,
585 span: Span,
586 key: C::Key,
587 mode: QueryMode,
588) -> Option<C::Value> {
589 let dep_node = DepNode::construct(tcx, query.dep_kind, &key);
590
591 if let QueryMode::EnsureOk = mode
593 && ensure_can_skip_execution(query, tcx, dep_node)
594 {
595 return None;
596 }
597
598 let (result, dep_node_index) =
599 try_execute_query::<C, true>(query, tcx, span, key, Some(dep_node));
600 if let Some(dep_node_index) = dep_node_index {
601 tcx.dep_graph.read_index(dep_node_index)
602 }
603 Some(result)
604}
605
606pub(crate) fn force_query_dep_node<'tcx, C: QueryCache>(
611 tcx: TyCtxt<'tcx>,
612 query: &'tcx QueryVTable<'tcx, C>,
613 dep_node: DepNode,
614) -> bool {
615 let Some(key) = C::Key::try_recover_key(tcx, &dep_node) else {
616 return false;
619 };
620
621 try_execute_query::<C, true>(query, tcx, DUMMY_SP, key, Some(dep_node));
622
623 true
626}