summaryrefslogtreecommitdiff
path: root/compiler/rustc_middle/src/query/plumbing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_middle/src/query/plumbing.rs')
-rw-r--r--compiler/rustc_middle/src/query/plumbing.rs640
1 files changed, 640 insertions, 0 deletions
diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs
new file mode 100644
index 00000000000..647f4826876
--- /dev/null
+++ b/compiler/rustc_middle/src/query/plumbing.rs
@@ -0,0 +1,640 @@
+use crate::dep_graph;
+use crate::dep_graph::DepKind;
+use crate::query::on_disk_cache::CacheEncoder;
+use crate::query::on_disk_cache::EncodedDepNodeIndex;
+use crate::query::on_disk_cache::OnDiskCache;
+use crate::query::{
+ DynamicQueries, ExternProviders, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates,
+};
+use crate::ty::TyCtxt;
+use field_offset::FieldOffset;
+use measureme::StringId;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::sync::AtomicU64;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::hir_id::OwnerId;
+use rustc_query_system::dep_graph::DepNodeIndex;
+use rustc_query_system::dep_graph::SerializedDepNodeIndex;
+pub(crate) use rustc_query_system::query::QueryJobId;
+use rustc_query_system::query::*;
+use rustc_query_system::HandleCycleError;
+use rustc_span::{Span, DUMMY_SP};
+use std::ops::Deref;
+
+pub struct QueryKeyStringCache {
+ pub def_id_cache: FxHashMap<DefId, StringId>,
+}
+
+impl QueryKeyStringCache {
+ pub fn new() -> QueryKeyStringCache {
+ QueryKeyStringCache { def_id_cache: Default::default() }
+ }
+}
+
+#[derive(Clone, Copy)]
+pub struct QueryStruct<'tcx> {
+ pub try_collect_active_jobs: fn(TyCtxt<'tcx>, &mut QueryMap<DepKind>) -> Option<()>,
+ pub alloc_self_profile_query_strings: fn(TyCtxt<'tcx>, &mut QueryKeyStringCache),
+ pub encode_query_results:
+ Option<fn(TyCtxt<'tcx>, &mut CacheEncoder<'_, 'tcx>, &mut EncodedDepNodeIndex)>,
+}
+
+pub struct DynamicQuery<'tcx, C: QueryCache> {
+ pub name: &'static str,
+ pub eval_always: bool,
+ pub dep_kind: rustc_middle::dep_graph::DepKind,
+ pub handle_cycle_error: HandleCycleError,
+ pub query_state: FieldOffset<QueryStates<'tcx>, QueryState<C::Key, crate::dep_graph::DepKind>>,
+ pub query_cache: FieldOffset<QueryCaches<'tcx>, C>,
+ pub cache_on_disk: fn(tcx: TyCtxt<'tcx>, key: &C::Key) -> bool,
+ pub execute_query: fn(tcx: TyCtxt<'tcx>, k: C::Key) -> C::Value,
+ pub compute: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value,
+ pub can_load_from_disk: bool,
+ pub try_load_from_disk: fn(
+ tcx: TyCtxt<'tcx>,
+ key: &C::Key,
+ prev_index: SerializedDepNodeIndex,
+ index: DepNodeIndex,
+ ) -> Option<C::Value>,
+ pub loadable_from_disk:
+ fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool,
+ pub hash_result: HashResult<C::Value>,
+ pub value_from_cycle_error:
+ fn(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo<crate::dep_graph::DepKind>]) -> C::Value,
+ pub format_value: fn(&C::Value) -> String,
+}
+
+pub struct QuerySystemFns<'tcx> {
+ pub engine: QueryEngine,
+ pub local_providers: Providers,
+ pub extern_providers: ExternProviders,
+ pub query_structs: Vec<QueryStruct<'tcx>>,
+ pub encode_query_results: fn(
+ tcx: TyCtxt<'tcx>,
+ encoder: &mut CacheEncoder<'_, 'tcx>,
+ query_result_index: &mut EncodedDepNodeIndex,
+ ),
+ pub try_mark_green: fn(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool,
+}
+
+pub struct QuerySystem<'tcx> {
+ pub states: QueryStates<'tcx>,
+ pub arenas: QueryArenas<'tcx>,
+ pub caches: QueryCaches<'tcx>,
+ pub dynamic_queries: DynamicQueries<'tcx>,
+
+ /// This provides access to the incremental compilation on-disk cache for query results.
+ /// Do not access this directly. It is only meant to be used by
+ /// `DepGraph::try_mark_green()` and the query infrastructure.
+ /// This is `None` if we are not incremental compilation mode
+ pub on_disk_cache: Option<OnDiskCache<'tcx>>,
+
+ pub fns: QuerySystemFns<'tcx>,
+
+ pub jobs: AtomicU64,
+}
+
+#[derive(Copy, Clone)]
+pub struct TyCtxtAt<'tcx> {
+ pub tcx: TyCtxt<'tcx>,
+ pub span: Span,
+}
+
+impl<'tcx> Deref for TyCtxtAt<'tcx> {
+ type Target = TyCtxt<'tcx>;
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ &self.tcx
+ }
+}
+
+#[derive(Copy, Clone)]
+pub struct TyCtxtEnsure<'tcx> {
+ pub tcx: TyCtxt<'tcx>,
+}
+
+#[derive(Copy, Clone)]
+pub struct TyCtxtEnsureWithValue<'tcx> {
+ pub tcx: TyCtxt<'tcx>,
+}
+
+impl<'tcx> TyCtxt<'tcx> {
+ /// Returns a transparent wrapper for `TyCtxt`, which ensures queries
+ /// are executed instead of just returning their results.
+ #[inline(always)]
+ pub fn ensure(self) -> TyCtxtEnsure<'tcx> {
+ TyCtxtEnsure { tcx: self }
+ }
+
+ /// Returns a transparent wrapper for `TyCtxt`, which ensures queries
+ /// are executed instead of just returning their results.
+ ///
+ /// This version verifies that the computed result exists in the cache before returning.
+ #[inline(always)]
+ pub fn ensure_with_value(self) -> TyCtxtEnsureWithValue<'tcx> {
+ TyCtxtEnsureWithValue { tcx: self }
+ }
+
+ /// Returns a transparent wrapper for `TyCtxt` which uses
+ /// `span` as the location of queries performed through it.
+ #[inline(always)]
+ pub fn at(self, span: Span) -> TyCtxtAt<'tcx> {
+ TyCtxtAt { tcx: self, span }
+ }
+
+ pub fn try_mark_green(self, dep_node: &dep_graph::DepNode) -> bool {
+ (self.query_system.fns.try_mark_green)(self, dep_node)
+ }
+}
+
+#[inline]
+pub fn query_get_at<'tcx, Cache>(
+ tcx: TyCtxt<'tcx>,
+ execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
+ query_cache: &Cache,
+ span: Span,
+ key: Cache::Key,
+) -> Cache::Value
+where
+ Cache: QueryCache,
+{
+ let key = key.into_query_param();
+ match try_get_cached(tcx, query_cache, &key) {
+ Some(value) => value,
+ None => execute_query(tcx, span, key, QueryMode::Get).unwrap(),
+ }
+}
+
+#[inline]
+pub fn query_ensure<'tcx, Cache>(
+ tcx: TyCtxt<'tcx>,
+ execute_query: fn(TyCtxt<'tcx>, Span, Cache::Key, QueryMode) -> Option<Cache::Value>,
+ query_cache: &Cache,
+ key: Cache::Key,
+ check_cache: bool,
+) where
+ Cache: QueryCache,
+{
+ let key = key.into_query_param();
+ if try_get_cached(tcx, query_cache, &key).is_none() {
+ execute_query(tcx, DUMMY_SP, key, QueryMode::Ensure { check_cache });
+ }
+}
+
+macro_rules! query_helper_param_ty {
+ (DefId) => { impl IntoQueryParam<DefId> };
+ (LocalDefId) => { impl IntoQueryParam<LocalDefId> };
+ ($K:ty) => { $K };
+}
+
+macro_rules! query_if_arena {
+ ([] $arena:tt $no_arena:tt) => {
+ $no_arena
+ };
+ ([(arena_cache) $($rest:tt)*] $arena:tt $no_arena:tt) => {
+ $arena
+ };
+ ([$other:tt $($modifiers:tt)*]$($args:tt)*) => {
+ query_if_arena!([$($modifiers)*]$($args)*)
+ };
+}
+
+/// If `separate_provide_if_extern`, then the key can be projected to its
+/// local key via `<$K as AsLocalKey>::LocalKey`.
+macro_rules! local_key_if_separate_extern {
+ ([] $($K:tt)*) => {
+ $($K)*
+ };
+ ([(separate_provide_extern) $($rest:tt)*] $($K:tt)*) => {
+ <$($K)* as AsLocalKey>::LocalKey
+ };
+ ([$other:tt $($modifiers:tt)*] $($K:tt)*) => {
+ local_key_if_separate_extern!([$($modifiers)*] $($K)*)
+ };
+}
+
+macro_rules! separate_provide_extern_decl {
+ ([][$name:ident]) => {
+ ()
+ };
+ ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => {
+ for<'tcx> fn(
+ TyCtxt<'tcx>,
+ query_keys::$name<'tcx>,
+ ) -> query_provided::$name<'tcx>
+ };
+ ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => {
+ separate_provide_extern_decl!([$($modifiers)*][$($args)*])
+ };
+}
+
+macro_rules! separate_provide_extern_default {
+ ([][$name:ident]) => {
+ ()
+ };
+ ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => {
+ |_, key| bug!(
+ "`tcx.{}({:?})` unsupported by its crate; \
+ perhaps the `{}` query was never assigned a provider function",
+ stringify!($name),
+ key,
+ stringify!($name),
+ )
+ };
+ ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => {
+ separate_provide_extern_default!([$($modifiers)*][$($args)*])
+ };
+}
+
+macro_rules! define_callbacks {
+ (
+ $($(#[$attr:meta])*
+ [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
+
+ // HACK(eddyb) this is like the `impl QueryConfig for queries::$name`
+ // below, but using type aliases instead of associated types, to bypass
+ // the limitations around normalizing under HRTB - for example, this:
+ // `for<'tcx> fn(...) -> <queries::$name<'tcx> as QueryConfig<TyCtxt<'tcx>>>::Value`
+ // doesn't currently normalize to `for<'tcx> fn(...) -> query_values::$name<'tcx>`.
+ // This is primarily used by the `provide!` macro in `rustc_metadata`.
+ #[allow(nonstandard_style, unused_lifetimes)]
+ pub mod query_keys {
+ use super::*;
+
+ $(pub type $name<'tcx> = $($K)*;)*
+ }
+ #[allow(nonstandard_style, unused_lifetimes)]
+ pub mod query_keys_local {
+ use super::*;
+
+ $(pub type $name<'tcx> = local_key_if_separate_extern!([$($modifiers)*] $($K)*);)*
+ }
+ #[allow(nonstandard_style, unused_lifetimes)]
+ pub mod query_values {
+ use super::*;
+
+ $(pub type $name<'tcx> = $V;)*
+ }
+
+ /// This module specifies the type returned from query providers and the type used for
+ /// decoding. For regular queries this is the declared returned type `V`, but
+ /// `arena_cache` will use `<V as Deref>::Target` instead.
+ #[allow(nonstandard_style, unused_lifetimes)]
+ pub mod query_provided {
+ use super::*;
+
+ $(
+ pub type $name<'tcx> = query_if_arena!([$($modifiers)*] (<$V as Deref>::Target) ($V));
+ )*
+ }
+
+ /// This module has a function per query which takes a `query_provided` value and coverts
+ /// it to a regular `V` value by allocating it on an arena if the query has the
+ /// `arena_cache` modifier. This will happen when computing the query using a provider or
+ /// decoding a stored result.
+ #[allow(nonstandard_style, unused_lifetimes)]
+ pub mod query_provided_to_value {
+ use super::*;
+
+ $(
+ #[inline(always)]
+ pub fn $name<'tcx>(
+ _tcx: TyCtxt<'tcx>,
+ value: query_provided::$name<'tcx>,
+ ) -> Erase<query_values::$name<'tcx>> {
+ erase(query_if_arena!([$($modifiers)*]
+ {
+ if mem::needs_drop::<query_provided::$name<'tcx>>() {
+ &*_tcx.query_system.arenas.$name.alloc(value)
+ } else {
+ &*_tcx.arena.dropless.alloc(value)
+ }
+ }
+ (value)
+ ))
+ }
+ )*
+ }
+ #[allow(nonstandard_style, unused_lifetimes)]
+ pub mod query_storage {
+ use super::*;
+
+ $(
+ pub type $name<'tcx> = <<$($K)* as Key>::CacheSelector as CacheSelector<'tcx, Erase<$V>>>::Cache;
+ )*
+ }
+
+ $(
+ // Ensure that keys grow no larger than 64 bytes
+ #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+ const _: () = {
+ if mem::size_of::<query_keys::$name<'static>>() > 64 {
+ panic!("{}", concat!(
+ "the query `",
+ stringify!($name),
+ "` has a key type `",
+ stringify!($($K)*),
+ "` that is too large"
+ ));
+ }
+ };
+
+ // Ensure that values grow no larger than 64 bytes
+ #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+ const _: () = {
+ if mem::size_of::<query_values::$name<'static>>() > 64 {
+ panic!("{}", concat!(
+ "the query `",
+ stringify!($name),
+ "` has a value type `",
+ stringify!($V),
+ "` that is too large"
+ ));
+ }
+ };
+ )*
+
+ pub struct QueryArenas<'tcx> {
+ $($(#[$attr])* pub $name: query_if_arena!([$($modifiers)*]
+ (WorkerLocal<TypedArena<<$V as Deref>::Target>>)
+ ()
+ ),)*
+ }
+
+ impl Default for QueryArenas<'_> {
+ fn default() -> Self {
+ Self {
+ $($name: query_if_arena!([$($modifiers)*]
+ (WorkerLocal::new(|_| Default::default()))
+ ()
+ ),)*
+ }
+ }
+ }
+
+ #[derive(Default)]
+ pub struct QueryCaches<'tcx> {
+ $($(#[$attr])* pub $name: query_storage::$name<'tcx>,)*
+ }
+
+ impl<'tcx> TyCtxtEnsure<'tcx> {
+ $($(#[$attr])*
+ #[inline(always)]
+ pub fn $name(self, key: query_helper_param_ty!($($K)*)) {
+ query_ensure(
+ self.tcx,
+ self.tcx.query_system.fns.engine.$name,
+ &self.tcx.query_system.caches.$name,
+ key.into_query_param(),
+ false,
+ );
+ })*
+ }
+
+ impl<'tcx> TyCtxtEnsureWithValue<'tcx> {
+ $($(#[$attr])*
+ #[inline(always)]
+ pub fn $name(self, key: query_helper_param_ty!($($K)*)) {
+ query_ensure(
+ self.tcx,
+ self.tcx.query_system.fns.engine.$name,
+ &self.tcx.query_system.caches.$name,
+ key.into_query_param(),
+ true,
+ );
+ })*
+ }
+
+ impl<'tcx> TyCtxt<'tcx> {
+ $($(#[$attr])*
+ #[inline(always)]
+ #[must_use]
+ pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V
+ {
+ self.at(DUMMY_SP).$name(key)
+ })*
+ }
+
+ impl<'tcx> TyCtxtAt<'tcx> {
+ $($(#[$attr])*
+ #[inline(always)]
+ pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V
+ {
+ restore::<$V>(query_get_at(
+ self.tcx,
+ self.tcx.query_system.fns.engine.$name,
+ &self.tcx.query_system.caches.$name,
+ self.span,
+ key.into_query_param(),
+ ))
+ })*
+ }
+
+ pub struct DynamicQueries<'tcx> {
+ $(
+ pub $name: DynamicQuery<'tcx, query_storage::$name<'tcx>>,
+ )*
+ }
+
+ #[derive(Default)]
+ pub struct QueryStates<'tcx> {
+ $(
+ pub $name: QueryState<$($K)*, DepKind>,
+ )*
+ }
+
+ pub struct Providers {
+ $(pub $name: for<'tcx> fn(
+ TyCtxt<'tcx>,
+ query_keys_local::$name<'tcx>,
+ ) -> query_provided::$name<'tcx>,)*
+ }
+
+ pub struct ExternProviders {
+ $(pub $name: separate_provide_extern_decl!([$($modifiers)*][$name]),)*
+ }
+
+ impl Default for Providers {
+ fn default() -> Self {
+ Providers {
+ $($name: |_, key| bug!(
+ "`tcx.{}({:?})` is not supported for this key;\n\
+ hint: Queries can be either made to the local crate, or the external crate. \
+ This error means you tried to use it for one that's not supported.\n\
+ If that's not the case, {} was likely never assigned to a provider function.\n",
+ stringify!($name),
+ key,
+ stringify!($name),
+ ),)*
+ }
+ }
+ }
+
+ impl Default for ExternProviders {
+ fn default() -> Self {
+ ExternProviders {
+ $($name: separate_provide_extern_default!([$($modifiers)*][$name]),)*
+ }
+ }
+ }
+
+ impl Copy for Providers {}
+ impl Clone for Providers {
+ fn clone(&self) -> Self { *self }
+ }
+
+ impl Copy for ExternProviders {}
+ impl Clone for ExternProviders {
+ fn clone(&self) -> Self { *self }
+ }
+
+ pub struct QueryEngine {
+ $(pub $name: for<'tcx> fn(
+ TyCtxt<'tcx>,
+ Span,
+ query_keys::$name<'tcx>,
+ QueryMode,
+ ) -> Option<Erase<$V>>,)*
+ }
+ };
+}
+
+macro_rules! hash_result {
+ ([]) => {{
+ Some(dep_graph::hash_result)
+ }};
+ ([(no_hash) $($rest:tt)*]) => {{
+ None
+ }};
+ ([$other:tt $($modifiers:tt)*]) => {
+ hash_result!([$($modifiers)*])
+ };
+}
+
+macro_rules! define_feedable {
+ ($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
+ $(impl<'tcx, K: IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> {
+ $(#[$attr])*
+ #[inline(always)]
+ pub fn $name(self, value: query_provided::$name<'tcx>) {
+ let key = self.key().into_query_param();
+
+ let tcx = self.tcx;
+ let erased = query_provided_to_value::$name(tcx, value);
+ let value = restore::<$V>(erased);
+ let cache = &tcx.query_system.caches.$name;
+
+ let hasher: Option<fn(&mut StableHashingContext<'_>, &_) -> _> = hash_result!([$($modifiers)*]);
+ match try_get_cached(tcx, cache, &key) {
+ Some(old) => {
+ let old = restore::<$V>(old);
+ if let Some(hasher) = hasher {
+ let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx.with_stable_hashing_context(|mut hcx|
+ (hasher(&mut hcx, &value), hasher(&mut hcx, &old))
+ );
+ assert_eq!(
+ old_hash, value_hash,
+ "Trying to feed an already recorded value for query {} key={key:?}:\nold value: {old:?}\nnew value: {value:?}",
+ stringify!($name),
+ )
+ } else {
+ bug!(
+ "Trying to feed an already recorded value for query {} key={key:?}:\nold value: {old:?}\nnew value: {value:?}",
+ stringify!($name),
+ )
+ }
+ }
+ None => {
+ let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::DepKind::$name, &key);
+ let dep_node_index = tcx.dep_graph.with_feed_task(
+ dep_node,
+ tcx,
+ key,
+ &value,
+ hash_result!([$($modifiers)*]),
+ );
+ cache.complete(key, erased, dep_node_index);
+ }
+ }
+ }
+ })*
+ }
+}
+
+// Each of these queries corresponds to a function pointer field in the
+// `Providers` struct for requesting a value of that type, and a method
+// on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way
+// which memoizes and does dep-graph tracking, wrapping around the actual
+// `Providers` that the driver creates (using several `rustc_*` crates).
+//
+// The result type of each query must implement `Clone`, and additionally
+// `ty::query::values::Value`, which produces an appropriate placeholder
+// (error) value if the query resulted in a query cycle.
+// Queries marked with `fatal_cycle` do not need the latter implementation,
+// as they will raise an fatal error on query cycles instead.
+
+mod sealed {
+ use super::{DefId, LocalDefId, OwnerId};
+
+ /// An analogue of the `Into` trait that's intended only for query parameters.
+ ///
+ /// This exists to allow queries to accept either `DefId` or `LocalDefId` while requiring that the
+ /// user call `to_def_id` to convert between them everywhere else.
+ pub trait IntoQueryParam<P> {
+ fn into_query_param(self) -> P;
+ }
+
+ impl<P> IntoQueryParam<P> for P {
+ #[inline(always)]
+ fn into_query_param(self) -> P {
+ self
+ }
+ }
+
+ impl<'a, P: Copy> IntoQueryParam<P> for &'a P {
+ #[inline(always)]
+ fn into_query_param(self) -> P {
+ *self
+ }
+ }
+
+ impl IntoQueryParam<LocalDefId> for OwnerId {
+ #[inline(always)]
+ fn into_query_param(self) -> LocalDefId {
+ self.def_id
+ }
+ }
+
+ impl IntoQueryParam<DefId> for LocalDefId {
+ #[inline(always)]
+ fn into_query_param(self) -> DefId {
+ self.to_def_id()
+ }
+ }
+
+ impl IntoQueryParam<DefId> for OwnerId {
+ #[inline(always)]
+ fn into_query_param(self) -> DefId {
+ self.to_def_id()
+ }
+ }
+}
+
+pub use sealed::IntoQueryParam;
+
+impl<'tcx> TyCtxt<'tcx> {
+ pub fn def_kind(self, def_id: impl IntoQueryParam<DefId>) -> DefKind {
+ let def_id = def_id.into_query_param();
+ self.opt_def_kind(def_id)
+ .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id))
+ }
+}
+
+impl<'tcx> TyCtxtAt<'tcx> {
+ pub fn def_kind(self, def_id: impl IntoQueryParam<DefId>) -> DefKind {
+ let def_id = def_id.into_query_param();
+ self.opt_def_kind(def_id)
+ .unwrap_or_else(|| bug!("def_kind: unsupported node: {:?}", def_id))
+ }
+}