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::{on_disk_cache, Queries}
;
6 use rustc_middle
::dep_graph
::{DepNodeIndex, SerializedDepNodeIndex}
;
7 use rustc_middle
::ty
::tls
::{self, ImplicitCtxt}
;
8 use rustc_middle
::ty
::TyCtxt
;
9 use rustc_query_system
::dep_graph
::HasDepContext
;
10 use rustc_query_system
::query
::{QueryContext, QueryJobId, QueryMap, QuerySideEffects}
;
12 use rustc_data_structures
::sync
::Lock
;
13 use rustc_data_structures
::thin_vec
::ThinVec
;
14 use rustc_errors
::{Diagnostic, Handler}
;
15 use rustc_serialize
::opaque
;
18 use std
::num
::NonZeroU64
;
20 #[derive(Copy, Clone)]
21 pub struct QueryCtxt
<'tcx
> {
22 pub tcx
: TyCtxt
<'tcx
>,
23 pub queries
: &'tcx Queries
<'tcx
>,
26 impl<'tcx
> std
::ops
::Deref
for QueryCtxt
<'tcx
> {
27 type Target
= TyCtxt
<'tcx
>;
30 fn deref(&self) -> &Self::Target
{
35 impl<'tcx
> HasDepContext
for QueryCtxt
<'tcx
> {
36 type DepKind
= rustc_middle
::dep_graph
::DepKind
;
37 type DepContext
= TyCtxt
<'tcx
>;
40 fn dep_context(&self) -> &Self::DepContext
{
45 impl QueryContext
for QueryCtxt
<'_
> {
46 fn next_job_id(&self) -> QueryJobId
{
49 self.queries
.jobs
.fetch_add(1, rustc_data_structures
::sync
::Ordering
::Relaxed
),
55 fn current_query_job(&self) -> Option
<QueryJobId
> {
56 tls
::with_related_context(**self, |icx
| icx
.query
)
59 fn try_collect_active_jobs(&self) -> Option
<QueryMap
> {
60 self.queries
.try_collect_active_jobs(**self)
63 // Interactions with on_disk_cache
64 fn load_side_effects(&self, prev_dep_node_index
: SerializedDepNodeIndex
) -> QuerySideEffects
{
68 .map(|c
| c
.load_side_effects(**self, prev_dep_node_index
))
72 fn store_side_effects(&self, dep_node_index
: DepNodeIndex
, side_effects
: QuerySideEffects
) {
73 if let Some(c
) = self.queries
.on_disk_cache
.as_ref() {
74 c
.store_side_effects(dep_node_index
, side_effects
)
78 fn store_side_effects_for_anon_node(
80 dep_node_index
: DepNodeIndex
,
81 side_effects
: QuerySideEffects
,
83 if let Some(c
) = self.queries
.on_disk_cache
.as_ref() {
84 c
.store_side_effects_for_anon_node(dep_node_index
, side_effects
)
88 /// Executes a job by changing the `ImplicitCtxt` to point to the
89 /// new query job while it executes. It returns the diagnostics
90 /// captured during execution and the actual result.
95 diagnostics
: Option
<&Lock
<ThinVec
<Diagnostic
>>>,
96 compute
: impl FnOnce() -> R
,
98 // The `TyCtxt` stored in TLS has the same global interner lifetime
99 // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes
100 // when accessing the `ImplicitCtxt`.
101 tls
::with_related_context(**self, move |current_icx
| {
102 // Update the `ImplicitCtxt` to point to our new query job.
103 let new_icx
= ImplicitCtxt
{
107 layout_depth
: current_icx
.layout_depth
,
108 task_deps
: current_icx
.task_deps
,
111 // Use the `ImplicitCtxt` while we execute the query.
112 tls
::enter_context(&new_icx
, |_
| {
113 rustc_data_structures
::stack
::ensure_sufficient_stack(compute
)
119 impl<'tcx
> QueryCtxt
<'tcx
> {
121 pub fn from_tcx(tcx
: TyCtxt
<'tcx
>) -> Self {
122 let queries
= tcx
.queries
.as_any();
123 let queries
= unsafe {
124 let queries
= std
::mem
::transmute
::<&dyn Any
, &dyn Any
>(queries
);
125 let queries
= queries
.downcast_ref().unwrap();
126 let queries
= std
::mem
::transmute
::<&Queries
<'_
>, &Queries
<'_
>>(queries
);
129 QueryCtxt { tcx, queries }
132 crate fn on_disk_cache(self) -> Option
<&'tcx on_disk_cache
::OnDiskCache
<'tcx
>> {
133 self.queries
.on_disk_cache
.as_ref()
136 #[cfg(parallel_compiler)]
137 pub unsafe fn deadlock(self, registry
: &rustc_rayon_core
::Registry
) {
138 rustc_query_system
::query
::deadlock(self, registry
)
141 pub(super) fn encode_query_results(
143 encoder
: &mut on_disk_cache
::CacheEncoder
<'_
, 'tcx
, opaque
::FileEncoder
>,
144 query_result_index
: &mut on_disk_cache
::EncodedDepNodeIndex
,
145 ) -> opaque
::FileEncodeResult
{
146 macro_rules
! encode_queries
{
147 ($
($query
:ident
,)*) => {
149 on_disk_cache
::encode_query_results
::<_
, super::queries
::$query
<'_
>>(
158 rustc_cached_queries
!(encode_queries
!);
163 pub fn try_print_query_stack(
165 query
: Option
<QueryJobId
>,
167 num_frames
: Option
<usize>,
169 rustc_query_system
::query
::print_query_stack(self, query
, handler
, num_frames
)
173 macro_rules
! handle_cycle_error
{
174 ([][$tcx
: expr
, $error
:expr
]) => {{
176 Value
::from_cycle_error($tcx
)
178 ([(fatal_cycle
) $
($rest
:tt
)*][$tcx
:expr
, $error
:expr
]) => {{
180 $tcx
.sess
.abort_if_errors();
183 ([(cycle_delay_bug
) $
($rest
:tt
)*][$tcx
:expr
, $error
:expr
]) => {{
184 $error
.delay_as_bug();
185 Value
::from_cycle_error($tcx
)
187 ([$other
:tt $
($modifiers
:tt
)*][$
($args
:tt
)*]) => {
188 handle_cycle_error
!([$
($modifiers
)*][$
($args
)*])
192 macro_rules
! is_anon
{
196 ([(anon
) $
($rest
:tt
)*]) => {{
199 ([$other
:tt $
($modifiers
:tt
)*]) => {
200 is_anon
!([$
($modifiers
)*])
204 macro_rules
! is_eval_always
{
208 ([(eval_always
) $
($rest
:tt
)*]) => {{
211 ([$other
:tt $
($modifiers
:tt
)*]) => {
212 is_eval_always
!([$
($modifiers
)*])
216 macro_rules
! hash_result
{
218 Some(dep_graph
::hash_result
)
220 ([(no_hash
) $
($rest
:tt
)*]) => {{
223 ([$other
:tt $
($modifiers
:tt
)*]) => {
224 hash_result
!([$
($modifiers
)*])
228 macro_rules
! get_provider
{
229 ([][$tcx
:expr
, $name
:ident
, $key
:expr
]) => {{
230 $tcx
.queries
.local_providers
.$name
232 ([(separate_provide_extern
) $
($rest
:tt
)*][$tcx
:expr
, $name
:ident
, $key
:expr
]) => {{
233 if $key
.query_crate_is_local() {
234 $tcx
.queries
.local_providers
.$name
236 $tcx
.queries
.extern_providers
.$name
239 ([$other
:tt $
($modifiers
:tt
)*][$
($args
:tt
)*]) => {
240 get_provider
!([$
($modifiers
)*][$
($args
)*])
244 macro_rules
! opt_remap_env_constness
{
245 ([][$name
:ident
]) => {}
;
246 ([(remap_env_constness
) $
($rest
:tt
)*][$name
:ident
]) => {
247 let $name
= $name
.without_const();
249 ([$other
:tt $
($modifiers
:tt
)*][$name
:ident
]) => {
250 opt_remap_env_constness
!([$
($modifiers
)*][$name
])
254 macro_rules
! define_queries
{
257 [$
($modifiers
:tt
)*] fn $name
:ident($
($K
:tt
)*) -> $V
:ty
,)*) => {
259 define_queries_struct
! {
261 input
: ($
(([$
($modifiers
)*] [$
($attr
)*] [$name
]))*)
267 // Create an eponymous constructor for each query.
268 $
(#[allow(nonstandard_style)] $(#[$attr])*
269 pub fn $name
<$tcx
>(tcx
: QueryCtxt
<$tcx
>, key
: query_keys
::$name
<$tcx
>) -> QueryStackFrame
{
270 opt_remap_env_constness
!([$
($modifiers
)*][key
]);
271 let kind
= dep_graph
::DepKind
::$name
;
272 let name
= stringify
!($name
);
273 // Disable visible paths printing for performance reasons.
274 // Showing visible path instead of any path is not that important in production.
275 let description
= ty
::print
::with_no_visible_paths
!(
276 // Force filename-line mode to avoid invoking `type_of` query.
277 ty
::print
::with_forced_impl_filename_line
!(
278 queries
::$name
::describe(tcx
, key
)
281 let description
= if tcx
.sess
.verbose() {
282 format
!("{} [{}]", description
, name
)
286 let span
= if kind
== dep_graph
::DepKind
::def_span
{
287 // The `def_span` query is used to calculate `default_span`,
288 // so exit to avoid infinite recursion.
291 Some(key
.default_span(*tcx
))
293 // Use `tcx.hir().opt_def_kind()` to reduce the chance of
294 // accidentally triggering an infinite query loop.
295 let def_kind
= key
.key_as_def_id()
296 .and_then(|def_id
| def_id
.as_local())
297 .and_then(|def_id
| tcx
.hir().opt_def_kind(def_id
));
299 let mut hcx
= tcx
.create_stable_hashing_context();
300 let mut hasher
= StableHasher
::new();
301 std
::mem
::discriminant(&kind
).hash_stable(&mut hcx
, &mut hasher
);
302 key
.hash_stable(&mut hcx
, &mut hasher
);
303 hasher
.finish
::<u64>()
306 QueryStackFrame
::new(name
, description
, span
, def_kind
, hash
)
310 #[allow(nonstandard_style)]
312 use std
::marker
::PhantomData
;
314 $
(pub struct $name
<$tcx
> {
315 data
: PhantomData
<&$
tcx ()>
319 $
(impl<$tcx
> QueryConfig
for queries
::$name
<$tcx
> {
320 type Key
= query_keys
::$name
<$tcx
>;
321 type Value
= query_values
::$name
<$tcx
>;
322 type Stored
= query_stored
::$name
<$tcx
>;
323 const NAME
: &'
static str = stringify
!($name
);
326 impl<$tcx
> QueryDescription
<QueryCtxt
<$tcx
>> for queries
::$name
<$tcx
> {
327 rustc_query_description
! { $name<$tcx> }
329 type Cache
= query_storage
::$name
<$tcx
>;
332 fn query_state
<'a
>(tcx
: QueryCtxt
<$tcx
>) -> &'a QueryState
<Self::Key
>
333 where QueryCtxt
<$tcx
>: 'a
339 fn query_cache
<'a
>(tcx
: QueryCtxt
<$tcx
>) -> &'a
Self::Cache
342 &tcx
.query_caches
.$name
346 fn make_vtable(tcx
: QueryCtxt
<'tcx
>, key
: &Self::Key
) ->
347 QueryVtable
<QueryCtxt
<$tcx
>, Self::Key
, Self::Value
>
349 let compute
= get_provider
!([$
($modifiers
)*][tcx
, $name
, key
]);
350 let cache_on_disk
= Self::cache_on_disk(tcx
.tcx
, key
);
352 anon
: is_anon
!([$
($modifiers
)*]),
353 eval_always
: is_eval_always
!([$
($modifiers
)*]),
354 dep_kind
: dep_graph
::DepKind
::$name
,
355 hash_result
: hash_result
!([$
($modifiers
)*]),
356 handle_cycle_error
: |tcx
, mut error
| handle_cycle_error
!([$
($modifiers
)*][tcx
, error
]),
359 try_load_from_disk
: Self::TRY_LOAD_FROM_DISK
,
364 #[allow(nonstandard_style)]
365 mod query_callbacks
{
367 use rustc_middle
::dep_graph
::DepNode
;
368 use rustc_middle
::ty
::query
::query_keys
;
369 use rustc_query_system
::dep_graph
::DepNodeParams
;
370 use rustc_query_system
::query
::{force_query, QueryDescription}
;
371 use rustc_query_system
::dep_graph
::FingerprintStyle
;
373 // We use this for most things when incr. comp. is turned off.
374 pub fn Null() -> DepKindStruct
{
377 is_eval_always
: false,
378 fingerprint_style
: FingerprintStyle
::Unit
,
379 force_from_dep_node
: Some(|_
, dep_node
| bug
!("force_from_dep_node: encountered {:?}", dep_node
)),
380 try_load_from_on_disk_cache
: None
,
384 pub fn TraitSelect() -> DepKindStruct
{
387 is_eval_always
: false,
388 fingerprint_style
: FingerprintStyle
::Unit
,
389 force_from_dep_node
: None
,
390 try_load_from_on_disk_cache
: None
,
394 pub fn CompileCodegenUnit() -> DepKindStruct
{
397 is_eval_always
: false,
398 fingerprint_style
: FingerprintStyle
::Opaque
,
399 force_from_dep_node
: None
,
400 try_load_from_on_disk_cache
: None
,
404 pub fn CompileMonoItem() -> DepKindStruct
{
407 is_eval_always
: false,
408 fingerprint_style
: FingerprintStyle
::Opaque
,
409 force_from_dep_node
: None
,
410 try_load_from_on_disk_cache
: None
,
414 $
(pub(crate) fn $
name()-> DepKindStruct
{
415 let is_anon
= is_anon
!([$
($modifiers
)*]);
416 let is_eval_always
= is_eval_always
!([$
($modifiers
)*]);
418 let fingerprint_style
=
419 <query_keys
::$name
<'_
> as DepNodeParams
<TyCtxt
<'_
>>>::fingerprint_style();
421 if is_anon
|| !fingerprint_style
.reconstructible() {
422 return DepKindStruct
{
426 force_from_dep_node
: None
,
427 try_load_from_on_disk_cache
: None
,
432 fn recover
<'tcx
>(tcx
: TyCtxt
<'tcx
>, dep_node
: DepNode
) -> Option
<query_keys
::$name
<'tcx
>> {
433 <query_keys
::$name
<'_
> as DepNodeParams
<TyCtxt
<'_
>>>::recover(tcx
, &dep_node
)
436 fn force_from_dep_node(tcx
: TyCtxt
<'_
>, dep_node
: DepNode
) -> bool
{
437 if let Some(key
) = recover(tcx
, dep_node
) {
438 let tcx
= QueryCtxt
::from_tcx(tcx
);
439 force_query
::<queries
::$name
<'_
>, _
>(tcx
, key
, dep_node
);
446 fn try_load_from_on_disk_cache(tcx
: TyCtxt
<'_
>, dep_node
: DepNode
) {
447 debug_assert
!(tcx
.dep_graph
.is_green(&dep_node
));
449 let key
= recover(tcx
, dep_node
).unwrap_or_else(|| panic
!("Failed to recover key for {:?} with hash {}", dep_node
, dep_node
.hash
));
450 if queries
::$name
::cache_on_disk(tcx
, &key
) {
451 let _
= tcx
.$
name(key
);
459 force_from_dep_node
: Some(force_from_dep_node
),
460 try_load_from_on_disk_cache
: Some(try_load_from_on_disk_cache
),
465 pub fn query_callbacks
<'tcx
>(arena
: &'tcx Arena
<'tcx
>) -> &'tcx
[DepKindStruct
] {
466 arena
.alloc_from_iter(make_dep_kind_array
!(query_callbacks
))
471 // FIXME(eddyb) this macro (and others?) use `$tcx` and `'tcx` interchangeably.
472 // We should either not take `$tcx` at all and use `'tcx` everywhere, or use
473 // `$tcx` everywhere (even if that isn't necessary due to lack of hygiene).
474 macro_rules
! define_queries_struct
{
476 input
: ($
(([$
($modifiers
:tt
)*] [$
($attr
:tt
)*] [$name
:ident
]))*)) => {
477 pub struct Queries
<$tcx
> {
478 local_providers
: Box
<Providers
>,
479 extern_providers
: Box
<ExternProviders
>,
481 pub on_disk_cache
: Option
<OnDiskCache
<$tcx
>>,
485 $
($
(#[$attr])* $name: QueryState<query_keys::$name<$tcx>>,)*
488 impl<$tcx
> Queries
<$tcx
> {
490 local_providers
: Providers
,
491 extern_providers
: ExternProviders
,
492 on_disk_cache
: Option
<OnDiskCache
<$tcx
>>,
495 local_providers
: Box
::new(local_providers
),
496 extern_providers
: Box
::new(extern_providers
),
498 jobs
: AtomicU64
::new(1),
499 $
($name
: Default
::default()),*
503 pub(crate) fn try_collect_active_jobs(
506 ) -> Option
<QueryMap
> {
507 let tcx
= QueryCtxt { tcx, queries: self }
;
508 let mut jobs
= QueryMap
::default();
511 self.$name
.try_collect_active_jobs(
522 impl<'tcx
> QueryEngine
<'tcx
> for Queries
<'tcx
> {
523 fn as_any(&'tcx
self) -> &'tcx
dyn std
::any
::Any
{
524 let this
= unsafe { std::mem::transmute::<&Queries<'_>, &Queries<'_>>(self) }
;
528 fn try_mark_green(&'tcx
self, tcx
: TyCtxt
<'tcx
>, dep_node
: &dep_graph
::DepNode
) -> bool
{
529 let qcx
= QueryCtxt { tcx, queries: self }
;
530 tcx
.dep_graph
.try_mark_green(qcx
, dep_node
).is_some()
539 key
: query_keys
::$name
<$tcx
>,
541 ) -> Option
<query_stored
::$name
<$tcx
>> {
542 opt_remap_env_constness
!([$
($modifiers
)*][key
]);
543 let qcx
= QueryCtxt { tcx, queries: self }
;
544 get_query
::<queries
::$name
<$tcx
>, _
>(qcx
, span
, key
, mode
)