diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 992d61e2b214b..2db4ce6c364e4 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -97,8 +97,7 @@ use crate::mir::interpret::{ use crate::mono::{ CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono, }; -use crate::query::describe_as_module; -use crate::query::plumbing::{define_callbacks, maybe_into_query_key}; +use crate::query::query_api::{define_query_api, maybe_into_query_key}; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, @@ -120,6 +119,15 @@ use crate::ty::{ }; use crate::{mir, thir}; +fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { + let def_id = def_id.into(); + if def_id.is_top_level_module() { + "top-level module".to_string() + } else { + format!("module `{}`", tcx.def_path_str(def_id)) + } +} + // 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 @@ -2835,4 +2843,4 @@ rustc_queries! { non_query Metadata } -rustc_with_all_queries! { define_callbacks! } +rustc_with_all_queries! { define_query_api! } diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/calls.rs similarity index 61% rename from compiler/rustc_middle/src/query/inner.rs rename to compiler/rustc_middle/src/query/calls.rs index a2ffe21ec7926..641c371023272 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/calls.rs @@ -1,13 +1,122 @@ //! Helper functions that serve as the immediate implementation of //! `tcx.$query(..)` and its variations. +use std::ops::Deref; + +use rustc_hir::def_id::LocalDefId; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use crate::dep_graph; use crate::dep_graph::DepNodeKey; use crate::query::erase::{self, Erasable, Erased}; -use crate::query::{QueryCache, QueryMode, QueryVTable}; -use crate::ty::TyCtxt; +use crate::query::{IntoQueryKey, QueryCache, QueryMode, QueryVTable}; +use crate::ty::{self, TyCtxt}; + +#[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)] +#[must_use] +pub struct TyCtxtEnsureOk<'tcx> { + pub tcx: TyCtxt<'tcx>, +} + +#[derive(Copy, Clone)] +#[must_use] +pub struct TyCtxtEnsureResult<'tcx> { + pub tcx: TyCtxt<'tcx>, +} + +#[derive(Copy, Clone)] +#[must_use] +pub struct TyCtxtEnsureDone<'tcx> { + pub tcx: TyCtxt<'tcx>, +} + +impl<'tcx> TyCtxtEnsureOk<'tcx> { + pub fn typeck(self, def_id: impl IntoQueryKey) { + self.typeck_root( + self.tcx.typeck_root_def_id(def_id.into_query_key().to_def_id()).expect_local(), + ) + } +} + +impl<'tcx> TyCtxt<'tcx> { + pub fn typeck(self, def_id: impl IntoQueryKey) -> &'tcx ty::TypeckResults<'tcx> { + self.typeck_root( + self.typeck_root_def_id(def_id.into_query_key().to_def_id()).expect_local(), + ) + } + + /// 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 } + } + + /// FIXME: `ensure_ok`'s effects are subtle. Is this comment fully accurate? + /// + /// Wrapper that calls queries in a special "ensure OK" mode, for callers + /// that don't need the return value and just want to invoke a query for + /// its potential side-effect of emitting fatal errors. + /// + /// This can be more efficient than a normal query call, because if the + /// query's inputs are all green, the call can return immediately without + /// needing to obtain a value (by decoding one from disk or by executing + /// the query). + /// + /// (As with all query calls, execution is also skipped if the query result + /// is already cached in memory.) + /// + /// ## WARNING + /// A subsequent normal call to the same query might still cause it to be + /// executed! This can occur when the inputs are all green, but the query's + /// result is not cached on disk, so the query must be executed to obtain a + /// return value. + /// + /// Therefore, this call mode is not appropriate for callers that want to + /// ensure that the query is _never_ executed in the future. + #[inline(always)] + pub fn ensure_ok(self) -> TyCtxtEnsureOk<'tcx> { + TyCtxtEnsureOk { tcx: self } + } + + /// This is a variant of `ensure_ok` only usable with queries that return + /// `Result<_, ErrorGuaranteed>`. Queries calls through this function will + /// return `Result<(), ErrorGuaranteed>`. I.e. the error status is returned + /// but nothing else. As with `ensure_ok`, this can be more efficient than + /// a normal query call. + #[inline(always)] + pub fn ensure_result(self) -> TyCtxtEnsureResult<'tcx> { + TyCtxtEnsureResult { tcx: self } + } + + /// Wrapper that calls queries where callers don't need the return value and + /// just want to guarantee that the query won't be executed in the future. + /// + /// This is useful for queries that read from a [`Steal`] value, to ensure + /// that they are executed before the query that will steal the value. + /// + /// Currently this causes the query to be executed normally, but this behavior may change. + /// + /// [`Steal`]: rustc_data_structures::steal::Steal + #[inline(always)] + pub fn ensure_done(self) -> TyCtxtEnsureDone<'tcx> { + TyCtxtEnsureDone { tcx: self } + } +} /// Checks whether there is already a value for this key in the in-memory /// query cache, returning that value if present. diff --git a/compiler/rustc_middle/src/query/job.rs b/compiler/rustc_middle/src/query/job.rs index 7f48b2dbdbb22..0f7fb7f6fe9f4 100644 --- a/compiler/rustc_middle/src/query/job.rs +++ b/compiler/rustc_middle/src/query/job.rs @@ -4,9 +4,11 @@ use std::num::NonZero; use std::sync::Arc; use parking_lot::{Condvar, Mutex}; +use rustc_data_structures::hash_table::HashTable; +use rustc_data_structures::sharded::Sharded; use rustc_span::Span; -use crate::query::Cycle; +use crate::queries::TaggedQueryKey; /// A value uniquely identifying an active query job. #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] @@ -50,12 +52,67 @@ impl<'tcx> QueryJob<'tcx> { } } +/// For a particular query and key, tracks the status of a query evaluation +/// that has started, but has not yet finished successfully. +/// +/// (Successful query evaluation for a key is represented by an entry in the +/// query's in-memory cache.) +pub enum ActiveKeyStatus<'tcx> { + /// Some thread is already evaluating the query for this key. + /// + /// The enclosed [`QueryJob`] can be used to wait for it to finish. + Started(QueryJob<'tcx>), + + /// The query panicked. Queries trying to wait on this will raise a fatal error which will + /// silently panic. + Poisoned, +} + +/// For a particular query, keeps track of "active" keys, i.e. keys whose +/// evaluation has started but has not yet finished successfully. +/// +/// (Successful query evaluation for a key is represented by an entry in the +/// query's in-memory cache.) +pub struct QueryState<'tcx, K> { + pub active: Sharded)>>, +} + +impl<'tcx, K> Default for QueryState<'tcx, K> { + fn default() -> QueryState<'tcx, K> { + QueryState { active: Default::default() } + } +} + +/// Description of a frame in the query stack. +/// +/// This is mostly used in case of cycles for error reporting. +#[derive(Debug)] +pub struct QueryStackFrame<'tcx> { + pub span: Span, + + /// The query and key of the query method call that this stack frame + /// corresponds to. + /// + /// Code that doesn't care about the specific key can still use this to + /// check which query it's for, or obtain the query's name. + pub tagged_key: TaggedQueryKey<'tcx>, +} + +#[derive(Debug)] +pub struct QueryCycle<'tcx> { + /// The query and related span that uses the cycle. + pub usage: Option>, + + /// The span here corresponds to the reason for which this query was required. + pub frames: Vec>, +} + #[derive(Debug)] pub struct QueryWaiter<'tcx> { pub parent: Option, pub condvar: Condvar, pub span: Span, - pub cycle: Mutex>>, + pub cycle: Mutex>>, } #[derive(Clone, Debug)] @@ -70,7 +127,7 @@ impl<'tcx> QueryLatch<'tcx> { } /// Awaits for the query job to complete. - pub fn wait_on(&self, query: Option, span: Span) -> Result<(), Cycle<'tcx>> { + pub fn wait_on(&self, query: Option, span: Span) -> Result<(), QueryCycle<'tcx>> { let mut waiters_guard = self.waiters.lock(); let Some(waiters) = &mut *waiters_guard else { return Ok(()); // already complete diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 7e9686941765f..e67826e722fa0 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1,34 +1,22 @@ -use rustc_hir::def_id::LocalDefId; - pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache}; +pub use self::calls::{TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, TyCtxtEnsureResult}; pub use self::into_query_key::IntoQueryKey; -pub use self::job::{QueryJob, QueryJobId, QueryLatch, QueryWaiter}; -pub use self::keys::{LocalCrate, QueryKey}; -pub use self::plumbing::{ - ActiveKeyStatus, Cycle, QueryMode, QueryState, QuerySystem, QueryVTable, TyCtxtAt, - TyCtxtEnsureDone, TyCtxtEnsureOk, TyCtxtEnsureResult, +pub use self::job::{ + ActiveKeyStatus, QueryCycle, QueryJob, QueryJobId, QueryLatch, QueryStackFrame, QueryState, + QueryWaiter, }; -pub use self::stack::QueryStackFrame; +pub use self::keys::{LocalCrate, QueryKey}; +pub use self::system::{QueryMode, QuerySystem, QueryVTable}; pub use crate::queries::Providers; -use crate::ty::TyCtxt; pub(crate) mod arena_cached; mod caches; +pub(crate) mod calls; pub mod erase; -pub(crate) mod inner; mod into_query_key; mod job; mod keys; pub(crate) mod modifiers; pub mod on_disk_cache; -pub(crate) mod plumbing; -mod stack; - -pub fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { - let def_id = def_id.into(); - if def_id.is_top_level_module() { - "top-level module".to_string() - } else { - format!("module `{}`", tcx.def_path_str(def_id)) - } -} +pub(crate) mod query_api; +mod system; diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs index 4f89ce529a586..d743c5dcc7e43 100644 --- a/compiler/rustc_middle/src/query/on_disk_cache.rs +++ b/compiler/rustc_middle/src/query/on_disk_cache.rs @@ -114,11 +114,11 @@ struct Footer { struct SourceFileIndex(u32); #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Encodable, Decodable)] -pub struct AbsoluteBytePos(u64); +struct AbsoluteBytePos(u64); impl AbsoluteBytePos { #[inline] - pub fn new(pos: usize) -> AbsoluteBytePos { + fn new(pos: usize) -> AbsoluteBytePos { AbsoluteBytePos(pos.try_into().expect("Incremental cache file size overflowed u64.")) } @@ -335,13 +335,6 @@ impl OnDiskCache { side_effect } - /// Returns true if there is a disk-cached query return value for the given node. - #[inline] - pub fn loadable_from_disk(&self, dep_node_index: SerializedDepNodeIndex) -> bool { - self.query_values_index.contains_key(&dep_node_index) - // with_decoder is infallible, so we can stop here - } - /// Returns the disk-cached query return value for the given node, if there is one. pub fn try_load_query_value<'tcx, T>( &self, diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/query_api.rs similarity index 56% rename from compiler/rustc_middle/src/query/plumbing.rs rename to compiler/rustc_middle/src/query/query_api.rs index e72770ad1d217..4d0eddd997190 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/query_api.rs @@ -1,293 +1,10 @@ -use std::fmt; -use std::ops::Deref; - -use rustc_data_structures::fingerprint::Fingerprint; -use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; -use rustc_data_structures::hash_table::HashTable; -use rustc_data_structures::sharded::Sharded; -use rustc_data_structures::sync::{AtomicU64, Lock, WorkerLocal}; -use rustc_errors::Diag; -use rustc_hir::def_id::LocalDefId; -use rustc_span::{Span, Symbol}; - -use crate::dep_graph::{ - DepKind, DepKindVTable, DepNodeIndex, QuerySideEffect, SerializedDepNodeIndex, -}; -use crate::ich::StableHashState; -use crate::queries::{ExternProviders, Providers, QueryArenas, QueryVTables, TaggedQueryKey}; -use crate::query::on_disk_cache::OnDiskCache; -use crate::query::{IntoQueryKey, QueryCache, QueryJob, QueryKey, QueryStackFrame}; -use crate::ty::{self, TyCtxt}; - -/// For a particular query, keeps track of "active" keys, i.e. keys whose -/// evaluation has started but has not yet finished successfully. -/// -/// (Successful query evaluation for a key is represented by an entry in the -/// query's in-memory cache.) -pub struct QueryState<'tcx, K> { - pub active: Sharded)>>, -} - -impl<'tcx, K> Default for QueryState<'tcx, K> { - fn default() -> QueryState<'tcx, K> { - QueryState { active: Default::default() } - } -} - -/// For a particular query and key, tracks the status of a query evaluation -/// that has started, but has not yet finished successfully. -/// -/// (Successful query evaluation for a key is represented by an entry in the -/// query's in-memory cache.) -pub enum ActiveKeyStatus<'tcx> { - /// Some thread is already evaluating the query for this key. - /// - /// The enclosed [`QueryJob`] can be used to wait for it to finish. - Started(QueryJob<'tcx>), - - /// The query panicked. Queries trying to wait on this will raise a fatal error which will - /// silently panic. - Poisoned, -} - -#[derive(Debug)] -pub struct Cycle<'tcx> { - /// The query and related span that uses the cycle. - pub usage: Option>, - - /// The span here corresponds to the reason for which this query was required. - pub frames: Vec>, -} - -#[derive(Debug)] -pub enum QueryMode { - /// This is a normal query call to `tcx.$query(..)` or `tcx.at(span).$query(..)`. - Get, - /// This is a call to `tcx.ensure_ok().$query(..)`. - EnsureOk, -} - -/// Stores data and metadata (e.g. function pointers) for a particular query. -pub struct QueryVTable<'tcx, C: QueryCache> { - pub name: &'static str, - - /// True if this query has the `eval_always` modifier. - pub eval_always: bool, - /// True if this query has the `depth_limit` modifier. - pub depth_limit: bool, - /// True if this query has the `feedable` modifier. - pub feedable: bool, - - pub cache_on_disk_local: bool, - pub separate_provide_extern: bool, - - pub dep_kind: DepKind, - pub state: QueryState<'tcx, C::Key>, - pub cache: C, - - /// Function pointer that actually calls this query's provider. - /// Also performs some associated secondary tasks; see the macro-defined - /// implementation in `mod invoke_provider_fn` for more details. - /// - /// This should be the only code that calls the provider function. - pub invoke_provider_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, - - /// Function pointer that tries to load a query value from disk. - /// - /// This should only be called after a successful check of [`Self::will_cache_on_disk_for_key`]. - pub try_load_from_disk_fn: - fn(tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) -> Option, - - /// Function pointer that hashes this query's result values. - /// - /// For `no_hash` queries, this function pointer is None. - pub hash_value_fn: Option, &C::Value) -> Fingerprint>, - - /// Function pointer that handles a cycle error. `error` must be consumed, e.g. with `emit` (if - /// it should be emitted) or `delay_as_bug` (if it need not be emitted because an alternative - /// error is created and emitted). A value may be returned, or (more commonly) the function may - /// just abort after emitting the error. - pub handle_cycle_error_fn: - fn(tcx: TyCtxt<'tcx>, key: C::Key, cycle: Cycle<'tcx>, error: Diag<'_>) -> C::Value, - - pub format_value: fn(&C::Value) -> String, - - pub create_tagged_key: fn(C::Key) -> TaggedQueryKey<'tcx>, - - /// Function pointer that is called by the query methods on [`TyCtxt`] and - /// friends[^1], after they have checked the in-memory cache and found no - /// existing value for this key. - /// - /// Transitive responsibilities include trying to load a disk-cached value - /// if possible (incremental only), invoking the query provider if necessary, - /// and putting the obtained value into the in-memory cache. - /// - /// [^1]: [`TyCtxt`], [`TyCtxtAt`], [`TyCtxtEnsureOk`], [`TyCtxtEnsureDone`] - pub execute_query_fn: fn(TyCtxt<'tcx>, Span, C::Key, QueryMode) -> Option, -} - -impl<'tcx, C: QueryCache> QueryVTable<'tcx, C> { - pub fn will_cache_on_disk_for_key(&self, key: C::Key) -> bool { - self.cache_on_disk_local && (!self.separate_provide_extern || key.as_local_key().is_some()) - } -} - -impl<'tcx, C: QueryCache> fmt::Debug for QueryVTable<'tcx, C> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // When debug-printing a query vtable (e.g. for ICE or tracing), - // just print the query name to know what query we're dealing with. - // The other fields and flags are probably just unhelpful noise. - // - // If there is need for a more detailed dump of all flags and fields, - // consider writing a separate dump method and calling it explicitly. - f.write_str(self.name) - } -} - -pub struct QuerySystem<'tcx> { - pub arenas: WorkerLocal>, - pub dep_kind_vtables: &'tcx [DepKindVTable<'tcx>], - pub query_vtables: QueryVTables<'tcx>, - - /// Side-effect associated with each [`DepKind::SideEffect`] node in the - /// current incremental-compilation session. Side effects will be written - /// to disk, and loaded by [`OnDiskCache`] in the next session. - /// - /// Always empty if incremental compilation is off. - pub side_effects: Lock>, - - /// Enabled features that are used in the current compilation. - /// - /// The value is the `DepNodeIndex` of the node that encodes the used feature. - pub used_features: Lock>, - - /// 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, - - pub local_providers: Providers, - pub extern_providers: ExternProviders, - - pub jobs: AtomicU64, - - pub cycle_handler_nesting: Lock, -} - -#[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)] -#[must_use] -pub struct TyCtxtEnsureOk<'tcx> { - pub tcx: TyCtxt<'tcx>, -} - -#[derive(Copy, Clone)] -#[must_use] -pub struct TyCtxtEnsureResult<'tcx> { - pub tcx: TyCtxt<'tcx>, -} - -#[derive(Copy, Clone)] -#[must_use] -pub struct TyCtxtEnsureDone<'tcx> { - pub tcx: TyCtxt<'tcx>, -} - -impl<'tcx> TyCtxtEnsureOk<'tcx> { - pub fn typeck(self, def_id: impl IntoQueryKey) { - self.typeck_root( - self.tcx.typeck_root_def_id(def_id.into_query_key().to_def_id()).expect_local(), - ) - } -} - -impl<'tcx> TyCtxt<'tcx> { - pub fn typeck(self, def_id: impl IntoQueryKey) -> &'tcx ty::TypeckResults<'tcx> { - self.typeck_root( - self.typeck_root_def_id(def_id.into_query_key().to_def_id()).expect_local(), - ) - } - - /// 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 } - } - - /// FIXME: `ensure_ok`'s effects are subtle. Is this comment fully accurate? - /// - /// Wrapper that calls queries in a special "ensure OK" mode, for callers - /// that don't need the return value and just want to invoke a query for - /// its potential side-effect of emitting fatal errors. - /// - /// This can be more efficient than a normal query call, because if the - /// query's inputs are all green, the call can return immediately without - /// needing to obtain a value (by decoding one from disk or by executing - /// the query). - /// - /// (As with all query calls, execution is also skipped if the query result - /// is already cached in memory.) - /// - /// ## WARNING - /// A subsequent normal call to the same query might still cause it to be - /// executed! This can occur when the inputs are all green, but the query's - /// result is not cached on disk, so the query must be executed to obtain a - /// return value. - /// - /// Therefore, this call mode is not appropriate for callers that want to - /// ensure that the query is _never_ executed in the future. - #[inline(always)] - pub fn ensure_ok(self) -> TyCtxtEnsureOk<'tcx> { - TyCtxtEnsureOk { tcx: self } - } - - /// This is a variant of `ensure_ok` only usable with queries that return - /// `Result<_, ErrorGuaranteed>`. Queries calls through this function will - /// return `Result<(), ErrorGuaranteed>`. I.e. the error status is returned - /// but nothing else. As with `ensure_ok`, this can be more efficient than - /// a normal query call. - #[inline(always)] - pub fn ensure_result(self) -> TyCtxtEnsureResult<'tcx> { - TyCtxtEnsureResult { tcx: self } - } - - /// Wrapper that calls queries where callers don't need the return value and - /// just want to guarantee that the query won't be executed in the future. - /// - /// This is useful for queries that read from a [`Steal`] value, to ensure - /// that they are executed before the query that will steal the value. - /// - /// Currently this causes the query to be executed normally, but this behavior may change. - /// - /// [`Steal`]: rustc_data_structures::steal::Steal - #[inline(always)] - pub fn ensure_done(self) -> TyCtxtEnsureDone<'tcx> { - TyCtxtEnsureDone { tcx: self } - } -} - macro_rules! maybe_into_query_key { (DefId) => { impl $crate::query::IntoQueryKey }; (LocalDefId) => { impl $crate::query::IntoQueryKey }; ($K:ty) => { $K }; } -macro_rules! define_callbacks { +macro_rules! define_query_api { ( // You might expect the key to be `$K:ty`, but it needs to be `$($K:tt)*` so that // `maybe_into_query_key!` can match on specific type names. @@ -520,7 +237,7 @@ macro_rules! define_callbacks { Providers { $( $name: |_, key| { - $crate::query::plumbing::default_query(stringify!($name), &key) + $crate::query::query_api::default_query(stringify!($name), &key) }, )* } @@ -532,7 +249,7 @@ macro_rules! define_callbacks { ExternProviders { $( #[cfg($separate_provide_extern)] - $name: |_, key| $crate::query::plumbing::default_extern_query( + $name: |_, key| $crate::query::query_api::default_extern_query( stringify!($name), &key, ), @@ -567,9 +284,7 @@ macro_rules! define_callbacks { $(#[$attr])* #[inline(always)] pub fn $name(self, key: maybe_into_query_key!($($K)*)) -> $V { - use $crate::query::{erase, inner}; - - erase::restore_val::<$V>(inner::query_get_at( + $crate::query::erase::restore_val::<$V>($crate::query::calls::query_get_at( self.tcx, self.span, &self.tcx.query_system.query_vtables.$name, @@ -584,7 +299,7 @@ macro_rules! define_callbacks { $(#[$attr])* #[inline(always)] pub fn $name(self, key: maybe_into_query_key!($($K)*)) { - $crate::query::inner::query_ensure_ok( + $crate::query::calls::query_ensure_ok( self.tcx, &self.tcx.query_system.query_vtables.$name, $crate::query::IntoQueryKey::into_query_key(key), @@ -603,7 +318,7 @@ macro_rules! define_callbacks { self, key: maybe_into_query_key!($($K)*), ) -> Result<(), rustc_errors::ErrorGuaranteed> { - $crate::query::inner::query_ensure_result( + $crate::query::calls::query_ensure_result( self.tcx, &self.tcx.query_system.query_vtables.$name, $crate::query::IntoQueryKey::into_query_key(key), @@ -633,7 +348,7 @@ macro_rules! define_callbacks { $(#[$attr])* #[inline(always)] pub fn $name(self, value: $name::ProvidedValue<'tcx>) { - $crate::query::inner::query_feed( + $crate::query::calls::query_feed( self.tcx, &self.tcx.query_system.query_vtables.$name, self.key().into_query_key(), @@ -646,7 +361,7 @@ macro_rules! define_callbacks { } // Re-export `macro_rules!` macros as normal items, so that they can be imported normally. -pub(crate) use define_callbacks; +pub(crate) use define_query_api; pub(crate) use maybe_into_query_key; #[cold] diff --git a/compiler/rustc_middle/src/query/stack.rs b/compiler/rustc_middle/src/query/stack.rs deleted file mode 100644 index 9465fc87edf09..0000000000000 --- a/compiler/rustc_middle/src/query/stack.rs +++ /dev/null @@ -1,18 +0,0 @@ -use rustc_span::Span; - -use crate::queries::TaggedQueryKey; - -/// Description of a frame in the query stack. -/// -/// This is mostly used in case of cycles for error reporting. -#[derive(Debug)] -pub struct QueryStackFrame<'tcx> { - pub span: Span, - - /// The query and key of the query method call that this stack frame - /// corresponds to. - /// - /// Code that doesn't care about the specific key can still use this to - /// check which query it's for, or obtain the query's name. - pub tagged_key: TaggedQueryKey<'tcx>, -} diff --git a/compiler/rustc_middle/src/query/system.rs b/compiler/rustc_middle/src/query/system.rs new file mode 100644 index 0000000000000..e217ee1894764 --- /dev/null +++ b/compiler/rustc_middle/src/query/system.rs @@ -0,0 +1,133 @@ +use std::fmt; + +use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; +use rustc_data_structures::sync::{AtomicU64, Lock, WorkerLocal}; +use rustc_errors::Diag; +use rustc_span::{Span, Symbol}; + +use crate::dep_graph::{ + DepKind, DepKindVTable, DepNodeIndex, QuerySideEffect, SerializedDepNodeIndex, +}; +use crate::ich::StableHashState; +use crate::queries::{ExternProviders, Providers, QueryArenas, QueryVTables, TaggedQueryKey}; +use crate::query::on_disk_cache::OnDiskCache; +use crate::query::{QueryCache, QueryCycle, QueryKey, QueryState}; +use crate::ty::TyCtxt; + +#[derive(Debug)] +pub enum QueryMode { + /// This is a normal query call to `tcx.$query(..)` or `tcx.at(span).$query(..)`. + Get, + /// This is a call to `tcx.ensure_ok().$query(..)`. + EnsureOk, +} + +/// Stores data and metadata (e.g. function pointers) for a particular query. +pub struct QueryVTable<'tcx, C: QueryCache> { + pub name: &'static str, + + /// True if this query has the `eval_always` modifier. + pub eval_always: bool, + /// True if this query has the `depth_limit` modifier. + pub depth_limit: bool, + /// True if this query has the `feedable` modifier. + pub feedable: bool, + + pub cache_on_disk_local: bool, + pub separate_provide_extern: bool, + + pub dep_kind: DepKind, + pub state: QueryState<'tcx, C::Key>, + pub cache: C, + + /// Function pointer that actually calls this query's provider. + /// Also performs some associated secondary tasks; see the macro-defined + /// implementation in `mod invoke_provider_fn` for more details. + /// + /// This should be the only code that calls the provider function. + pub invoke_provider_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, + + /// Function pointer that tries to load a query value from disk. + /// + /// This should only be called after a successful check of [`Self::will_cache_on_disk_for_key`]. + pub try_load_from_disk_fn: + fn(tcx: TyCtxt<'tcx>, prev_index: SerializedDepNodeIndex) -> Option, + + /// Function pointer that hashes this query's result values. + /// + /// For `no_hash` queries, this function pointer is None. + pub hash_value_fn: Option, &C::Value) -> Fingerprint>, + + /// Function pointer that handles a cycle error. `error` must be consumed, e.g. with `emit` (if + /// it should be emitted) or `delay_as_bug` (if it need not be emitted because an alternative + /// error is created and emitted). A value may be returned, or (more commonly) the function may + /// just abort after emitting the error. + pub handle_cycle_error_fn: + fn(tcx: TyCtxt<'tcx>, key: C::Key, cycle: QueryCycle<'tcx>, error: Diag<'_>) -> C::Value, + + pub format_value: fn(&C::Value) -> String, + + pub create_tagged_key: fn(C::Key) -> TaggedQueryKey<'tcx>, + + /// Function pointer that is called by the query methods on [`TyCtxt`] and + /// friends[^1], after they have checked the in-memory cache and found no + /// existing value for this key. + /// + /// Transitive responsibilities include trying to load a disk-cached value + /// if possible (incremental only), invoking the query provider if necessary, + /// and putting the obtained value into the in-memory cache. + /// + /// [^1]: [`TyCtxt`], [`crate::query::TyCtxtAt`], [`crate::query::TyCtxtEnsureOk`], + /// [`crate::query::TyCtxtEnsureDone`] + pub execute_query_fn: fn(TyCtxt<'tcx>, Span, C::Key, QueryMode) -> Option, +} + +impl<'tcx, C: QueryCache> QueryVTable<'tcx, C> { + pub fn will_cache_on_disk_for_key(&self, key: C::Key) -> bool { + self.cache_on_disk_local && (!self.separate_provide_extern || key.as_local_key().is_some()) + } +} + +impl<'tcx, C: QueryCache> fmt::Debug for QueryVTable<'tcx, C> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // When debug-printing a query vtable (e.g. for ICE or tracing), + // just print the query name to know what query we're dealing with. + // The other fields and flags are probably just unhelpful noise. + // + // If there is need for a more detailed dump of all flags and fields, + // consider writing a separate dump method and calling it explicitly. + f.write_str(self.name) + } +} + +pub struct QuerySystem<'tcx> { + pub arenas: WorkerLocal>, + pub dep_kind_vtables: &'tcx [DepKindVTable<'tcx>], + pub query_vtables: QueryVTables<'tcx>, + + /// Side-effect associated with each [`DepKind::SideEffect`] node in the + /// current incremental-compilation session. Side effects will be written + /// to disk, and loaded by [`OnDiskCache`] in the next session. + /// + /// Always empty if incremental compilation is off. + pub side_effects: Lock>, + + /// Enabled features that are used in the current compilation. + /// + /// The value is the `DepNodeIndex` of the node that encodes the used feature. + pub used_features: Lock>, + + /// 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, + + pub local_providers: Providers, + pub extern_providers: ExternProviders, + + pub jobs: AtomicU64, + + pub cycle_handler_nesting: Lock, +} diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 3e5ee960e2772..e8fcb64bd259f 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -9,8 +9,8 @@ use rustc_middle::dep_graph::{ DepGraphData, DepNode, DepNodeIndex, DepNodeKey, SerializedDepNodeIndex, }; use rustc_middle::query::{ - ActiveKeyStatus, Cycle, QueryCache, QueryJob, QueryJobId, QueryLatch, QueryMode, QueryState, - QueryVTable, + ActiveKeyStatus, QueryCache, QueryCycle, QueryJob, QueryJobId, QueryLatch, QueryMode, + QueryState, QueryVTable, }; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::tls::{self, ImplicitCtxt}; @@ -36,7 +36,7 @@ fn handle_cycle<'tcx, C: QueryCache>( query: &'tcx QueryVTable<'tcx, C>, tcx: TyCtxt<'tcx>, key: C::Key, - cycle: Cycle<'tcx>, + cycle: QueryCycle<'tcx>, ) -> C::Value { let nested; { diff --git a/compiler/rustc_query_impl/src/handle_cycle_error.rs b/compiler/rustc_query_impl/src/handle_cycle_error.rs index 6bc7bfc59b08b..8d0d671406468 100644 --- a/compiler/rustc_query_impl/src/handle_cycle_error.rs +++ b/compiler/rustc_query_impl/src/handle_cycle_error.rs @@ -10,7 +10,7 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::bug; use rustc_middle::queries::TaggedQueryKey; -use rustc_middle::query::Cycle; +use rustc_middle::query::QueryCycle; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; @@ -25,7 +25,7 @@ pub(crate) fn default(err: Diag<'_>) -> ! { pub(crate) fn fn_sig<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, - _: Cycle<'tcx>, + _: QueryCycle<'tcx>, err: Diag<'_>, ) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { let guar = err.delay_as_bug(); @@ -50,7 +50,7 @@ pub(crate) fn fn_sig<'tcx>( pub(crate) fn check_representability<'tcx>( tcx: TyCtxt<'tcx>, _key: LocalDefId, - cycle: Cycle<'tcx>, + cycle: QueryCycle<'tcx>, _err: Diag<'_>, ) { check_representability_inner(tcx, cycle); @@ -59,13 +59,13 @@ pub(crate) fn check_representability<'tcx>( pub(crate) fn check_representability_adt_ty<'tcx>( tcx: TyCtxt<'tcx>, _key: Ty<'tcx>, - cycle: Cycle<'tcx>, + cycle: QueryCycle<'tcx>, _err: Diag<'_>, ) { check_representability_inner(tcx, cycle); } -fn check_representability_inner<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> ! { +fn check_representability_inner<'tcx>(tcx: TyCtxt<'tcx>, cycle: QueryCycle<'tcx>) -> ! { let mut item_and_field_ids = Vec::new(); let mut representable_ids = FxHashSet::default(); for frame in &cycle.frames { @@ -99,7 +99,7 @@ fn check_representability_inner<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> pub(crate) fn variances_of<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, - _cycle: Cycle<'tcx>, + _cycle: QueryCycle<'tcx>, err: Diag<'_>, ) -> &'tcx [ty::Variance] { let _guar = err.delay_as_bug(); @@ -129,7 +129,7 @@ fn search_for_cycle_permutation( pub(crate) fn layout_of<'tcx>( tcx: TyCtxt<'tcx>, _key: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, - cycle: Cycle<'tcx>, + cycle: QueryCycle<'tcx>, err: Diag<'_>, ) -> Result, &'tcx ty::layout::LayoutError<'tcx>> { let _guar = err.delay_as_bug(); @@ -346,7 +346,7 @@ fn find_item_ty_spans( #[cold] pub(crate) fn create_cycle_error<'tcx>( tcx: TyCtxt<'tcx>, - Cycle { usage, frames }: &Cycle<'tcx>, + QueryCycle { usage, frames }: &QueryCycle<'tcx>, nested: bool, ) -> Diag<'tcx> { assert!(!frames.is_empty()); diff --git a/compiler/rustc_query_impl/src/job.rs b/compiler/rustc_query_impl/src/job.rs index 1f9b04278bd07..54f7a42bffb26 100644 --- a/compiler/rustc_query_impl/src/job.rs +++ b/compiler/rustc_query_impl/src/job.rs @@ -9,7 +9,7 @@ use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::DiagCtxtHandle; use rustc_middle::queries::TaggedQueryKey; use rustc_middle::query::{ - ActiveKeyStatus, Cycle, QueryCache, QueryJob, QueryJobId, QueryKey, QueryLatch, + ActiveKeyStatus, QueryCache, QueryCycle, QueryJob, QueryJobId, QueryKey, QueryLatch, QueryStackFrame, QueryVTable, QueryWaiter, }; use rustc_middle::ty::TyCtxt; @@ -142,7 +142,7 @@ pub(crate) fn find_cycle_in_stack<'tcx>( job_map: QueryJobMap<'tcx>, current_job: &Option, span: Span, -) -> Cycle<'tcx> { +) -> QueryCycle<'tcx> { // Find the waitee amongst `current_job` parents. let mut frames = Vec::new(); let mut current_job = Option::clone(current_job); @@ -163,7 +163,7 @@ pub(crate) fn find_cycle_in_stack<'tcx>( let parent = info.job.parent?; QueryStackFrame { span: info.job.span, tagged_key: job_map.tagged_key_of(parent) } }; - return Cycle { usage, frames }; + return QueryCycle { usage, frames }; } current_job = info.job.parent; @@ -316,7 +316,10 @@ fn connected_to_root<'tcx>( } /// Processes a found query cycle into a `Cycle` -fn process_cycle<'tcx>(job_map: &QueryJobMap<'tcx>, stack: Vec<(Span, QueryJobId)>) -> Cycle<'tcx> { +fn process_cycle<'tcx>( + job_map: &QueryJobMap<'tcx>, + stack: Vec<(Span, QueryJobId)>, +) -> QueryCycle<'tcx> { // The stack is a vector of pairs of spans and queries; reverse it so that // the earlier entries require later entries let (mut spans, queries): (Vec<_>, Vec<_>) = stack.into_iter().rev().unzip(); @@ -380,7 +383,7 @@ fn process_cycle<'tcx>(job_map: &QueryJobMap<'tcx>, stack: Vec<(Span, QueryJobId .map(|(span, job)| QueryStackFrame { span, tagged_key: job_map.tagged_key_of(job) }); // Create the cycle error - Cycle { + QueryCycle { usage, frames: stack .iter()