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.
5 use crate::dep_graph
::{DepContext, DepKind, DepNode, DepNodeParams}
;
6 use crate::dep_graph
::{DepNodeIndex, SerializedDepNodeIndex}
;
7 use crate::query
::caches
::QueryCache
;
8 use crate::query
::config
::{QueryDescription, QueryVtable, QueryVtableExt}
;
9 use crate::query
::job
::{
10 report_cycle
, QueryInfo
, QueryJob
, QueryJobId
, QueryJobInfo
, QueryShardJobId
,
12 use crate::query
::{QueryContext, QueryMap, QueryStackFrame}
;
14 use rustc_data_structures
::fingerprint
::Fingerprint
;
15 use rustc_data_structures
::fx
::{FxHashMap, FxHasher}
;
16 use rustc_data_structures
::sharded
::{get_shard_index_by_hash, Sharded}
;
17 use rustc_data_structures
::sync
::{Lock, LockGuard}
;
18 use rustc_data_structures
::thin_vec
::ThinVec
;
19 #[cfg(not(parallel_compiler))]
20 use rustc_errors
::DiagnosticBuilder
;
21 use rustc_errors
::{Diagnostic, FatalError}
;
22 use rustc_span
::{Span, DUMMY_SP}
;
23 use std
::collections
::hash_map
::Entry
;
25 use std
::hash
::{Hash, Hasher}
;
27 use std
::num
::NonZeroU32
;
29 #[cfg(debug_assertions)]
30 use std
::sync
::atomic
::{AtomicUsize, Ordering}
;
32 pub struct QueryCacheStore
<C
: QueryCache
> {
34 shards
: Sharded
<C
::Sharded
>,
35 #[cfg(debug_assertions)]
36 pub cache_hits
: AtomicUsize
,
39 impl<C
: QueryCache
+ Default
> Default
for QueryCacheStore
<C
> {
40 fn default() -> Self {
43 shards
: Default
::default(),
44 #[cfg(debug_assertions)]
45 cache_hits
: AtomicUsize
::new(0),
50 /// Values used when checking a query cache which can be reused on a cache-miss to execute the query.
51 pub struct QueryLookup
{
52 pub(super) key_hash
: u64,
56 // We compute the key's hash once and then use it for both the
57 // shard lookup and the hashmap lookup. This relies on the fact
58 // that both of them use `FxHasher`.
59 fn hash_for_shard
<K
: Hash
>(key
: &K
) -> u64 {
60 let mut hasher
= FxHasher
::default();
61 key
.hash(&mut hasher
);
65 impl<C
: QueryCache
> QueryCacheStore
<C
> {
66 pub(super) fn get_lookup
<'tcx
>(
69 ) -> (QueryLookup
, LockGuard
<'tcx
, C
::Sharded
>) {
70 let key_hash
= hash_for_shard(key
);
71 let shard
= get_shard_index_by_hash(key_hash
);
72 let lock
= self.shards
.get_shard_by_index(shard
).lock();
73 (QueryLookup { key_hash, shard }
, lock
)
76 pub fn iter_results(&self, f
: &mut dyn FnMut(&C
::Key
, &C
::Value
, DepNodeIndex
)) {
77 self.cache
.iter(&self.shards
, f
)
81 struct QueryStateShard
<D
, K
> {
82 active
: FxHashMap
<K
, QueryResult
<D
>>,
84 /// Used to generate unique ids for active jobs.
88 impl<D
, K
> Default
for QueryStateShard
<D
, K
> {
89 fn default() -> QueryStateShard
<D
, K
> {
90 QueryStateShard { active: Default::default(), jobs: 0 }
94 pub struct QueryState
<D
, K
> {
95 shards
: Sharded
<QueryStateShard
<D
, K
>>,
98 /// Indicates the state of a query for a given key in a query map.
100 /// An already executing query. The query job can be used to await for its completion.
101 Started(QueryJob
<D
>),
103 /// The query panicked. Queries trying to wait on this will raise a fatal error which will
108 impl<D
, K
> QueryState
<D
, K
>
110 D
: Copy
+ Clone
+ Eq
+ Hash
,
111 K
: Eq
+ Hash
+ Clone
+ Debug
,
113 pub fn all_inactive(&self) -> bool
{
114 let shards
= self.shards
.lock_shards();
115 shards
.iter().all(|shard
| shard
.active
.is_empty())
118 pub fn try_collect_active_jobs
<CTX
: Copy
>(
122 make_query
: fn(CTX
, K
) -> QueryStackFrame
,
123 jobs
: &mut QueryMap
<D
>,
125 // We use try_lock_shards here since we are called from the
126 // deadlock handler, and this shouldn't be locked.
127 let shards
= self.shards
.try_lock_shards()?
;
128 for (shard_id
, shard
) in shards
.iter().enumerate() {
129 for (k
, v
) in shard
.active
.iter() {
130 if let QueryResult
::Started(ref job
) = *v
{
131 let id
= QueryJobId
::new(job
.id
, shard_id
, kind
);
132 let info
= QueryInfo { span: job.span, query: make_query(tcx, k.clone()) }
;
133 jobs
.insert(id
, QueryJobInfo { info, job: job.clone() }
);
142 impl<D
, K
> Default
for QueryState
<D
, K
> {
143 fn default() -> QueryState
<D
, K
> {
144 QueryState { shards: Default::default() }
148 /// A type representing the responsibility to execute the job in the `job` field.
149 /// This will poison the relevant query if dropped.
150 struct JobOwner
<'tcx
, D
, C
>
152 D
: Copy
+ Clone
+ Eq
+ Hash
,
155 state
: &'tcx QueryState
<D
, C
::Key
>,
156 cache
: &'tcx QueryCacheStore
<C
>,
163 #[cfg(not(parallel_compiler))]
164 fn mk_cycle
<CTX
, V
, R
>(
166 root
: QueryJobId
<CTX
::DepKind
>,
168 handle_cycle_error
: fn(CTX
, DiagnosticBuilder
<'_
>) -> V
,
169 cache
: &dyn crate::query
::QueryStorage
<Value
= V
, Stored
= R
>,
176 let error
: CycleError
= root
.find_cycle_in_stack(
177 tcx
.try_collect_active_jobs().unwrap(),
178 &tcx
.current_query_job(),
181 let error
= report_cycle(tcx
.dep_context().sess(), error
);
182 let value
= handle_cycle_error(tcx
, error
);
183 cache
.store_nocache(value
)
186 impl<'tcx
, D
, C
> JobOwner
<'tcx
, D
, C
>
188 D
: Copy
+ Clone
+ Eq
+ Hash
,
191 /// Either gets a `JobOwner` corresponding the query, allowing us to
192 /// start executing the query, or returns with the result of the query.
193 /// This function assumes that `try_get_cached` is already called and returned `lookup`.
194 /// If the query is executing elsewhere, this will wait for it and return the result.
195 /// If the query panicked, this will silently panic.
197 /// This function is inlined because that results in a noticeable speed-up
198 /// for some compile-time benchmarks.
200 fn try_start
<'b
, CTX
>(
202 state
: &'b QueryState
<CTX
::DepKind
, C
::Key
>,
203 cache
: &'b QueryCacheStore
<C
>,
207 query
: &QueryVtable
<CTX
, C
::Key
, C
::Value
>,
208 ) -> TryGetJob
<'b
, CTX
::DepKind
, C
>
212 let shard
= lookup
.shard
;
213 let mut state_lock
= state
.shards
.get_shard_by_index(shard
).lock();
214 let lock
= &mut *state_lock
;
216 match lock
.active
.entry(key
) {
217 Entry
::Vacant(entry
) => {
218 // Generate an id unique within this shard.
219 let id
= lock
.jobs
.checked_add(1).unwrap();
221 let id
= QueryShardJobId(NonZeroU32
::new(id
).unwrap());
223 let job
= tcx
.current_query_job();
224 let job
= QueryJob
::new(id
, span
, job
);
226 let key
= entry
.key().clone();
227 entry
.insert(QueryResult
::Started(job
));
229 let global_id
= QueryJobId
::new(id
, shard
, query
.dep_kind
);
230 let owner
= JobOwner { state, cache, id: global_id, key }
;
231 return TryGetJob
::NotYetStarted(owner
);
233 Entry
::Occupied(mut entry
) => {
234 match entry
.get_mut() {
235 #[cfg(not(parallel_compiler))]
236 QueryResult
::Started(job
) => {
237 let id
= QueryJobId
::new(job
.id
, shard
, query
.dep_kind
);
241 // If we are single-threaded we know that we have cycle error,
242 // so we just return the error.
243 return TryGetJob
::Cycle(mk_cycle(
247 query
.handle_cycle_error
,
251 #[cfg(parallel_compiler)]
252 QueryResult
::Started(job
) => {
253 // For parallel queries, we'll block and wait until the query running
254 // in another thread has completed. Record how long we wait in the
256 let query_blocked_prof_timer
= tcx
.dep_context().profiler().query_blocked();
259 let latch
= job
.latch();
260 let key
= entry
.key().clone();
264 // With parallel queries we might just have to wait on some other
266 let result
= latch
.wait_on(tcx
.current_query_job(), span
);
268 if let Err(cycle
) = result
{
269 let cycle
= report_cycle(tcx
.dep_context().sess(), cycle
);
270 let value
= (query
.handle_cycle_error
)(tcx
, cycle
);
271 let value
= cache
.cache
.store_nocache(value
);
272 return TryGetJob
::Cycle(value
);
277 .lookup(cache
, &key
, |value
, index
| {
278 if unlikely
!(tcx
.dep_context().profiler().enabled()) {
279 tcx
.dep_context().profiler().query_cache_hit(index
.into());
281 #[cfg(debug_assertions)]
283 cache
.cache_hits
.fetch_add(1, Ordering
::Relaxed
);
285 (value
.clone(), index
)
287 .unwrap_or_else(|_
| panic
!("value must be in cache after waiting"));
289 query_blocked_prof_timer
.finish_with_query_invocation_id(cached
.1.into
());
291 return TryGetJob
::JobCompleted(cached
);
293 QueryResult
::Poisoned
=> FatalError
.raise(),
299 /// Completes the query by updating the query cache with the `result`,
300 /// signals the waiter and forgets the JobOwner, so it won't poison the query
301 fn complete(self, result
: C
::Value
, dep_node_index
: DepNodeIndex
) -> C
::Stored
{
302 // We can move out of `self` here because we `mem::forget` it below
303 let key
= unsafe { ptr::read(&self.key) }
;
304 let state
= self.state
;
305 let cache
= self.cache
;
307 // Forget ourself so our destructor won't poison the query
310 let (job
, result
) = {
311 let key_hash
= hash_for_shard(&key
);
312 let shard
= get_shard_index_by_hash(key_hash
);
314 let mut lock
= state
.shards
.get_shard_by_index(shard
).lock();
315 match lock
.active
.remove(&key
).unwrap() {
316 QueryResult
::Started(job
) => job
,
317 QueryResult
::Poisoned
=> panic
!(),
321 let mut lock
= cache
.shards
.get_shard_by_index(shard
).lock();
322 cache
.cache
.complete(&mut lock
, key
, result
, dep_node_index
)
327 job
.signal_complete();
332 fn with_diagnostics
<F
, R
>(f
: F
) -> (R
, ThinVec
<Diagnostic
>)
334 F
: FnOnce(Option
<&Lock
<ThinVec
<Diagnostic
>>>) -> R
,
336 let diagnostics
= Lock
::new(ThinVec
::new());
337 let result
= f(Some(&diagnostics
));
338 (result
, diagnostics
.into_inner())
341 impl<'tcx
, D
, C
> Drop
for JobOwner
<'tcx
, D
, C
>
343 D
: Copy
+ Clone
+ Eq
+ Hash
,
349 // Poison the query so jobs waiting on it panic.
350 let state
= self.state
;
351 let shard
= state
.shards
.get_shard_by_value(&self.key
);
353 let mut shard
= shard
.lock();
354 let job
= match shard
.active
.remove(&self.key
).unwrap() {
355 QueryResult
::Started(job
) => job
,
356 QueryResult
::Poisoned
=> panic
!(),
358 shard
.active
.insert(self.key
.clone(), QueryResult
::Poisoned
);
361 // Also signal the completion of the job, so waiters
362 // will continue execution.
363 job
.signal_complete();
368 pub(crate) struct CycleError
{
369 /// The query and related span that uses the cycle.
370 pub usage
: Option
<(Span
, QueryStackFrame
)>,
371 pub cycle
: Vec
<QueryInfo
>,
374 /// The result of `try_start`.
375 enum TryGetJob
<'tcx
, D
, C
>
377 D
: Copy
+ Clone
+ Eq
+ Hash
,
380 /// The query is not yet started. Contains a guard to the cache eventually used to start it.
381 NotYetStarted(JobOwner
<'tcx
, D
, C
>),
383 /// The query was already completed.
384 /// Returns the result of the query and its dep-node index
385 /// if it succeeded or a cycle error if it failed.
386 #[cfg(parallel_compiler)]
387 JobCompleted((C
::Stored
, DepNodeIndex
)),
389 /// Trying to execute the query resulted in a cycle.
393 /// Checks if the query is already computed and in the cache.
394 /// It returns the shard index and a lock guard to the shard,
395 /// which will be used if the query is not in the cache and we need
398 pub fn try_get_cached
<'a
, CTX
, C
, R
, OnHit
>(
400 cache
: &'a QueryCacheStore
<C
>,
402 // `on_hit` can be called while holding a lock to the query cache
404 ) -> Result
<R
, QueryLookup
>
408 OnHit
: FnOnce(&C
::Stored
) -> R
,
410 cache
.cache
.lookup(cache
, &key
, |value
, index
| {
411 if unlikely
!(tcx
.profiler().enabled()) {
412 tcx
.profiler().query_cache_hit(index
.into());
414 #[cfg(debug_assertions)]
416 cache
.cache_hits
.fetch_add(1, Ordering
::Relaxed
);
418 tcx
.dep_graph().read_index(index
);
423 fn try_execute_query
<CTX
, C
>(
425 state
: &QueryState
<CTX
::DepKind
, C
::Key
>,
426 cache
: &QueryCacheStore
<C
>,
430 query
: &QueryVtable
<CTX
, C
::Key
, C
::Value
>,
431 compute
: fn(CTX
::DepContext
, C
::Key
) -> C
::Value
,
435 C
::Key
: DepNodeParams
<CTX
::DepContext
>,
438 let job
= match JobOwner
::<'_
, CTX
::DepKind
, C
>::try_start(
447 TryGetJob
::NotYetStarted(job
) => job
,
448 TryGetJob
::Cycle(result
) => return result
,
449 #[cfg(parallel_compiler)]
450 TryGetJob
::JobCompleted((v
, index
)) => {
451 tcx
.dep_context().dep_graph().read_index(index
);
456 let dep_graph
= tcx
.dep_context().dep_graph();
458 // Fast path for when incr. comp. is off.
459 if !dep_graph
.is_fully_enabled() {
460 let prof_timer
= tcx
.dep_context().profiler().query_provider();
461 let result
= tcx
.start_query(job
.id
, None
, || compute(*tcx
.dep_context(), key
));
462 let dep_node_index
= dep_graph
.next_virtual_depnode_index();
463 prof_timer
.finish_with_query_invocation_id(dep_node_index
.into());
464 return job
.complete(result
, dep_node_index
);
468 let prof_timer
= tcx
.dep_context().profiler().query_provider();
470 let ((result
, dep_node_index
), diagnostics
) = with_diagnostics(|diagnostics
| {
471 tcx
.start_query(job
.id
, diagnostics
, || {
472 dep_graph
.with_anon_task(*tcx
.dep_context(), query
.dep_kind
, || {
473 compute(*tcx
.dep_context(), key
)
478 prof_timer
.finish_with_query_invocation_id(dep_node_index
.into());
480 dep_graph
.read_index(dep_node_index
);
482 if unlikely
!(!diagnostics
.is_empty()) {
483 tcx
.store_diagnostics_for_anon_node(dep_node_index
, diagnostics
);
486 return job
.complete(result
, dep_node_index
);
489 let dep_node
= query
.to_dep_node(*tcx
.dep_context(), &key
);
491 if !query
.eval_always
{
492 // The diagnostics for this query will be
493 // promoted to the current session during
494 // `try_mark_green()`, so we can ignore them here.
495 let loaded
= tcx
.start_query(job
.id
, None
, || {
496 let marked
= dep_graph
.try_mark_green_and_read(tcx
, &dep_node
);
497 marked
.map(|(prev_dep_node_index
, dep_node_index
)| {
499 load_from_disk_and_cache_in_memory(
512 if let Some((result
, dep_node_index
)) = loaded
{
513 return job
.complete(result
, dep_node_index
);
517 let (result
, dep_node_index
) = force_query_with_job(tcx
, key
, job
, dep_node
, query
, compute
);
518 dep_graph
.read_index(dep_node_index
);
522 fn load_from_disk_and_cache_in_memory
<CTX
, K
, V
: Debug
>(
525 prev_dep_node_index
: SerializedDepNodeIndex
,
526 dep_node_index
: DepNodeIndex
,
527 dep_node
: &DepNode
<CTX
::DepKind
>,
528 query
: &QueryVtable
<CTX
, K
, V
>,
529 compute
: fn(CTX
::DepContext
, K
) -> V
,
534 // Note this function can be called concurrently from the same query
535 // We must ensure that this is handled correctly.
537 debug_assert
!(tcx
.dep_context().dep_graph().is_green(dep_node
));
539 // First we try to load the result from the on-disk cache.
540 let result
= if query
.cache_on_disk(tcx
, &key
, None
) {
541 let prof_timer
= tcx
.dep_context().profiler().incr_cache_loading();
542 let result
= query
.try_load_from_disk(tcx
, prev_dep_node_index
);
543 prof_timer
.finish_with_query_invocation_id(dep_node_index
.into());
545 // We always expect to find a cached result for things that
546 // can be forced from `DepNode`.
548 !dep_node
.kind
.can_reconstruct_query_key() || result
.is_some(),
549 "missing on-disk cache entry for {:?}",
554 // Some things are never cached on disk.
558 if let Some(result
) = result
{
559 // If `-Zincremental-verify-ich` is specified, re-hash results from
560 // the cache and make sure that they have the expected fingerprint.
561 if unlikely
!(tcx
.dep_context().sess().opts
.debugging_opts
.incremental_verify_ich
) {
562 incremental_verify_ich(*tcx
.dep_context(), &result
, dep_node
, query
);
567 // We could not load a result from the on-disk cache, so
569 let prof_timer
= tcx
.dep_context().profiler().query_provider();
571 // The dep-graph for this computation is already in-place.
572 let result
= tcx
.dep_context().dep_graph().with_ignore(|| compute(*tcx
.dep_context(), key
));
574 prof_timer
.finish_with_query_invocation_id(dep_node_index
.into());
576 // Verify that re-running the query produced a result with the expected hash
577 // This catches bugs in query implementations, turning them into ICEs.
578 // For example, a query might sort its result by `DefId` - since `DefId`s are
579 // not stable across compilation sessions, the result could get up getting sorted
580 // in a different order when the query is re-run, even though all of the inputs
581 // (e.g. `DefPathHash` values) were green.
583 // See issue #82920 for an example of a miscompilation that would get turned into
584 // an ICE by this check
585 incremental_verify_ich(*tcx
.dep_context(), &result
, dep_node
, query
);
591 fn incremental_verify_ich
<CTX
, K
, V
: Debug
>(
592 tcx
: CTX
::DepContext
,
594 dep_node
: &DepNode
<CTX
::DepKind
>,
595 query
: &QueryVtable
<CTX
, K
, V
>,
600 tcx
.dep_graph().is_green(dep_node
),
601 "fingerprint for green query instance not loaded from cache: {:?}",
605 debug
!("BEGIN verify_ich({:?})", dep_node
);
606 let mut hcx
= tcx
.create_stable_hashing_context();
608 let new_hash
= query
.hash_result(&mut hcx
, result
).unwrap_or(Fingerprint
::ZERO
);
609 debug
!("END verify_ich({:?})", dep_node
);
611 let old_hash
= tcx
.dep_graph().prev_fingerprint_of(dep_node
);
613 if Some(new_hash
) != old_hash
{
614 let run_cmd
= if let Some(crate_name
) = &tcx
.sess().opts
.crate_name
{
615 format
!("`cargo clean -p {}` or `cargo clean`", crate_name
)
617 "`cargo clean`".to_string()
619 tcx
.sess().struct_err(&format
!("internal compiler error: encountered incremental compilation error with {:?}", dep_node
))
620 .help(&format
!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd
))
621 .note(&format
!("Please follow the instructions below to create a bug report with the provided information"))
622 .note(&format
!("See <https://github.com/rust-lang/rust/issues/84970> for more information"))
624 panic
!("Found unstable fingerprints for {:?}: {:?}", dep_node
, result
);
628 fn force_query_with_job
<C
, CTX
>(
631 job
: JobOwner
<'_
, CTX
::DepKind
, C
>,
632 dep_node
: DepNode
<CTX
::DepKind
>,
633 query
: &QueryVtable
<CTX
, C
::Key
, C
::Value
>,
634 compute
: fn(CTX
::DepContext
, C
::Key
) -> C
::Value
,
635 ) -> (C
::Stored
, DepNodeIndex
)
640 // If the following assertion triggers, it can have two reasons:
641 // 1. Something is wrong with DepNode creation, either here or
642 // in `DepGraph::try_mark_green()`.
643 // 2. Two distinct query keys get mapped to the same `DepNode`
644 // (see for example #48923).
646 !tcx
.dep_context().dep_graph().dep_node_exists(&dep_node
),
647 "forcing query with already existing `DepNode`\n\
654 let prof_timer
= tcx
.dep_context().profiler().query_provider();
656 let ((result
, dep_node_index
), diagnostics
) = with_diagnostics(|diagnostics
| {
657 tcx
.start_query(job
.id
, diagnostics
, || {
658 if query
.eval_always
{
659 tcx
.dep_context().dep_graph().with_eval_always_task(
667 tcx
.dep_context().dep_graph().with_task(
678 prof_timer
.finish_with_query_invocation_id(dep_node_index
.into());
680 if unlikely
!(!diagnostics
.is_empty()) && dep_node
.kind
!= DepKind
::NULL
{
681 tcx
.store_diagnostics(dep_node_index
, diagnostics
);
684 let result
= job
.complete(result
, dep_node_index
);
686 (result
, dep_node_index
)
690 fn get_query_impl
<CTX
, C
>(
692 state
: &QueryState
<CTX
::DepKind
, C
::Key
>,
693 cache
: &QueryCacheStore
<C
>,
697 query
: &QueryVtable
<CTX
, C
::Key
, C
::Value
>,
698 compute
: fn(CTX
::DepContext
, C
::Key
) -> C
::Value
,
703 C
::Key
: DepNodeParams
<CTX
::DepContext
>,
705 try_execute_query(tcx
, state
, cache
, span
, key
, lookup
, query
, compute
)
708 /// Ensure that either this query has all green inputs or been executed.
709 /// Executing `query::ensure(D)` is considered a read of the dep-node `D`.
710 /// Returns true if the query should still run.
712 /// This function is particularly useful when executing passes for their
713 /// side-effects -- e.g., in order to report errors for erroneous programs.
715 /// Note: The optimization is only available during incr. comp.
717 fn ensure_must_run
<CTX
, K
, V
>(tcx
: CTX
, key
: &K
, query
: &QueryVtable
<CTX
, K
, V
>) -> bool
719 K
: crate::dep_graph
::DepNodeParams
<CTX
::DepContext
>,
722 if query
.eval_always
{
726 // Ensuring an anonymous query makes no sense
727 assert
!(!query
.anon
);
729 let dep_node
= query
.to_dep_node(*tcx
.dep_context(), key
);
731 match tcx
.dep_context().dep_graph().try_mark_green_and_read(tcx
, &dep_node
) {
733 // A None return from `try_mark_green_and_read` means that this is either
734 // a new dep node or that the dep node has already been marked red.
735 // Either way, we can't call `dep_graph.read()` as we don't have the
736 // DepNodeIndex. We must invoke the query itself. The performance cost
737 // this introduces should be negligible as we'll immediately hit the
738 // in-memory cache, or another query down the line will.
741 Some((_
, dep_node_index
)) => {
742 tcx
.dep_context().profiler().query_cache_hit(dep_node_index
.into());
749 fn force_query_impl
<CTX
, C
>(
751 state
: &QueryState
<CTX
::DepKind
, C
::Key
>,
752 cache
: &QueryCacheStore
<C
>,
754 dep_node
: DepNode
<CTX
::DepKind
>,
755 query
: &QueryVtable
<CTX
, C
::Key
, C
::Value
>,
756 compute
: fn(CTX
::DepContext
, C
::Key
) -> C
::Value
,
760 C
::Key
: DepNodeParams
<CTX
::DepContext
>,
763 debug_assert
!(!query
.anon
);
765 // We may be concurrently trying both execute and force a query.
766 // Ensure that only one of them runs the query.
767 let cached
= cache
.cache
.lookup(cache
, &key
, |_
, index
| {
768 if unlikely
!(tcx
.dep_context().profiler().enabled()) {
769 tcx
.dep_context().profiler().query_cache_hit(index
.into());
771 #[cfg(debug_assertions)]
773 cache
.cache_hits
.fetch_add(1, Ordering
::Relaxed
);
777 let lookup
= match cached
{
778 Ok(()) => return true,
779 Err(lookup
) => lookup
,
782 let job
= match JobOwner
::<'_
, CTX
::DepKind
, C
>::try_start(
791 TryGetJob
::NotYetStarted(job
) => job
,
792 TryGetJob
::Cycle(_
) => return true,
793 #[cfg(parallel_compiler)]
794 TryGetJob
::JobCompleted(_
) => return true,
797 force_query_with_job(tcx
, key
, job
, dep_node
, query
, compute
);
807 pub fn get_query
<Q
, CTX
>(
813 ) -> Option
<Q
::Stored
>
815 Q
: QueryDescription
<CTX
>,
816 Q
::Key
: DepNodeParams
<CTX
::DepContext
>,
819 let query
= &Q
::VTABLE
;
820 if let QueryMode
::Ensure
= mode
{
821 if !ensure_must_run(tcx
, &key
, query
) {
826 debug
!("ty::query::get_query<{}>(key={:?}, span={:?})", Q
::NAME
, key
, span
);
827 let compute
= Q
::compute_fn(tcx
, &key
);
828 let value
= get_query_impl(
841 pub fn force_query
<Q
, CTX
>(tcx
: CTX
, dep_node
: &DepNode
<CTX
::DepKind
>) -> bool
843 Q
: QueryDescription
<CTX
>,
844 Q
::Key
: DepNodeParams
<CTX
::DepContext
>,
851 if !<Q
::Key
as DepNodeParams
<CTX
::DepContext
>>::can_reconstruct_query_key() {
855 let key
= if let Some(key
) =
856 <Q
::Key
as DepNodeParams
<CTX
::DepContext
>>::recover(*tcx
.dep_context(), &dep_node
)
863 let compute
= Q
::compute_fn(tcx
, &key
);