| //! Support for serializing the dep-graph and reloading it. | |
| // tidy-alphabetical-start | |
| #![allow(internal_features)] | |
| #![feature(adt_const_params)] | |
| #![feature(core_intrinsics)] | |
| #![feature(min_specialization)] | |
| #![feature(rustc_attrs)] | |
| #![feature(try_blocks)] | |
| // tidy-alphabetical-end | |
| use std::marker::ConstParamTy; | |
| use rustc_data_structures::sync::AtomicU64; | |
| use rustc_middle::dep_graph::{self, DepKind, DepNode, DepNodeIndex, SerializedDepNodeIndex}; | |
| use rustc_middle::queries::{ | |
| self, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, | |
| }; | |
| use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; | |
| use rustc_middle::query::plumbing::{ | |
| HashResult, QueryState, QuerySystem, QuerySystemFns, QueryVTable, | |
| }; | |
| use rustc_middle::query::{AsLocalKey, CycleError, CycleErrorHandling, QueryCache, QueryMode}; | |
| use rustc_middle::ty::TyCtxt; | |
| use rustc_span::{ErrorGuaranteed, Span}; | |
| pub use crate::dep_kind_vtables::make_dep_kind_vtables; | |
| pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack}; | |
| pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all}; | |
| use crate::plumbing::{encode_all_query_results, try_mark_green}; | |
| use crate::profiling_support::QueryKeyStringCache; | |
| pub use crate::profiling_support::alloc_self_profile_query_strings; | |
| use crate::values::Value; | |
| #[macro_use] | |
| mod plumbing; | |
| mod dep_kind_vtables; | |
| mod error; | |
| mod execution; | |
| mod job; | |
| mod profiling_support; | |
| mod values; | |
| #[derive(ConstParamTy)] // Allow this struct to be used for const-generic values. | |
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] | |
| struct QueryFlags { | |
| /// True if this query has the `anon` modifier. | |
| is_anon: bool, | |
| /// True if this query has the `depth_limit` modifier. | |
| is_depth_limit: bool, | |
| /// True if this query has the `feedable` modifier. | |
| is_feedable: bool, | |
| } | |
| /// Combines a [`QueryVTable`] with some additional compile-time booleans. | |
| /// "Dispatcher" should be understood as a near-synonym of "vtable". | |
| /// | |
| /// Baking these boolean flags into the type gives a modest but measurable | |
| /// improvement to compiler perf and compiler code size; see | |
| /// <https://github.com/rust-lang/rust/pull/151633>. | |
| struct SemiDynamicQueryDispatcher<'tcx, C: QueryCache, const FLAGS: QueryFlags> { | |
| vtable: &'tcx QueryVTable<'tcx, C>, | |
| } | |
| // Manually implement Copy/Clone, because deriving would put trait bounds on the cache type. | |
| impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Copy | |
| for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> | |
| { | |
| } | |
| impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Clone | |
| for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> | |
| { | |
| fn clone(&self) -> Self { | |
| *self | |
| } | |
| } | |
| impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> SemiDynamicQueryDispatcher<'tcx, C, FLAGS> { | |
| #[inline(always)] | |
| fn name(self) -> &'static str { | |
| self.vtable.name | |
| } | |
| #[inline(always)] | |
| fn will_cache_on_disk_for_key(self, tcx: TyCtxt<'tcx>, key: &C::Key) -> bool { | |
| self.vtable.will_cache_on_disk_for_key_fn.map_or(false, |f| f(tcx, key)) | |
| } | |
| // Don't use this method to access query results, instead use the methods on TyCtxt. | |
| #[inline(always)] | |
| fn query_state(self, qcx: QueryCtxt<'tcx>) -> &'tcx QueryState<'tcx, C::Key> { | |
| // Safety: | |
| // This is just manually doing the subfield referencing through pointer math. | |
| unsafe { | |
| &*(&qcx.tcx.query_system.states as *const QueryStates<'tcx>) | |
| .byte_add(self.vtable.query_state) | |
| .cast::<QueryState<'tcx, C::Key>>() | |
| } | |
| } | |
| // Don't use this method to access query results, instead use the methods on TyCtxt. | |
| #[inline(always)] | |
| fn query_cache(self, qcx: QueryCtxt<'tcx>) -> &'tcx C { | |
| // Safety: | |
| // This is just manually doing the subfield referencing through pointer math. | |
| unsafe { | |
| &*(&qcx.tcx.query_system.caches as *const QueryCaches<'tcx>) | |
| .byte_add(self.vtable.query_cache) | |
| .cast::<C>() | |
| } | |
| } | |
| /// Calls `tcx.$query(key)` for this query, and discards the returned value. | |
| /// See [`QueryVTable::call_query_method_fn`] for details of this strange operation. | |
| #[inline(always)] | |
| fn call_query_method(self, tcx: TyCtxt<'tcx>, key: C::Key) { | |
| (self.vtable.call_query_method_fn)(tcx, key) | |
| } | |
| /// Calls the actual provider function for this query. | |
| /// See [`QueryVTable::invoke_provider_fn`] for more details. | |
| #[inline(always)] | |
| fn invoke_provider(self, qcx: QueryCtxt<'tcx>, key: C::Key) -> C::Value { | |
| (self.vtable.invoke_provider_fn)(qcx.tcx, key) | |
| } | |
| #[inline(always)] | |
| fn try_load_from_disk( | |
| self, | |
| qcx: QueryCtxt<'tcx>, | |
| key: &C::Key, | |
| prev_index: SerializedDepNodeIndex, | |
| index: DepNodeIndex, | |
| ) -> Option<C::Value> { | |
| // `?` will return None immediately for queries that never cache to disk. | |
| self.vtable.try_load_from_disk_fn?(qcx.tcx, key, prev_index, index) | |
| } | |
| #[inline] | |
| fn is_loadable_from_disk( | |
| self, | |
| qcx: QueryCtxt<'tcx>, | |
| key: &C::Key, | |
| index: SerializedDepNodeIndex, | |
| ) -> bool { | |
| self.vtable.is_loadable_from_disk_fn.map_or(false, |f| f(qcx.tcx, key, index)) | |
| } | |
| /// Synthesize an error value to let compilation continue after a cycle. | |
| fn value_from_cycle_error( | |
| self, | |
| tcx: TyCtxt<'tcx>, | |
| cycle_error: &CycleError, | |
| guar: ErrorGuaranteed, | |
| ) -> C::Value { | |
| (self.vtable.value_from_cycle_error)(tcx, cycle_error, guar) | |
| } | |
| #[inline(always)] | |
| fn format_value(self) -> fn(&C::Value) -> String { | |
| self.vtable.format_value | |
| } | |
| #[inline(always)] | |
| fn anon(self) -> bool { | |
| FLAGS.is_anon | |
| } | |
| #[inline(always)] | |
| fn eval_always(self) -> bool { | |
| self.vtable.eval_always | |
| } | |
| #[inline(always)] | |
| fn depth_limit(self) -> bool { | |
| FLAGS.is_depth_limit | |
| } | |
| #[inline(always)] | |
| fn feedable(self) -> bool { | |
| FLAGS.is_feedable | |
| } | |
| #[inline(always)] | |
| fn dep_kind(self) -> DepKind { | |
| self.vtable.dep_kind | |
| } | |
| #[inline(always)] | |
| fn cycle_error_handling(self) -> CycleErrorHandling { | |
| self.vtable.cycle_error_handling | |
| } | |
| #[inline(always)] | |
| fn hash_result(self) -> HashResult<C::Value> { | |
| self.vtable.hash_result | |
| } | |
| fn construct_dep_node(self, tcx: TyCtxt<'tcx>, key: &C::Key) -> DepNode { | |
| DepNode::construct(tcx, self.dep_kind(), key) | |
| } | |
| } | |
| /// Provides access to vtable-like operations for a query | |
| /// (by creating a [`SemiDynamicQueryDispatcher`]), | |
| /// but also keeps track of the "unerased" value type of the query | |
| /// (i.e. the actual result type in the query declaration). | |
| /// | |
| /// This trait allows some per-query code to be defined in generic functions | |
| /// with a trait bound, instead of having to be defined inline within a macro | |
| /// expansion. | |
| /// | |
| /// There is one macro-generated implementation of this trait for each query, | |
| /// on the type `rustc_query_impl::query_impl::$name::QueryType`. | |
| trait QueryDispatcherUnerased<'tcx, C: QueryCache, const FLAGS: QueryFlags> { | |
| type UnerasedValue; | |
| const NAME: &'static &'static str; | |
| fn query_dispatcher(tcx: TyCtxt<'tcx>) -> SemiDynamicQueryDispatcher<'tcx, C, FLAGS>; | |
| fn restore_val(value: C::Value) -> Self::UnerasedValue; | |
| } | |
| pub fn query_system<'tcx>( | |
| local_providers: Providers, | |
| extern_providers: ExternProviders, | |
| on_disk_cache: Option<OnDiskCache>, | |
| incremental: bool, | |
| ) -> QuerySystem<'tcx> { | |
| QuerySystem { | |
| states: Default::default(), | |
| arenas: Default::default(), | |
| caches: Default::default(), | |
| query_vtables: make_query_vtables(), | |
| on_disk_cache, | |
| fns: QuerySystemFns { | |
| engine: engine(incremental), | |
| local_providers, | |
| extern_providers, | |
| encode_query_results: encode_all_query_results, | |
| try_mark_green, | |
| }, | |
| jobs: AtomicU64::new(1), | |
| } | |
| } | |
| rustc_middle::rustc_with_all_queries! { define_queries! } | |
| pub fn provide(providers: &mut rustc_middle::util::Providers) { | |
| providers.hooks.alloc_self_profile_query_strings = alloc_self_profile_query_strings; | |
| providers.hooks.query_key_hash_verify_all = query_key_hash_verify_all; | |
| } |