rustc_query_system/query/
mod.rs

1mod plumbing;
2use std::fmt::Debug;
3use std::marker::PhantomData;
4use std::mem::transmute;
5use std::sync::Arc;
6
7pub use self::plumbing::*;
8
9mod job;
10pub use self::job::{
11    QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap, break_query_cycles, print_query_stack,
12    report_cycle,
13};
14
15mod caches;
16pub use self::caches::{DefIdCache, DefaultCache, QueryCache, SingleCache, VecCache};
17
18mod config;
19use rustc_data_structures::sync::{DynSend, DynSync};
20use rustc_errors::DiagInner;
21use rustc_hashes::Hash64;
22use rustc_hir::def::DefKind;
23use rustc_macros::{Decodable, Encodable};
24use rustc_span::Span;
25use rustc_span::def_id::DefId;
26
27pub use self::config::{HashResult, QueryConfig};
28use crate::dep_graph::{DepKind, DepNodeIndex, HasDepContext, SerializedDepNodeIndex};
29
30/// Description of a frame in the query stack.
31///
32/// This is mostly used in case of cycles for error reporting.
33#[derive(Clone, Debug)]
34pub struct QueryStackFrame<I> {
35    /// This field initially stores a `QueryStackDeferred` during collection,
36    /// but can later be changed to `QueryStackFrameExtra` containing concrete information
37    /// by calling `lift`. This is done so that collecting query does not need to invoke
38    /// queries, instead `lift` will call queries in a more appropriate location.
39    pub info: I,
40
41    pub dep_kind: DepKind,
42    /// This hash is used to deterministically pick
43    /// a query to remove cycles in the parallel compiler.
44    hash: Hash64,
45    pub def_id: Option<DefId>,
46    /// A def-id that is extracted from a `Ty` in a query key
47    pub def_id_for_ty_in_cycle: Option<DefId>,
48}
49
50impl<I> QueryStackFrame<I> {
51    #[inline]
52    pub fn new(
53        info: I,
54        dep_kind: DepKind,
55        hash: impl FnOnce() -> Hash64,
56        def_id: Option<DefId>,
57        def_id_for_ty_in_cycle: Option<DefId>,
58    ) -> Self {
59        Self { info, def_id, dep_kind, hash: hash(), def_id_for_ty_in_cycle }
60    }
61
62    fn lift<Qcx: QueryContext<QueryInfo = I>>(
63        &self,
64        qcx: Qcx,
65    ) -> QueryStackFrame<QueryStackFrameExtra> {
66        QueryStackFrame {
67            info: qcx.lift_query_info(&self.info),
68            dep_kind: self.dep_kind,
69            hash: self.hash,
70            def_id: self.def_id,
71            def_id_for_ty_in_cycle: self.def_id_for_ty_in_cycle,
72        }
73    }
74}
75
76#[derive(Clone, Debug)]
77pub struct QueryStackFrameExtra {
78    pub description: String,
79    span: Option<Span>,
80    pub def_kind: Option<DefKind>,
81}
82
83impl QueryStackFrameExtra {
84    #[inline]
85    pub fn new(description: String, span: Option<Span>, def_kind: Option<DefKind>) -> Self {
86        Self { description, span, def_kind }
87    }
88
89    // FIXME(eddyb) Get more valid `Span`s on queries.
90    #[inline]
91    pub fn default_span(&self, span: Span) -> Span {
92        if !span.is_dummy() {
93            return span;
94        }
95        self.span.unwrap_or(span)
96    }
97}
98
99/// Track a 'side effect' for a particular query.
100/// This is used to hold a closure which can create `QueryStackFrameExtra`.
101#[derive(Clone)]
102pub struct QueryStackDeferred<'tcx> {
103    _dummy: PhantomData<&'tcx ()>,
104
105    // `extract` may contain references to 'tcx, but we can't tell drop checking that it won't
106    // access it in the destructor.
107    extract: Arc<dyn Fn() -> QueryStackFrameExtra + DynSync + DynSend>,
108}
109
110impl<'tcx> QueryStackDeferred<'tcx> {
111    pub fn new<C: Copy + DynSync + DynSend + 'tcx>(
112        context: C,
113        extract: fn(C) -> QueryStackFrameExtra,
114    ) -> Self {
115        let extract: Arc<dyn Fn() -> QueryStackFrameExtra + DynSync + DynSend + 'tcx> =
116            Arc::new(move || extract(context));
117        // SAFETY: The `extract` closure does not access 'tcx in its destructor as the only
118        // captured variable is `context` which is Copy and cannot have a destructor.
119        Self { _dummy: PhantomData, extract: unsafe { transmute(extract) } }
120    }
121
122    pub fn extract(&self) -> QueryStackFrameExtra {
123        (self.extract)()
124    }
125}
126
127impl<'tcx> Debug for QueryStackDeferred<'tcx> {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        f.write_str("QueryStackDeferred")
130    }
131}
132
133/// Tracks 'side effects' for a particular query.
134/// This struct is saved to disk along with the query result,
135/// and loaded from disk if we mark the query as green.
136/// This allows us to 'replay' changes to global state
137/// that would otherwise only occur if we actually
138/// executed the query method.
139///
140/// Each side effect gets an unique dep node index which is added
141/// as a dependency of the query which had the effect.
142#[derive(Debug, Encodable, Decodable)]
143pub enum QuerySideEffect {
144    /// Stores a diagnostic emitted during query execution.
145    /// This diagnostic will be re-emitted if we mark
146    /// the query as green, as that query will have the side
147    /// effect dep node as a dependency.
148    Diagnostic(DiagInner),
149}
150
151pub trait QueryContext: HasDepContext {
152    type QueryInfo: Clone;
153
154    fn next_job_id(self) -> QueryJobId;
155
156    /// Get the query information from the TLS context.
157    fn current_query_job(self) -> Option<QueryJobId>;
158
159    fn collect_active_jobs(self) -> Result<QueryMap<Self::QueryInfo>, QueryMap<Self::QueryInfo>>;
160
161    fn lift_query_info(self, info: &Self::QueryInfo) -> QueryStackFrameExtra;
162
163    /// Load a side effect associated to the node in the previous session.
164    fn load_side_effect(
165        self,
166        prev_dep_node_index: SerializedDepNodeIndex,
167    ) -> Option<QuerySideEffect>;
168
169    /// Register a side effect for the given node, for use in next session.
170    fn store_side_effect(self, dep_node_index: DepNodeIndex, side_effect: QuerySideEffect);
171
172    /// Executes a job by changing the `ImplicitCtxt` to point to the
173    /// new query job while it executes.
174    fn start_query<R>(self, token: QueryJobId, depth_limit: bool, compute: impl FnOnce() -> R)
175    -> R;
176
177    fn depth_limit_error(self, job: QueryJobId);
178}