Skip to main content

rustc_middle/query/
query_api.rs

1macro_rules! maybe_into_query_key {
2    (DefId) => { impl $crate::query::IntoQueryKey<DefId> };
3    (LocalDefId) => { impl $crate::query::IntoQueryKey<LocalDefId> };
4    ($K:ty) => { $K };
5}
6
7macro_rules! define_query_api {
8    (
9        // You might expect the key to be `$K:ty`, but it needs to be `$($K:tt)*` so that
10        // `maybe_into_query_key!` can match on specific type names.
11        queries {
12            $(
13                $(#[$attr:meta])*
14                fn $name:ident($($K:tt)*) -> $V:ty
15                {
16                    // Search for (QMODLIST) to find all occurrences of this query modifier list.
17                    arena_cache: $arena_cache:literal,
18                    cache_on_disk: $cache_on_disk:literal,
19                    depth_limit: $depth_limit:literal,
20                    desc: $desc:expr,
21                    eval_always: $eval_always:literal,
22                    feedable: $feedable:literal,
23                    handle_cycle_error: $handle_cycle_error:literal,
24                    no_force: $no_force:literal,
25                    no_hash: $no_hash:literal,
26                    returns_error_guaranteed: $returns_error_guaranteed:literal,
27                    separate_provide_extern: $separate_provide_extern:literal,
28                }
29            )*
30        }
31        // Non-queries are unused here.
32        non_queries { $($_:tt)* }
33    ) => {
34        $(
35            pub mod $name {
36                use super::*;
37                use $crate::query::erase::{self, Erased};
38
39                pub type Key<'tcx> = $($K)*;
40                pub type Value<'tcx> = $V;
41
42                /// Key type used by provider functions in `local_providers`.
43                /// This query has the `separate_provide_extern` modifier.
44                #[cfg($separate_provide_extern)]
45                pub type LocalKey<'tcx> =
46                    <Key<'tcx> as $crate::query::QueryKey>::LocalQueryKey;
47                /// Key type used by provider functions in `local_providers`.
48                #[cfg(not($separate_provide_extern))]
49                pub type LocalKey<'tcx> = Key<'tcx>;
50
51                /// Type returned from query providers and loaded from disk-cache.
52                #[cfg($arena_cache)]
53                pub type ProvidedValue<'tcx> =
54                    <Value<'tcx> as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided;
55                /// Type returned from query providers and loaded from disk-cache.
56                #[cfg(not($arena_cache))]
57                pub type ProvidedValue<'tcx> = Value<'tcx>;
58
59                pub type Cache<'tcx> =
60                    <Key<'tcx> as $crate::query::QueryKey>::Cache<Erased<Value<'tcx>>>;
61
62                /// This helper function takes a value returned by the query provider
63                /// (or loaded from disk, or supplied by query feeding), allocates
64                /// it in an arena if requested by the `arena_cache` modifier, and
65                /// then returns an erased copy of it.
66                #[inline(always)]
67                pub fn provided_to_erased<'tcx>(
68                    tcx: TyCtxt<'tcx>,
69                    provided_value: ProvidedValue<'tcx>,
70                ) -> Erased<Value<'tcx>> {
71                    // For queries with the `arena_cache` modifier, store the
72                    // provided value in an arena and get a reference to it.
73                    #[cfg($arena_cache)]
74                    let value: Value<'tcx> = {
75                        use $crate::query::arena_cached::ArenaCached;
76                        <Value<'tcx> as ArenaCached>::alloc_in_arena(
77                            tcx,
78                            &tcx.query_system.arenas.$name,
79                            provided_value,
80                        )
81                    };
82
83                    // Otherwise, the provided value is the value (and `tcx` is unused).
84                    #[cfg(not($arena_cache))]
85                    let value: Value<'tcx> = {
86                        let _ = tcx;
87                        provided_value
88                    };
89
90                    erase::erase_val(value)
91                }
92
93                // Ensure that keys grow no larger than 88 bytes by accident.
94                // Increase this limit if necessary, but do try to keep the size low if possible
95                #[cfg(target_pointer_width = "64")]
96                const _: () = {
97                    if size_of::<Key<'static>>() > 88 {
98                        panic!("{}", concat!(
99                            "the query `",
100                            stringify!($name),
101                            "` has a key type `",
102                            stringify!($($K)*),
103                            "` that is too large"
104                        ));
105                    }
106                };
107
108                // Ensure that values grow no larger than 64 bytes by accident.
109                // Increase this limit if necessary, but do try to keep the size low if possible
110                #[cfg(target_pointer_width = "64")]
111                #[cfg(not(feature = "rustc_randomized_layouts"))]
112                const _: () = {
113                    if size_of::<Value<'static>>() > 64 {
114                        panic!("{}", concat!(
115                            "the query `",
116                            stringify!($name),
117                            "` has a value type `",
118                            stringify!($V),
119                            "` that is too large"
120                        ));
121                    }
122                };
123            }
124        )*
125
126        /// Identifies a query by kind and key. This is in contrast to `QueryJobId` which is just a
127        /// number.
128        #[allow(non_camel_case_types)]
129        #[derive(Clone, Copy, Debug)]
130        pub enum TaggedQueryKey<'tcx> {
131            $(
132                $name($name::Key<'tcx>),
133            )*
134        }
135
136        impl<'tcx> TaggedQueryKey<'tcx> {
137            /// Returns the name of the query this key is tagged with.
138            ///
139            /// This is useful for error/debug output, but don't use it to check for
140            /// specific query names. Instead, match on the `TaggedQueryKey` variant.
141            pub fn query_name(&self) -> &'static str {
142                match self {
143                    $(
144                        TaggedQueryKey::$name(_) => stringify!($name),
145                    )*
146                }
147            }
148
149            /// Formats a human-readable description of this query and its key, as
150            /// specified by the `desc` query modifier.
151            ///
152            /// Used when reporting query cycle errors and similar problems.
153            pub fn description(&self, tcx: TyCtxt<'tcx>) -> String {
154                let (name, description) = ty::print::with_no_queries!(match self {
155                    $(
156                        TaggedQueryKey::$name(key) => (stringify!($name), ($desc)(tcx, *key)),
157                    )*
158                });
159                if tcx.sess.verbose_internals() {
160                    format!("{description} [{name:?}]")
161                } else {
162                    description
163                }
164            }
165
166            /// Calls `self.description` or returns a fallback if there was a fatal error
167            pub fn catch_description(&self, tcx: TyCtxt<'tcx>) -> String {
168                catch_fatal_errors(|| self.description(tcx)).unwrap_or_else(|_| format!("<error describing {}>", self.query_name()))
169            }
170
171            /// Returns the default span for this query if `span` is a dummy span.
172            pub fn default_span(&self, tcx: TyCtxt<'tcx>, span: Span) -> Span {
173                if !span.is_dummy() {
174                    return span
175                }
176                if let TaggedQueryKey::def_span(..) = self {
177                    // The `def_span` query is used to calculate `default_span`,
178                    // so exit to avoid infinite recursion.
179                    return DUMMY_SP
180                }
181                match self {
182                    $(
183                        TaggedQueryKey::$name(key) =>
184                            $crate::query::QueryKey::default_span(key, tcx),
185                    )*
186                }
187            }
188
189            /// Calls `self.default_span` or returns `DUMMY_SP` if there was a fatal error
190            pub fn catch_default_span(&self, tcx: TyCtxt<'tcx>, span: Span) -> Span {
191                catch_fatal_errors(|| self.default_span(tcx, span)).unwrap_or(DUMMY_SP)
192            }
193        }
194
195        /// Holds a `QueryVTable` for each query.
196        pub struct QueryVTables<'tcx> {
197            $(
198                pub $name: $crate::query::QueryVTable<'tcx, $name::Cache<'tcx>>,
199            )*
200        }
201
202        /// Holds per-query arenas for queries with the `arena_cache` modifier.
203        #[derive(Default)]
204        pub struct QueryArenas<'tcx> {
205            $(
206                // Use the `ArenaCached` helper trait to determine the arena's value type.
207                #[cfg($arena_cache)]
208                pub $name: TypedArena<
209                    <$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Allocated,
210                >,
211            )*
212        }
213
214        pub struct Providers {
215            $(
216                /// This is the provider for the query. Use `Find references` on this to
217                /// navigate between the provider assignment and the query definition.
218                pub $name: for<'tcx> fn(
219                    TyCtxt<'tcx>,
220                    $name::LocalKey<'tcx>,
221                ) -> $name::ProvidedValue<'tcx>,
222            )*
223        }
224
225        pub struct ExternProviders {
226            $(
227                #[cfg($separate_provide_extern)]
228                pub $name: for<'tcx> fn(
229                    TyCtxt<'tcx>,
230                    $name::Key<'tcx>,
231                ) -> $name::ProvidedValue<'tcx>,
232            )*
233        }
234
235        impl Default for Providers {
236            fn default() -> Self {
237                Providers {
238                    $(
239                        $name: |_, key| {
240                            $crate::query::query_api::default_query(stringify!($name), &key)
241                        },
242                    )*
243                }
244            }
245        }
246
247        impl Default for ExternProviders {
248            fn default() -> Self {
249                ExternProviders {
250                    $(
251                        #[cfg($separate_provide_extern)]
252                        $name: |_, key| $crate::query::query_api::default_extern_query(
253                            stringify!($name),
254                            &key,
255                        ),
256                    )*
257                }
258            }
259        }
260
261        impl Copy for Providers {}
262        impl Clone for Providers {
263            fn clone(&self) -> Self { *self }
264        }
265
266        impl Copy for ExternProviders {}
267        impl Clone for ExternProviders {
268            fn clone(&self) -> Self { *self }
269        }
270
271        impl<'tcx> TyCtxt<'tcx> {
272            $(
273                $(#[$attr])*
274                #[inline(always)]
275                #[must_use]
276                pub fn $name(self, key: maybe_into_query_key!($($K)*)) -> $V {
277                    self.at(DUMMY_SP).$name(key)
278                }
279            )*
280        }
281
282        impl<'tcx> $crate::query::TyCtxtAt<'tcx> {
283            $(
284                $(#[$attr])*
285                #[inline(always)]
286                pub fn $name(self, key: maybe_into_query_key!($($K)*)) -> $V {
287                    $crate::query::erase::restore_val::<$V>($crate::query::calls::query_get_at(
288                        self.tcx,
289                        self.span,
290                        &self.tcx.query_system.query_vtables.$name,
291                        $crate::query::IntoQueryKey::into_query_key(key),
292                    ))
293                }
294            )*
295        }
296
297        impl<'tcx> $crate::query::TyCtxtEnsureOk<'tcx> {
298            $(
299                $(#[$attr])*
300                #[inline(always)]
301                pub fn $name(self, key: maybe_into_query_key!($($K)*)) {
302                    $crate::query::calls::query_ensure_ok(
303                        self.tcx,
304                        &self.tcx.query_system.query_vtables.$name,
305                        $crate::query::IntoQueryKey::into_query_key(key),
306                    )
307                }
308            )*
309        }
310
311        // Only defined when the `returns_error_guaranteed` modifier is present.
312        impl<'tcx> $crate::query::TyCtxtEnsureResult<'tcx> {
313            $(
314                #[cfg($returns_error_guaranteed)]
315                $(#[$attr])*
316                #[inline(always)]
317                pub fn $name(
318                    self,
319                    key: maybe_into_query_key!($($K)*),
320                ) -> Result<(), rustc_errors::ErrorGuaranteed> {
321                    $crate::query::calls::query_ensure_result(
322                        self.tcx,
323                        &self.tcx.query_system.query_vtables.$name,
324                        $crate::query::IntoQueryKey::into_query_key(key),
325                    )
326                }
327            )*
328        }
329
330        impl<'tcx> $crate::query::TyCtxtEnsureDone<'tcx> {
331            $(
332                $(#[$attr])*
333                #[inline(always)]
334                pub fn $name(self, key: maybe_into_query_key!($($K)*)) {
335                    // This has the same implementation as `tcx.$query(..)` as it isn't currently
336                    // beneficial to have an optimized variant due to how promotion works.
337                    let _ = self.tcx.$name(key);
338                }
339            )*
340        }
341
342        $(
343            // Only defined when the `feedable` modifier is present.
344            #[cfg($feedable)]
345            impl<'tcx, K: $crate::query::IntoQueryKey<$name::Key<'tcx>> + Copy>
346                TyCtxtFeed<'tcx, K>
347            {
348                $(#[$attr])*
349                #[inline(always)]
350                pub fn $name(self, value: $name::ProvidedValue<'tcx>) {
351                    $crate::query::calls::query_feed(
352                        self.tcx,
353                        &self.tcx.query_system.query_vtables.$name,
354                        self.key().into_query_key(),
355                        $name::provided_to_erased(self.tcx, value),
356                    );
357                }
358            }
359        )*
360    };
361}
362
363// Re-export `macro_rules!` macros as normal items, so that they can be imported normally.
364pub(crate) use define_query_api;
365pub(crate) use maybe_into_query_key;
366
367#[cold]
368pub(crate) fn default_query(name: &str, key: &dyn std::fmt::Debug) -> ! {
369    crate::util::bug::bug_fmt(format_args!("`tcx.{0}({1:?})` is not supported for this key;\nhint: 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.\nIf that\'s not the case, {0} was likely never assigned to a provider function.\n",
        name, key))bug!(
370        "`tcx.{name}({key:?})` is not supported for this key;\n\
371        hint: Queries can be either made to the local crate, or the external crate. \
372        This error means you tried to use it for one that's not supported.\n\
373        If that's not the case, {name} was likely never assigned to a provider function.\n",
374    )
375}
376
377#[cold]
378pub(crate) fn default_extern_query(name: &str, key: &dyn std::fmt::Debug) -> ! {
379    crate::util::bug::bug_fmt(format_args!("`tcx.{0}({1:?})` unsupported by its crate; perhaps the `{0}` query was never assigned a provider function",
        name, key))bug!(
380        "`tcx.{name}({key:?})` unsupported by its crate; \
381         perhaps the `{name}` query was never assigned a provider function",
382    )
383}