]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_query_impl/src/plumbing.rs
Auto merge of #100571 - cjgillot:mir-cost-visit, r=compiler-errors
[rust.git] / compiler / rustc_query_impl / src / plumbing.rs
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.
4
5 use crate::keys::Key;
6 use crate::{on_disk_cache, Queries};
7 use rustc_middle::dep_graph::{self, DepKind, DepNodeIndex, SerializedDepNodeIndex};
8 use rustc_middle::ty::tls::{self, ImplicitCtxt};
9 use rustc_middle::ty::{self, TyCtxt};
10 use rustc_query_system::dep_graph::HasDepContext;
11 use rustc_query_system::ich::StableHashingContext;
12 use rustc_query_system::query::{
13     QueryContext, QueryJobId, QueryMap, QuerySideEffects, QueryStackFrame,
14 };
15
16 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
17 use rustc_data_structures::sync::Lock;
18 use rustc_data_structures::thin_vec::ThinVec;
19 use rustc_errors::{Diagnostic, Handler};
20
21 use std::any::Any;
22 use std::num::NonZeroU64;
23
24 #[derive(Copy, Clone)]
25 pub struct QueryCtxt<'tcx> {
26     pub tcx: TyCtxt<'tcx>,
27     pub queries: &'tcx Queries<'tcx>,
28 }
29
30 impl<'tcx> std::ops::Deref for QueryCtxt<'tcx> {
31     type Target = TyCtxt<'tcx>;
32
33     #[inline]
34     fn deref(&self) -> &Self::Target {
35         &self.tcx
36     }
37 }
38
39 impl<'tcx> HasDepContext for QueryCtxt<'tcx> {
40     type DepKind = rustc_middle::dep_graph::DepKind;
41     type DepContext = TyCtxt<'tcx>;
42
43     #[inline]
44     fn dep_context(&self) -> &Self::DepContext {
45         &self.tcx
46     }
47 }
48
49 impl QueryContext for QueryCtxt<'_> {
50     fn next_job_id(&self) -> QueryJobId {
51         QueryJobId(
52             NonZeroU64::new(
53                 self.queries.jobs.fetch_add(1, rustc_data_structures::sync::Ordering::Relaxed),
54             )
55             .unwrap(),
56         )
57     }
58
59     fn current_query_job(&self) -> Option<QueryJobId> {
60         tls::with_related_context(**self, |icx| icx.query)
61     }
62
63     fn try_collect_active_jobs(&self) -> Option<QueryMap> {
64         self.queries.try_collect_active_jobs(**self)
65     }
66
67     // Interactions with on_disk_cache
68     fn load_side_effects(&self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects {
69         self.queries
70             .on_disk_cache
71             .as_ref()
72             .map(|c| c.load_side_effects(**self, prev_dep_node_index))
73             .unwrap_or_default()
74     }
75
76     fn store_side_effects(&self, dep_node_index: DepNodeIndex, side_effects: QuerySideEffects) {
77         if let Some(c) = self.queries.on_disk_cache.as_ref() {
78             c.store_side_effects(dep_node_index, side_effects)
79         }
80     }
81
82     fn store_side_effects_for_anon_node(
83         &self,
84         dep_node_index: DepNodeIndex,
85         side_effects: QuerySideEffects,
86     ) {
87         if let Some(c) = self.queries.on_disk_cache.as_ref() {
88             c.store_side_effects_for_anon_node(dep_node_index, side_effects)
89         }
90     }
91
92     /// Executes a job by changing the `ImplicitCtxt` to point to the
93     /// new query job while it executes. It returns the diagnostics
94     /// captured during execution and the actual result.
95     #[inline(always)]
96     fn start_query<R>(
97         &self,
98         token: QueryJobId,
99         diagnostics: Option<&Lock<ThinVec<Diagnostic>>>,
100         compute: impl FnOnce() -> R,
101     ) -> R {
102         // The `TyCtxt` stored in TLS has the same global interner lifetime
103         // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes
104         // when accessing the `ImplicitCtxt`.
105         tls::with_related_context(**self, move |current_icx| {
106             // Update the `ImplicitCtxt` to point to our new query job.
107             let new_icx = ImplicitCtxt {
108                 tcx: **self,
109                 query: Some(token),
110                 diagnostics,
111                 layout_depth: current_icx.layout_depth,
112                 task_deps: current_icx.task_deps,
113             };
114
115             // Use the `ImplicitCtxt` while we execute the query.
116             tls::enter_context(&new_icx, |_| {
117                 rustc_data_structures::stack::ensure_sufficient_stack(compute)
118             })
119         })
120     }
121 }
122
123 impl<'tcx> QueryCtxt<'tcx> {
124     #[inline]
125     pub fn from_tcx(tcx: TyCtxt<'tcx>) -> Self {
126         let queries = tcx.queries.as_any();
127         let queries = unsafe {
128             let queries = std::mem::transmute::<&dyn Any, &dyn Any>(queries);
129             let queries = queries.downcast_ref().unwrap();
130             let queries = std::mem::transmute::<&Queries<'_>, &Queries<'_>>(queries);
131             queries
132         };
133         QueryCtxt { tcx, queries }
134     }
135
136     pub(crate) fn on_disk_cache(self) -> Option<&'tcx on_disk_cache::OnDiskCache<'tcx>> {
137         self.queries.on_disk_cache.as_ref()
138     }
139
140     pub(super) fn encode_query_results(
141         self,
142         encoder: &mut on_disk_cache::CacheEncoder<'_, 'tcx>,
143         query_result_index: &mut on_disk_cache::EncodedDepNodeIndex,
144     ) {
145         macro_rules! encode_queries {
146             ($($query:ident,)*) => {
147                 $(
148                     on_disk_cache::encode_query_results::<_, super::queries::$query<'_>>(
149                         self,
150                         encoder,
151                         query_result_index
152                     );
153                 )*
154             }
155         }
156
157         rustc_cached_queries!(encode_queries!);
158     }
159
160     pub fn try_print_query_stack(
161         self,
162         query: Option<QueryJobId>,
163         handler: &Handler,
164         num_frames: Option<usize>,
165     ) -> usize {
166         rustc_query_system::query::print_query_stack(self, query, handler, num_frames)
167     }
168 }
169
170 macro_rules! handle_cycle_error {
171     ([][$tcx: expr, $error:expr]) => {{
172         $error.emit();
173         Value::from_cycle_error($tcx)
174     }};
175     ([(fatal_cycle) $($rest:tt)*][$tcx:expr, $error:expr]) => {{
176         $error.emit();
177         $tcx.sess.abort_if_errors();
178         unreachable!()
179     }};
180     ([(cycle_delay_bug) $($rest:tt)*][$tcx:expr, $error:expr]) => {{
181         $error.delay_as_bug();
182         Value::from_cycle_error($tcx)
183     }};
184     ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => {
185         handle_cycle_error!([$($modifiers)*][$($args)*])
186     };
187 }
188
189 macro_rules! is_anon {
190     ([]) => {{
191         false
192     }};
193     ([(anon) $($rest:tt)*]) => {{
194         true
195     }};
196     ([$other:tt $($modifiers:tt)*]) => {
197         is_anon!([$($modifiers)*])
198     };
199 }
200
201 macro_rules! is_eval_always {
202     ([]) => {{
203         false
204     }};
205     ([(eval_always) $($rest:tt)*]) => {{
206         true
207     }};
208     ([$other:tt $($modifiers:tt)*]) => {
209         is_eval_always!([$($modifiers)*])
210     };
211 }
212
213 macro_rules! hash_result {
214     ([]) => {{
215         Some(dep_graph::hash_result)
216     }};
217     ([(no_hash) $($rest:tt)*]) => {{
218         None
219     }};
220     ([$other:tt $($modifiers:tt)*]) => {
221         hash_result!([$($modifiers)*])
222     };
223 }
224
225 macro_rules! get_provider {
226     ([][$tcx:expr, $name:ident, $key:expr]) => {{
227         $tcx.queries.local_providers.$name
228     }};
229     ([(separate_provide_extern) $($rest:tt)*][$tcx:expr, $name:ident, $key:expr]) => {{
230         if $key.query_crate_is_local() {
231             $tcx.queries.local_providers.$name
232         } else {
233             $tcx.queries.extern_providers.$name
234         }
235     }};
236     ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => {
237         get_provider!([$($modifiers)*][$($args)*])
238     };
239 }
240
241 pub(crate) fn create_query_frame<
242     'tcx,
243     K: Copy + Key + for<'a> HashStable<StableHashingContext<'a>>,
244 >(
245     tcx: QueryCtxt<'tcx>,
246     do_describe: fn(QueryCtxt<'tcx>, K) -> String,
247     key: K,
248     kind: DepKind,
249     name: &'static str,
250 ) -> QueryStackFrame {
251     // Disable visible paths printing for performance reasons.
252     // Showing visible path instead of any path is not that important in production.
253     let description = ty::print::with_no_visible_paths!(
254         // Force filename-line mode to avoid invoking `type_of` query.
255         ty::print::with_forced_impl_filename_line!(do_describe(tcx, key))
256     );
257     let description =
258         if tcx.sess.verbose() { format!("{} [{}]", description, name) } else { description };
259     let span = if kind == dep_graph::DepKind::def_span {
260         // The `def_span` query is used to calculate `default_span`,
261         // so exit to avoid infinite recursion.
262         None
263     } else {
264         Some(key.default_span(*tcx))
265     };
266     let def_kind = if kind == dep_graph::DepKind::opt_def_kind {
267         // Try to avoid infinite recursion.
268         None
269     } else {
270         key.key_as_def_id()
271             .and_then(|def_id| def_id.as_local())
272             .and_then(|def_id| tcx.opt_def_kind(def_id))
273     };
274     let hash = || {
275         tcx.with_stable_hashing_context(|mut hcx| {
276             let mut hasher = StableHasher::new();
277             std::mem::discriminant(&kind).hash_stable(&mut hcx, &mut hasher);
278             key.hash_stable(&mut hcx, &mut hasher);
279             hasher.finish::<u64>()
280         })
281     };
282
283     QueryStackFrame::new(name, description, span, def_kind, hash)
284 }
285
286 // NOTE: `$V` isn't used here, but we still need to match on it so it can be passed to other macros
287 // invoked by `rustc_query_append`.
288 macro_rules! define_queries {
289     (
290      $($(#[$attr:meta])*
291         [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
292         define_queries_struct! {
293             input: ($(([$($modifiers)*] [$($attr)*] [$name]))*)
294         }
295
296         mod make_query {
297             use super::*;
298
299             // Create an eponymous constructor for each query.
300             $(#[allow(nonstandard_style)] $(#[$attr])*
301             pub fn $name<'tcx>(tcx: QueryCtxt<'tcx>, key: <queries::$name<'tcx> as QueryConfig>::Key) -> QueryStackFrame {
302                 let kind = dep_graph::DepKind::$name;
303                 let name = stringify!($name);
304                 $crate::plumbing::create_query_frame(tcx, queries::$name::describe, key, kind, name)
305             })*
306         }
307
308         #[allow(nonstandard_style)]
309         mod queries {
310             use std::marker::PhantomData;
311
312             $(pub struct $name<'tcx> {
313                 data: PhantomData<&'tcx ()>
314             })*
315         }
316
317         $(impl<'tcx> QueryConfig for queries::$name<'tcx> {
318             type Key = query_keys::$name<'tcx>;
319             type Value = query_values::$name<'tcx>;
320             type Stored = query_stored::$name<'tcx>;
321             const NAME: &'static str = stringify!($name);
322         }
323
324         impl<'tcx> QueryDescription<QueryCtxt<'tcx>> for queries::$name<'tcx> {
325             rustc_query_description! { $name<'tcx> }
326
327             type Cache = query_storage::$name<'tcx>;
328
329             #[inline(always)]
330             fn query_state<'a>(tcx: QueryCtxt<'tcx>) -> &'a QueryState<Self::Key>
331                 where QueryCtxt<'tcx>: 'a
332             {
333                 &tcx.queries.$name
334             }
335
336             #[inline(always)]
337             fn query_cache<'a>(tcx: QueryCtxt<'tcx>) -> &'a Self::Cache
338                 where 'tcx:'a
339             {
340                 &tcx.query_caches.$name
341             }
342
343             #[inline]
344             fn make_vtable(tcx: QueryCtxt<'tcx>, key: &Self::Key) ->
345                 QueryVTable<QueryCtxt<'tcx>, Self::Key, Self::Value>
346             {
347                 let compute = get_provider!([$($modifiers)*][tcx, $name, key]);
348                 let cache_on_disk = Self::cache_on_disk(tcx.tcx, key);
349                 QueryVTable {
350                     anon: is_anon!([$($modifiers)*]),
351                     eval_always: is_eval_always!([$($modifiers)*]),
352                     dep_kind: dep_graph::DepKind::$name,
353                     hash_result: hash_result!([$($modifiers)*]),
354                     handle_cycle_error: |tcx, mut error| handle_cycle_error!([$($modifiers)*][tcx, error]),
355                     compute,
356                     cache_on_disk,
357                     try_load_from_disk: Self::TRY_LOAD_FROM_DISK,
358                 }
359             }
360         })*
361
362         #[allow(nonstandard_style)]
363         mod query_callbacks {
364             use super::*;
365             use rustc_middle::dep_graph::DepNode;
366             use rustc_query_system::dep_graph::DepNodeParams;
367             use rustc_query_system::query::{force_query, QueryDescription};
368             use rustc_query_system::dep_graph::FingerprintStyle;
369
370             // We use this for most things when incr. comp. is turned off.
371             pub fn Null() -> DepKindStruct {
372                 DepKindStruct {
373                     is_anon: false,
374                     is_eval_always: false,
375                     fingerprint_style: FingerprintStyle::Unit,
376                     force_from_dep_node: Some(|_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node)),
377                     try_load_from_on_disk_cache: None,
378                 }
379             }
380
381             // We use this for the forever-red node.
382             pub fn Red() -> DepKindStruct {
383                 DepKindStruct {
384                     is_anon: false,
385                     is_eval_always: false,
386                     fingerprint_style: FingerprintStyle::Unit,
387                     force_from_dep_node: Some(|_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node)),
388                     try_load_from_on_disk_cache: None,
389                 }
390             }
391
392             pub fn TraitSelect() -> DepKindStruct {
393                 DepKindStruct {
394                     is_anon: true,
395                     is_eval_always: false,
396                     fingerprint_style: FingerprintStyle::Unit,
397                     force_from_dep_node: None,
398                     try_load_from_on_disk_cache: None,
399                 }
400             }
401
402             pub fn CompileCodegenUnit() -> DepKindStruct {
403                 DepKindStruct {
404                     is_anon: false,
405                     is_eval_always: false,
406                     fingerprint_style: FingerprintStyle::Opaque,
407                     force_from_dep_node: None,
408                     try_load_from_on_disk_cache: None,
409                 }
410             }
411
412             pub fn CompileMonoItem() -> DepKindStruct {
413                 DepKindStruct {
414                     is_anon: false,
415                     is_eval_always: false,
416                     fingerprint_style: FingerprintStyle::Opaque,
417                     force_from_dep_node: None,
418                     try_load_from_on_disk_cache: None,
419                 }
420             }
421
422             $(pub(crate) fn $name()-> DepKindStruct {
423                 let is_anon = is_anon!([$($modifiers)*]);
424                 let is_eval_always = is_eval_always!([$($modifiers)*]);
425
426                 let fingerprint_style =
427                     <<queries::$name<'_> as QueryConfig>::Key as DepNodeParams<TyCtxt<'_>>>::fingerprint_style();
428
429                 if is_anon || !fingerprint_style.reconstructible() {
430                     return DepKindStruct {
431                         is_anon,
432                         is_eval_always,
433                         fingerprint_style,
434                         force_from_dep_node: None,
435                         try_load_from_on_disk_cache: None,
436                     }
437                 }
438
439                 #[inline(always)]
440                 fn recover<'tcx>(tcx: TyCtxt<'tcx>, dep_node: DepNode) -> Option<<queries::$name<'tcx> as QueryConfig>::Key> {
441                     <<queries::$name<'_> as QueryConfig>::Key as DepNodeParams<TyCtxt<'_>>>::recover(tcx, &dep_node)
442                 }
443
444                 fn force_from_dep_node(tcx: TyCtxt<'_>, dep_node: DepNode) -> bool {
445                     if let Some(key) = recover(tcx, dep_node) {
446                         #[cfg(debug_assertions)]
447                         let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
448                         let tcx = QueryCtxt::from_tcx(tcx);
449                         force_query::<queries::$name<'_>, _>(tcx, key, dep_node);
450                         true
451                     } else {
452                         false
453                     }
454                 }
455
456                 fn try_load_from_on_disk_cache(tcx: TyCtxt<'_>, dep_node: DepNode) {
457                     debug_assert!(tcx.dep_graph.is_green(&dep_node));
458
459                     let key = recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash));
460                     if queries::$name::cache_on_disk(tcx, &key) {
461                         let _ = tcx.$name(key);
462                     }
463                 }
464
465                 DepKindStruct {
466                     is_anon,
467                     is_eval_always,
468                     fingerprint_style,
469                     force_from_dep_node: Some(force_from_dep_node),
470                     try_load_from_on_disk_cache: Some(try_load_from_on_disk_cache),
471                 }
472             })*
473         }
474
475         pub fn query_callbacks<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindStruct] {
476             arena.alloc_from_iter(make_dep_kind_array!(query_callbacks))
477         }
478     }
479 }
480
481 macro_rules! define_queries_struct {
482     (
483      input: ($(([$($modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => {
484         pub struct Queries<'tcx> {
485             local_providers: Box<Providers>,
486             extern_providers: Box<ExternProviders>,
487
488             pub on_disk_cache: Option<OnDiskCache<'tcx>>,
489
490             jobs: AtomicU64,
491
492             $($(#[$attr])*  $name: QueryState<<queries::$name<'tcx> as QueryConfig>::Key>,)*
493         }
494
495         impl<'tcx> Queries<'tcx> {
496             pub fn new(
497                 local_providers: Providers,
498                 extern_providers: ExternProviders,
499                 on_disk_cache: Option<OnDiskCache<'tcx>>,
500             ) -> Self {
501                 Queries {
502                     local_providers: Box::new(local_providers),
503                     extern_providers: Box::new(extern_providers),
504                     on_disk_cache,
505                     jobs: AtomicU64::new(1),
506                     $($name: Default::default()),*
507                 }
508             }
509
510             pub(crate) fn try_collect_active_jobs(
511                 &'tcx self,
512                 tcx: TyCtxt<'tcx>,
513             ) -> Option<QueryMap> {
514                 let tcx = QueryCtxt { tcx, queries: self };
515                 let mut jobs = QueryMap::default();
516
517                 $(
518                     self.$name.try_collect_active_jobs(
519                         tcx,
520                         make_query::$name,
521                         &mut jobs,
522                     )?;
523                 )*
524
525                 Some(jobs)
526             }
527         }
528
529         impl<'tcx> QueryEngine<'tcx> for Queries<'tcx> {
530             fn as_any(&'tcx self) -> &'tcx dyn std::any::Any {
531                 let this = unsafe { std::mem::transmute::<&Queries<'_>, &Queries<'_>>(self) };
532                 this as _
533             }
534
535             fn try_mark_green(&'tcx self, tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool {
536                 let qcx = QueryCtxt { tcx, queries: self };
537                 tcx.dep_graph.try_mark_green(qcx, dep_node).is_some()
538             }
539
540             $($(#[$attr])*
541             #[inline(always)]
542             #[tracing::instrument(level = "trace", skip(self, tcx))]
543             fn $name(
544                 &'tcx self,
545                 tcx: TyCtxt<'tcx>,
546                 span: Span,
547                 key: <queries::$name<'tcx> as QueryConfig>::Key,
548                 mode: QueryMode,
549             ) -> Option<query_stored::$name<'tcx>> {
550                 let qcx = QueryCtxt { tcx, queries: self };
551                 get_query::<queries::$name<'tcx>, _>(qcx, span, key, mode)
552             })*
553         }
554     };
555 }