Skip to main content

rustc_middle/query/
query_api.rs

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