summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_query_impl/src/plumbing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_query_impl/src/plumbing.rs')
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs558
1 files changed, 558 insertions, 0 deletions
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
new file mode 100644
index 000000000..eda4401c8
--- /dev/null
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -0,0 +1,558 @@
+//! The implementation of the query system itself. This defines the macros that
+//! generate the actual methods on tcx which find and execute the provider,
+//! manage the caches, and so forth.
+
+use crate::{on_disk_cache, Queries};
+use rustc_middle::dep_graph::{DepNodeIndex, SerializedDepNodeIndex};
+use rustc_middle::ty::tls::{self, ImplicitCtxt};
+use rustc_middle::ty::TyCtxt;
+use rustc_query_system::dep_graph::HasDepContext;
+use rustc_query_system::query::{QueryContext, QueryJobId, QueryMap, QuerySideEffects};
+
+use rustc_data_structures::sync::Lock;
+use rustc_data_structures::thin_vec::ThinVec;
+use rustc_errors::{Diagnostic, Handler};
+
+use std::any::Any;
+use std::num::NonZeroU64;
+
+#[derive(Copy, Clone)]
+pub struct QueryCtxt<'tcx> {
+ pub tcx: TyCtxt<'tcx>,
+ pub queries: &'tcx Queries<'tcx>,
+}
+
+impl<'tcx> std::ops::Deref for QueryCtxt<'tcx> {
+ type Target = TyCtxt<'tcx>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.tcx
+ }
+}
+
+impl<'tcx> HasDepContext for QueryCtxt<'tcx> {
+ type DepKind = rustc_middle::dep_graph::DepKind;
+ type DepContext = TyCtxt<'tcx>;
+
+ #[inline]
+ fn dep_context(&self) -> &Self::DepContext {
+ &self.tcx
+ }
+}
+
+impl QueryContext for QueryCtxt<'_> {
+ fn next_job_id(&self) -> QueryJobId {
+ QueryJobId(
+ NonZeroU64::new(
+ self.queries.jobs.fetch_add(1, rustc_data_structures::sync::Ordering::Relaxed),
+ )
+ .unwrap(),
+ )
+ }
+
+ fn current_query_job(&self) -> Option<QueryJobId> {
+ tls::with_related_context(**self, |icx| icx.query)
+ }
+
+ fn try_collect_active_jobs(&self) -> Option<QueryMap> {
+ self.queries.try_collect_active_jobs(**self)
+ }
+
+ // Interactions with on_disk_cache
+ fn load_side_effects(&self, prev_dep_node_index: SerializedDepNodeIndex) -> QuerySideEffects {
+ self.queries
+ .on_disk_cache
+ .as_ref()
+ .map(|c| c.load_side_effects(**self, prev_dep_node_index))
+ .unwrap_or_default()
+ }
+
+ fn store_side_effects(&self, dep_node_index: DepNodeIndex, side_effects: QuerySideEffects) {
+ if let Some(c) = self.queries.on_disk_cache.as_ref() {
+ c.store_side_effects(dep_node_index, side_effects)
+ }
+ }
+
+ fn store_side_effects_for_anon_node(
+ &self,
+ dep_node_index: DepNodeIndex,
+ side_effects: QuerySideEffects,
+ ) {
+ if let Some(c) = self.queries.on_disk_cache.as_ref() {
+ c.store_side_effects_for_anon_node(dep_node_index, side_effects)
+ }
+ }
+
+ /// Executes a job by changing the `ImplicitCtxt` to point to the
+ /// new query job while it executes. It returns the diagnostics
+ /// captured during execution and the actual result.
+ #[inline(always)]
+ fn start_query<R>(
+ &self,
+ token: QueryJobId,
+ diagnostics: Option<&Lock<ThinVec<Diagnostic>>>,
+ compute: impl FnOnce() -> R,
+ ) -> R {
+ // The `TyCtxt` stored in TLS has the same global interner lifetime
+ // as `self`, so we use `with_related_context` to relate the 'tcx lifetimes
+ // when accessing the `ImplicitCtxt`.
+ tls::with_related_context(**self, move |current_icx| {
+ // Update the `ImplicitCtxt` to point to our new query job.
+ let new_icx = ImplicitCtxt {
+ tcx: **self,
+ query: Some(token),
+ diagnostics,
+ layout_depth: current_icx.layout_depth,
+ task_deps: current_icx.task_deps,
+ };
+
+ // Use the `ImplicitCtxt` while we execute the query.
+ tls::enter_context(&new_icx, |_| {
+ rustc_data_structures::stack::ensure_sufficient_stack(compute)
+ })
+ })
+ }
+}
+
+impl<'tcx> QueryCtxt<'tcx> {
+ #[inline]
+ pub fn from_tcx(tcx: TyCtxt<'tcx>) -> Self {
+ let queries = tcx.queries.as_any();
+ let queries = unsafe {
+ let queries = std::mem::transmute::<&dyn Any, &dyn Any>(queries);
+ let queries = queries.downcast_ref().unwrap();
+ let queries = std::mem::transmute::<&Queries<'_>, &Queries<'_>>(queries);
+ queries
+ };
+ QueryCtxt { tcx, queries }
+ }
+
+ pub(crate) fn on_disk_cache(self) -> Option<&'tcx on_disk_cache::OnDiskCache<'tcx>> {
+ self.queries.on_disk_cache.as_ref()
+ }
+
+ pub(super) fn encode_query_results(
+ self,
+ encoder: &mut on_disk_cache::CacheEncoder<'_, 'tcx>,
+ query_result_index: &mut on_disk_cache::EncodedDepNodeIndex,
+ ) {
+ macro_rules! encode_queries {
+ ($($query:ident,)*) => {
+ $(
+ on_disk_cache::encode_query_results::<_, super::queries::$query<'_>>(
+ self,
+ encoder,
+ query_result_index
+ );
+ )*
+ }
+ }
+
+ rustc_cached_queries!(encode_queries!);
+ }
+
+ pub fn try_print_query_stack(
+ self,
+ query: Option<QueryJobId>,
+ handler: &Handler,
+ num_frames: Option<usize>,
+ ) -> usize {
+ rustc_query_system::query::print_query_stack(self, query, handler, num_frames)
+ }
+}
+
+macro_rules! handle_cycle_error {
+ ([][$tcx: expr, $error:expr]) => {{
+ $error.emit();
+ Value::from_cycle_error($tcx)
+ }};
+ ([(fatal_cycle) $($rest:tt)*][$tcx:expr, $error:expr]) => {{
+ $error.emit();
+ $tcx.sess.abort_if_errors();
+ unreachable!()
+ }};
+ ([(cycle_delay_bug) $($rest:tt)*][$tcx:expr, $error:expr]) => {{
+ $error.delay_as_bug();
+ Value::from_cycle_error($tcx)
+ }};
+ ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => {
+ handle_cycle_error!([$($modifiers)*][$($args)*])
+ };
+}
+
+macro_rules! is_anon {
+ ([]) => {{
+ false
+ }};
+ ([(anon) $($rest:tt)*]) => {{
+ true
+ }};
+ ([$other:tt $($modifiers:tt)*]) => {
+ is_anon!([$($modifiers)*])
+ };
+}
+
+macro_rules! is_eval_always {
+ ([]) => {{
+ false
+ }};
+ ([(eval_always) $($rest:tt)*]) => {{
+ true
+ }};
+ ([$other:tt $($modifiers:tt)*]) => {
+ is_eval_always!([$($modifiers)*])
+ };
+}
+
+macro_rules! hash_result {
+ ([]) => {{
+ Some(dep_graph::hash_result)
+ }};
+ ([(no_hash) $($rest:tt)*]) => {{
+ None
+ }};
+ ([$other:tt $($modifiers:tt)*]) => {
+ hash_result!([$($modifiers)*])
+ };
+}
+
+macro_rules! get_provider {
+ ([][$tcx:expr, $name:ident, $key:expr]) => {{
+ $tcx.queries.local_providers.$name
+ }};
+ ([(separate_provide_extern) $($rest:tt)*][$tcx:expr, $name:ident, $key:expr]) => {{
+ if $key.query_crate_is_local() {
+ $tcx.queries.local_providers.$name
+ } else {
+ $tcx.queries.extern_providers.$name
+ }
+ }};
+ ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => {
+ get_provider!([$($modifiers)*][$($args)*])
+ };
+}
+
+macro_rules! opt_remap_env_constness {
+ ([][$name:ident]) => {};
+ ([(remap_env_constness) $($rest:tt)*][$name:ident]) => {
+ let $name = $name.without_const();
+ };
+ ([$other:tt $($modifiers:tt)*][$name:ident]) => {
+ opt_remap_env_constness!([$($modifiers)*][$name])
+ };
+}
+
+macro_rules! define_queries {
+ (<$tcx:tt>
+ $($(#[$attr:meta])*
+ [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
+
+ define_queries_struct! {
+ tcx: $tcx,
+ input: ($(([$($modifiers)*] [$($attr)*] [$name]))*)
+ }
+
+ mod make_query {
+ use super::*;
+
+ // Create an eponymous constructor for each query.
+ $(#[allow(nonstandard_style)] $(#[$attr])*
+ pub fn $name<$tcx>(tcx: QueryCtxt<$tcx>, key: query_keys::$name<$tcx>) -> QueryStackFrame {
+ opt_remap_env_constness!([$($modifiers)*][key]);
+ let kind = dep_graph::DepKind::$name;
+ let name = stringify!($name);
+ // Disable visible paths printing for performance reasons.
+ // Showing visible path instead of any path is not that important in production.
+ let description = ty::print::with_no_visible_paths!(
+ // Force filename-line mode to avoid invoking `type_of` query.
+ ty::print::with_forced_impl_filename_line!(
+ queries::$name::describe(tcx, key)
+ )
+ );
+ let description = if tcx.sess.verbose() {
+ format!("{} [{}]", description, name)
+ } else {
+ description
+ };
+ let span = if kind == dep_graph::DepKind::def_span {
+ // The `def_span` query is used to calculate `default_span`,
+ // so exit to avoid infinite recursion.
+ None
+ } else {
+ Some(key.default_span(*tcx))
+ };
+ let def_kind = if kind == dep_graph::DepKind::opt_def_kind {
+ // Try to avoid infinite recursion.
+ None
+ } else {
+ key.key_as_def_id()
+ .and_then(|def_id| def_id.as_local())
+ .and_then(|def_id| tcx.opt_def_kind(def_id))
+ };
+ let hash = || {
+ tcx.with_stable_hashing_context(|mut hcx|{
+ let mut hasher = StableHasher::new();
+ std::mem::discriminant(&kind).hash_stable(&mut hcx, &mut hasher);
+ key.hash_stable(&mut hcx, &mut hasher);
+ hasher.finish::<u64>()
+ })
+ };
+
+ QueryStackFrame::new(name, description, span, def_kind, hash)
+ })*
+ }
+
+ #[allow(nonstandard_style)]
+ mod queries {
+ use std::marker::PhantomData;
+
+ $(pub struct $name<$tcx> {
+ data: PhantomData<&$tcx ()>
+ })*
+ }
+
+ $(impl<$tcx> QueryConfig for queries::$name<$tcx> {
+ type Key = query_keys::$name<$tcx>;
+ type Value = query_values::$name<$tcx>;
+ type Stored = query_stored::$name<$tcx>;
+ const NAME: &'static str = stringify!($name);
+ }
+
+ impl<$tcx> QueryDescription<QueryCtxt<$tcx>> for queries::$name<$tcx> {
+ rustc_query_description! { $name<$tcx> }
+
+ type Cache = query_storage::$name<$tcx>;
+
+ #[inline(always)]
+ fn query_state<'a>(tcx: QueryCtxt<$tcx>) -> &'a QueryState<Self::Key>
+ where QueryCtxt<$tcx>: 'a
+ {
+ &tcx.queries.$name
+ }
+
+ #[inline(always)]
+ fn query_cache<'a>(tcx: QueryCtxt<$tcx>) -> &'a Self::Cache
+ where 'tcx:'a
+ {
+ &tcx.query_caches.$name
+ }
+
+ #[inline]
+ fn make_vtable(tcx: QueryCtxt<'tcx>, key: &Self::Key) ->
+ QueryVTable<QueryCtxt<$tcx>, Self::Key, Self::Value>
+ {
+ let compute = get_provider!([$($modifiers)*][tcx, $name, key]);
+ let cache_on_disk = Self::cache_on_disk(tcx.tcx, key);
+ QueryVTable {
+ anon: is_anon!([$($modifiers)*]),
+ eval_always: is_eval_always!([$($modifiers)*]),
+ dep_kind: dep_graph::DepKind::$name,
+ hash_result: hash_result!([$($modifiers)*]),
+ handle_cycle_error: |tcx, mut error| handle_cycle_error!([$($modifiers)*][tcx, error]),
+ compute,
+ cache_on_disk,
+ try_load_from_disk: Self::TRY_LOAD_FROM_DISK,
+ }
+ }
+ })*
+
+ #[allow(nonstandard_style)]
+ mod query_callbacks {
+ use super::*;
+ use rustc_middle::dep_graph::DepNode;
+ use rustc_middle::ty::query::query_keys;
+ use rustc_query_system::dep_graph::DepNodeParams;
+ use rustc_query_system::query::{force_query, QueryDescription};
+ use rustc_query_system::dep_graph::FingerprintStyle;
+
+ // We use this for most things when incr. comp. is turned off.
+ pub fn Null() -> DepKindStruct {
+ DepKindStruct {
+ is_anon: false,
+ is_eval_always: false,
+ fingerprint_style: FingerprintStyle::Unit,
+ force_from_dep_node: Some(|_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node)),
+ try_load_from_on_disk_cache: None,
+ }
+ }
+
+ // We use this for the forever-red node.
+ pub fn Red() -> DepKindStruct {
+ DepKindStruct {
+ is_anon: false,
+ is_eval_always: false,
+ fingerprint_style: FingerprintStyle::Unit,
+ force_from_dep_node: Some(|_, dep_node| bug!("force_from_dep_node: encountered {:?}", dep_node)),
+ try_load_from_on_disk_cache: None,
+ }
+ }
+
+ pub fn TraitSelect() -> DepKindStruct {
+ DepKindStruct {
+ is_anon: true,
+ is_eval_always: false,
+ fingerprint_style: FingerprintStyle::Unit,
+ force_from_dep_node: None,
+ try_load_from_on_disk_cache: None,
+ }
+ }
+
+ pub fn CompileCodegenUnit() -> DepKindStruct {
+ DepKindStruct {
+ is_anon: false,
+ is_eval_always: false,
+ fingerprint_style: FingerprintStyle::Opaque,
+ force_from_dep_node: None,
+ try_load_from_on_disk_cache: None,
+ }
+ }
+
+ pub fn CompileMonoItem() -> DepKindStruct {
+ DepKindStruct {
+ is_anon: false,
+ is_eval_always: false,
+ fingerprint_style: FingerprintStyle::Opaque,
+ force_from_dep_node: None,
+ try_load_from_on_disk_cache: None,
+ }
+ }
+
+ $(pub(crate) fn $name()-> DepKindStruct {
+ let is_anon = is_anon!([$($modifiers)*]);
+ let is_eval_always = is_eval_always!([$($modifiers)*]);
+
+ let fingerprint_style =
+ <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>>::fingerprint_style();
+
+ if is_anon || !fingerprint_style.reconstructible() {
+ return DepKindStruct {
+ is_anon,
+ is_eval_always,
+ fingerprint_style,
+ force_from_dep_node: None,
+ try_load_from_on_disk_cache: None,
+ }
+ }
+
+ #[inline(always)]
+ fn recover<'tcx>(tcx: TyCtxt<'tcx>, dep_node: DepNode) -> Option<query_keys::$name<'tcx>> {
+ <query_keys::$name<'_> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, &dep_node)
+ }
+
+ fn force_from_dep_node(tcx: TyCtxt<'_>, dep_node: DepNode) -> bool {
+ if let Some(key) = recover(tcx, dep_node) {
+ #[cfg(debug_assertions)]
+ let _guard = tracing::span!(tracing::Level::TRACE, stringify!($name), ?key).entered();
+ let tcx = QueryCtxt::from_tcx(tcx);
+ force_query::<queries::$name<'_>, _>(tcx, key, dep_node);
+ true
+ } else {
+ false
+ }
+ }
+
+ fn try_load_from_on_disk_cache(tcx: TyCtxt<'_>, dep_node: DepNode) {
+ debug_assert!(tcx.dep_graph.is_green(&dep_node));
+
+ let key = recover(tcx, dep_node).unwrap_or_else(|| panic!("Failed to recover key for {:?} with hash {}", dep_node, dep_node.hash));
+ if queries::$name::cache_on_disk(tcx, &key) {
+ let _ = tcx.$name(key);
+ }
+ }
+
+ DepKindStruct {
+ is_anon,
+ is_eval_always,
+ fingerprint_style,
+ force_from_dep_node: Some(force_from_dep_node),
+ try_load_from_on_disk_cache: Some(try_load_from_on_disk_cache),
+ }
+ })*
+ }
+
+ pub fn query_callbacks<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindStruct] {
+ arena.alloc_from_iter(make_dep_kind_array!(query_callbacks))
+ }
+ }
+}
+
+// FIXME(eddyb) this macro (and others?) use `$tcx` and `'tcx` interchangeably.
+// We should either not take `$tcx` at all and use `'tcx` everywhere, or use
+// `$tcx` everywhere (even if that isn't necessary due to lack of hygiene).
+macro_rules! define_queries_struct {
+ (tcx: $tcx:tt,
+ input: ($(([$($modifiers:tt)*] [$($attr:tt)*] [$name:ident]))*)) => {
+ pub struct Queries<$tcx> {
+ local_providers: Box<Providers>,
+ extern_providers: Box<ExternProviders>,
+
+ pub on_disk_cache: Option<OnDiskCache<$tcx>>,
+
+ jobs: AtomicU64,
+
+ $($(#[$attr])* $name: QueryState<query_keys::$name<$tcx>>,)*
+ }
+
+ impl<$tcx> Queries<$tcx> {
+ pub fn new(
+ local_providers: Providers,
+ extern_providers: ExternProviders,
+ on_disk_cache: Option<OnDiskCache<$tcx>>,
+ ) -> Self {
+ Queries {
+ local_providers: Box::new(local_providers),
+ extern_providers: Box::new(extern_providers),
+ on_disk_cache,
+ jobs: AtomicU64::new(1),
+ $($name: Default::default()),*
+ }
+ }
+
+ pub(crate) fn try_collect_active_jobs(
+ &$tcx self,
+ tcx: TyCtxt<$tcx>,
+ ) -> Option<QueryMap> {
+ let tcx = QueryCtxt { tcx, queries: self };
+ let mut jobs = QueryMap::default();
+
+ $(
+ self.$name.try_collect_active_jobs(
+ tcx,
+ make_query::$name,
+ &mut jobs,
+ )?;
+ )*
+
+ Some(jobs)
+ }
+ }
+
+ impl<'tcx> QueryEngine<'tcx> for Queries<'tcx> {
+ fn as_any(&'tcx self) -> &'tcx dyn std::any::Any {
+ let this = unsafe { std::mem::transmute::<&Queries<'_>, &Queries<'_>>(self) };
+ this as _
+ }
+
+ fn try_mark_green(&'tcx self, tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool {
+ let qcx = QueryCtxt { tcx, queries: self };
+ tcx.dep_graph.try_mark_green(qcx, dep_node).is_some()
+ }
+
+ $($(#[$attr])*
+ #[inline(always)]
+ #[tracing::instrument(level = "trace", skip(self, tcx))]
+ fn $name(
+ &'tcx self,
+ tcx: TyCtxt<$tcx>,
+ span: Span,
+ key: query_keys::$name<$tcx>,
+ mode: QueryMode,
+ ) -> Option<query_stored::$name<$tcx>> {
+ opt_remap_env_constness!([$($modifiers)*][key]);
+ let qcx = QueryCtxt { tcx, queries: self };
+ get_query::<queries::$name<$tcx>, _>(qcx, span, key, mode)
+ })*
+ }
+ };
+}