use std::fmt::Debug; use std::marker::PhantomData; use std::mem::transmute; use std::sync::Arc; use rustc_data_structures::jobserver::Proxy; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::DiagInner; use rustc_hashes::Hash64; use rustc_hir::def::DefKind; use rustc_macros::{Decodable, Encodable}; use rustc_span::Span; use rustc_span::def_id::DefId; pub use self::caches::{ DefIdCache, DefaultCache, QueryCache, QueryCacheKey, SingleCache, VecCache, }; pub use self::job::{QueryInfo, QueryJob, QueryJobId, QueryLatch, QueryWaiter}; pub use self::plumbing::*; use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIndex}; mod caches; mod job; mod plumbing; /// How a particular query deals with query cycle errors. /// /// Inspected by the code that actually handles cycle errors, to decide what /// approach to use. #[derive(Copy, Clone)] pub enum CycleErrorHandling { Error, Fatal, DelayBug, Stash, } /// Description of a frame in the query stack. /// /// This is mostly used in case of cycles for error reporting. #[derive(Clone, Debug)] pub struct QueryStackFrame { /// This field initially stores a `QueryStackDeferred` during collection, /// but can later be changed to `QueryStackFrameExtra` containing concrete information /// by calling `lift`. This is done so that collecting query does not need to invoke /// queries, instead `lift` will call queries in a more appropriate location. pub info: I, pub dep_kind: DepKind, /// This hash is used to deterministically pick /// a query to remove cycles in the parallel compiler. pub hash: Hash64, pub def_id: Option, /// A def-id that is extracted from a `Ty` in a query key pub def_id_for_ty_in_cycle: Option, } impl<'tcx> QueryStackFrame> { #[inline] pub fn new( info: QueryStackDeferred<'tcx>, dep_kind: DepKind, hash: Hash64, def_id: Option, def_id_for_ty_in_cycle: Option, ) -> Self { Self { info, def_id, dep_kind, hash, def_id_for_ty_in_cycle } } fn lift(&self) -> QueryStackFrame { QueryStackFrame { info: self.info.extract(), dep_kind: self.dep_kind, hash: self.hash, def_id: self.def_id, def_id_for_ty_in_cycle: self.def_id_for_ty_in_cycle, } } } #[derive(Clone, Debug)] pub struct QueryStackFrameExtra { pub description: String, pub span: Option, pub def_kind: Option, } impl QueryStackFrameExtra { #[inline] pub fn new(description: String, span: Option, def_kind: Option) -> Self { Self { description, span, def_kind } } // FIXME(eddyb) Get more valid `Span`s on queries. #[inline] pub fn default_span(&self, span: Span) -> Span { if !span.is_dummy() { return span; } self.span.unwrap_or(span) } } /// Track a 'side effect' for a particular query. /// This is used to hold a closure which can create `QueryStackFrameExtra`. #[derive(Clone)] pub struct QueryStackDeferred<'tcx> { _dummy: PhantomData<&'tcx ()>, // `extract` may contain references to 'tcx, but we can't tell drop checking that it won't // access it in the destructor. extract: Arc QueryStackFrameExtra + DynSync + DynSend>, } impl<'tcx> QueryStackDeferred<'tcx> { pub fn new( context: C, extract: fn(C) -> QueryStackFrameExtra, ) -> Self { let extract: Arc QueryStackFrameExtra + DynSync + DynSend + 'tcx> = Arc::new(move || extract(context)); // SAFETY: The `extract` closure does not access 'tcx in its destructor as the only // captured variable is `context` which is Copy and cannot have a destructor. Self { _dummy: PhantomData, extract: unsafe { transmute(extract) } } } pub fn extract(&self) -> QueryStackFrameExtra { (self.extract)() } } impl<'tcx> Debug for QueryStackDeferred<'tcx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str("QueryStackDeferred") } } /// Tracks 'side effects' for a particular query. /// This struct is saved to disk along with the query result, /// and loaded from disk if we mark the query as green. /// This allows us to 'replay' changes to global state /// that would otherwise only occur if we actually /// executed the query method. /// /// Each side effect gets an unique dep node index which is added /// as a dependency of the query which had the effect. #[derive(Debug, Encodable, Decodable)] pub enum QuerySideEffect { /// Stores a diagnostic emitted during query execution. /// This diagnostic will be re-emitted if we mark /// the query as green, as that query will have the side /// effect dep node as a dependency. Diagnostic(DiagInner), } pub trait QueryContext<'tcx>: HasDepContext { /// Gets a jobserver reference which is used to release then acquire /// a token while waiting on a query. fn jobserver_proxy(&self) -> &Proxy; /// Load a side effect associated to the node in the previous session. fn load_side_effect( self, prev_dep_node_index: SerializedDepNodeIndex, ) -> Option; /// Register a side effect for the given node, for use in next session. fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect); }