| use rustc_span::bug; | |
| macro_rules! maybe_into_query_key { | |
| (DefId) => { impl $crate::query::IntoQueryKey<DefId> }; | |
| (LocalDefId) => { impl $crate::query::IntoQueryKey<LocalDefId> }; | |
| ($K:ty) => { $K }; | |
| } | |
| 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. | |
| queries { | |
| $( | |
| $(#[$attr:meta])* | |
| fn $name:ident($($K:tt)*) -> $V:ty | |
| { | |
| // Search for (QMODLIST) to find all occurrences of this query modifier list. | |
| arena_cache: $arena_cache:literal, | |
| cache_on_disk: $cache_on_disk:literal, | |
| depth_limit: $depth_limit:literal, | |
| desc: $desc:expr, | |
| eval_always: $eval_always:literal, | |
| feedable: $feedable:literal, | |
| handle_cycle_error: $handle_cycle_error:literal, | |
| no_force: $no_force:literal, | |
| no_hash: $no_hash:literal, | |
| returns_error_guaranteed: $returns_error_guaranteed:literal, | |
| separate_provide_extern: $separate_provide_extern:literal, | |
| } | |
| )* | |
| } | |
| // Non-queries are unused here. | |
| non_queries { $($_:tt)* } | |
| ) => { | |
| $( | |
| pub mod $name { | |
| use super::*; | |
| use $crate::query::erase::{self, Erased}; | |
| pub type Key<'tcx> = $($K)*; | |
| pub type Value<'tcx> = $V; | |
| /// Key type used by provider functions in `local_providers`. | |
| /// This query has the `separate_provide_extern` modifier. | |
| #[cfg($separate_provide_extern)] | |
| pub type LocalKey<'tcx> = | |
| <Key<'tcx> as $crate::query::QueryKey>::LocalQueryKey; | |
| /// Key type used by provider functions in `local_providers`. | |
| #[cfg(not($separate_provide_extern))] | |
| pub type LocalKey<'tcx> = Key<'tcx>; | |
| /// Type returned from query providers and loaded from disk-cache. | |
| #[cfg($arena_cache)] | |
| pub type ProvidedValue<'tcx> = | |
| <Value<'tcx> as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided; | |
| /// Type returned from query providers and loaded from disk-cache. | |
| #[cfg(not($arena_cache))] | |
| pub type ProvidedValue<'tcx> = Value<'tcx>; | |
| pub type Cache<'tcx> = | |
| <Key<'tcx> as $crate::query::QueryKey>::Cache<Erased<Value<'tcx>>>; | |
| /// This helper function takes a value returned by the query provider | |
| /// (or loaded from disk, or supplied by query feeding), allocates | |
| /// it in an arena if requested by the `arena_cache` modifier, and | |
| /// then returns an erased copy of it. | |
| #[inline(always)] | |
| pub fn provided_to_erased<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| provided_value: ProvidedValue<'tcx>, | |
| ) -> Erased<Value<'tcx>> { | |
| // For queries with the `arena_cache` modifier, store the | |
| // provided value in an arena and get a reference to it. | |
| #[cfg($arena_cache)] | |
| let value: Value<'tcx> = { | |
| use $crate::query::arena_cached::ArenaCached; | |
| <Value<'tcx> as ArenaCached>::alloc_in_arena( | |
| tcx, | |
| &tcx.query_system.arenas.$name, | |
| provided_value, | |
| ) | |
| }; | |
| // Otherwise, the provided value is the value (and `tcx` is unused). | |
| #[cfg(not($arena_cache))] | |
| let value: Value<'tcx> = { | |
| let _ = tcx; | |
| provided_value | |
| }; | |
| erase::erase_val(value) | |
| } | |
| // Ensure that keys grow no larger than 88 bytes by accident. | |
| // Increase this limit if necessary, but do try to keep the size low if possible | |
| #[cfg(target_pointer_width = "64")] | |
| const _: () = { | |
| if size_of::<Key<'static>>() > 88 { | |
| panic!("{}", concat!( | |
| "the query `", | |
| stringify!($name), | |
| "` has a key type `", | |
| stringify!($($K)*), | |
| "` that is too large" | |
| )); | |
| } | |
| }; | |
| // Ensure that values grow no larger than 64 bytes by accident. | |
| // Increase this limit if necessary, but do try to keep the size low if possible | |
| #[cfg(target_pointer_width = "64")] | |
| #[cfg(not(feature = "rustc_randomized_layouts"))] | |
| const _: () = { | |
| if size_of::<Value<'static>>() > 64 { | |
| panic!("{}", concat!( | |
| "the query `", | |
| stringify!($name), | |
| "` has a value type `", | |
| stringify!($V), | |
| "` that is too large" | |
| )); | |
| } | |
| }; | |
| } | |
| )* | |
| /// Identifies a query by kind and key. This is in contrast to `QueryJobId` which is just a | |
| /// number. | |
| #[allow(non_camel_case_types)] | |
| #[derive(Clone, Copy, Debug)] | |
| pub enum TaggedQueryKey<'tcx> { | |
| $( | |
| $name($name::Key<'tcx>), | |
| )* | |
| } | |
| impl<'tcx> TaggedQueryKey<'tcx> { | |
| /// Returns the name of the query this key is tagged with. | |
| /// | |
| /// This is useful for error/debug output, but don't use it to check for | |
| /// specific query names. Instead, match on the `TaggedQueryKey` variant. | |
| pub fn query_name(&self) -> &'static str { | |
| match self { | |
| $( | |
| TaggedQueryKey::$name(_) => stringify!($name), | |
| )* | |
| } | |
| } | |
| /// Formats a human-readable description of this query and its key, as | |
| /// specified by the `desc` query modifier. | |
| /// | |
| /// Used when reporting query cycle errors and similar problems. | |
| pub fn description(&self, tcx: TyCtxt<'tcx>) -> String { | |
| let (name, description) = ty::print::with_no_queries!(match self { | |
| $( | |
| TaggedQueryKey::$name(key) => (stringify!($name), ($desc)(tcx, *key)), | |
| )* | |
| }); | |
| if tcx.sess.verbose_internals() { | |
| format!("{description} [{name:?}]") | |
| } else { | |
| description | |
| } | |
| } | |
| /// Calls `self.description` or returns a fallback if there was a fatal error | |
| pub fn catch_description(&self, tcx: TyCtxt<'tcx>) -> String { | |
| catch_fatal_errors(|| self.description(tcx)).unwrap_or_else(|_| format!("<error describing {}>", self.query_name())) | |
| } | |
| /// Returns the default span for this query if `span` is a dummy span. | |
| pub fn default_span(&self, tcx: TyCtxt<'tcx>, span: Span) -> Span { | |
| if !span.is_dummy() { | |
| return span | |
| } | |
| if let TaggedQueryKey::def_span(..) = self { | |
| // The `def_span` query is used to calculate `default_span`, | |
| // so exit to avoid infinite recursion. | |
| return DUMMY_SP | |
| } | |
| match self { | |
| $( | |
| TaggedQueryKey::$name(key) => | |
| $crate::query::QueryKey::default_span(key, tcx), | |
| )* | |
| } | |
| } | |
| /// Calls `self.default_span` or returns `DUMMY_SP` if there was a fatal error | |
| pub fn catch_default_span(&self, tcx: TyCtxt<'tcx>, span: Span) -> Span { | |
| catch_fatal_errors(|| self.default_span(tcx, span)).unwrap_or(DUMMY_SP) | |
| } | |
| } | |
| /// Holds a `QueryVTable` for each query. | |
| pub struct QueryVTables<'tcx> { | |
| $( | |
| pub $name: $crate::query::QueryVTable<'tcx, $name::Cache<'tcx>>, | |
| )* | |
| } | |
| /// Holds per-query arenas for queries with the `arena_cache` modifier. | |
| #[derive(Default)] | |
| pub struct QueryArenas<'tcx> { | |
| $( | |
| // Use the `ArenaCached` helper trait to determine the arena's value type. | |
| #[cfg($arena_cache)] | |
| pub $name: TypedArena< | |
| <$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Allocated, | |
| >, | |
| )* | |
| } | |
| pub struct Providers { | |
| $( | |
| /// This is the provider for the query. Use `Find references` on this to | |
| /// navigate between the provider assignment and the query definition. | |
| pub $name: for<'tcx> fn( | |
| TyCtxt<'tcx>, | |
| $name::LocalKey<'tcx>, | |
| ) -> $name::ProvidedValue<'tcx>, | |
| )* | |
| } | |
| pub struct ExternProviders { | |
| $( | |
| #[cfg($separate_provide_extern)] | |
| pub $name: for<'tcx> fn( | |
| TyCtxt<'tcx>, | |
| $name::Key<'tcx>, | |
| ) -> $name::ProvidedValue<'tcx>, | |
| )* | |
| } | |
| impl Default for Providers { | |
| fn default() -> Self { | |
| Providers { | |
| $( | |
| $name: |_, key| { | |
| $crate::query::query_api::default_query(stringify!($name), &key) | |
| }, | |
| )* | |
| } | |
| } | |
| } | |
| impl Default for ExternProviders { | |
| fn default() -> Self { | |
| ExternProviders { | |
| $( | |
| #[cfg($separate_provide_extern)] | |
| $name: |_, key| $crate::query::query_api::default_extern_query( | |
| stringify!($name), | |
| &key, | |
| ), | |
| )* | |
| } | |
| } | |
| } | |
| impl Copy for Providers {} | |
| impl Clone for Providers { | |
| fn clone(&self) -> Self { *self } | |
| } | |
| impl Copy for ExternProviders {} | |
| impl Clone for ExternProviders { | |
| fn clone(&self) -> Self { *self } | |
| } | |
| impl<'tcx> TyCtxt<'tcx> { | |
| $( | |
| $(#[$attr])* | |
| #[inline(always)] | |
| #[must_use] | |
| pub fn $name(self, key: maybe_into_query_key!($($K)*)) -> $V { | |
| self.at(DUMMY_SP).$name(key) | |
| } | |
| )* | |
| } | |
| impl<'tcx> $crate::query::TyCtxtAt<'tcx> { | |
| $( | |
| $(#[$attr])* | |
| #[inline(always)] | |
| pub fn $name(self, key: maybe_into_query_key!($($K)*)) -> $V { | |
| $crate::query::erase::restore_val::<$V>($crate::query::calls::query_get_at( | |
| self.tcx, | |
| self.span, | |
| &self.tcx.query_system.query_vtables.$name, | |
| $crate::query::IntoQueryKey::into_query_key(key), | |
| )) | |
| } | |
| )* | |
| } | |
| impl<'tcx> $crate::query::TyCtxtEnsureOk<'tcx> { | |
| $( | |
| $(#[$attr])* | |
| #[inline(always)] | |
| pub fn $name(self, key: maybe_into_query_key!($($K)*)) { | |
| $crate::query::calls::query_ensure_ok( | |
| self.tcx, | |
| &self.tcx.query_system.query_vtables.$name, | |
| $crate::query::IntoQueryKey::into_query_key(key), | |
| ) | |
| } | |
| )* | |
| } | |
| // Only defined when the `returns_error_guaranteed` modifier is present. | |
| impl<'tcx> $crate::query::TyCtxtEnsureResult<'tcx> { | |
| $( | |
| #[cfg($returns_error_guaranteed)] | |
| $(#[$attr])* | |
| #[inline(always)] | |
| pub fn $name( | |
| self, | |
| key: maybe_into_query_key!($($K)*), | |
| ) -> Result<(), rustc_errors::ErrorGuaranteed> { | |
| $crate::query::calls::query_ensure_result( | |
| self.tcx, | |
| &self.tcx.query_system.query_vtables.$name, | |
| $crate::query::IntoQueryKey::into_query_key(key), | |
| ) | |
| } | |
| )* | |
| } | |
| impl<'tcx> $crate::query::TyCtxtEnsureDone<'tcx> { | |
| $( | |
| $(#[$attr])* | |
| #[inline(always)] | |
| pub fn $name(self, key: maybe_into_query_key!($($K)*)) { | |
| // This has the same implementation as `tcx.$query(..)` as it isn't currently | |
| // beneficial to have an optimized variant due to how promotion works. | |
| let _ = self.tcx.$name(key); | |
| } | |
| )* | |
| } | |
| $( | |
| // Only defined when the `feedable` modifier is present. | |
| #[cfg($feedable)] | |
| impl<'tcx, K: $crate::query::IntoQueryKey<$name::Key<'tcx>> + Copy> | |
| TyCtxtFeed<'tcx, K> | |
| { | |
| $(#[$attr])* | |
| #[inline(always)] | |
| pub fn $name(self, value: $name::ProvidedValue<'tcx>) { | |
| $crate::query::calls::query_feed( | |
| self.tcx, | |
| &self.tcx.query_system.query_vtables.$name, | |
| self.key().into_query_key(), | |
| $name::provided_to_erased(self.tcx, value), | |
| ); | |
| } | |
| } | |
| )* | |
| }; | |
| } | |
| // Re-export `macro_rules!` macros as normal items, so that they can be imported normally. | |
| pub(crate) use define_query_api; | |
| pub(crate) use maybe_into_query_key; | |
| #[cold] | |
| pub(crate) fn default_query(name: &str, key: &dyn std::fmt::Debug) -> ! { | |
| bug!( | |
| "`tcx.{name}({key:?})` is not supported for this key;\n\ | |
| hint: 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.\n\ | |
| If that's not the case, {name} was likely never assigned to a provider function.\n", | |
| ) | |
| } | |
| #[cold] | |
| pub(crate) fn default_extern_query(name: &str, key: &dyn std::fmt::Debug) -> ! { | |
| bug!( | |
| "`tcx.{name}({key:?})` unsupported by its crate; \ | |
| perhaps the `{name}` query was never assigned a provider function", | |
| ) | |
| } |