//! Support for serializing the dep-graph and reloading it. // tidy-alphabetical-start #![allow(internal_features)] #![feature(min_specialization)] #![feature(rustc_attrs)] // tidy-alphabetical-end use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::{self, DepKind, DepKindVTable, DepNodeIndex}; use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; use rustc_middle::query::plumbing::{QuerySystem, QuerySystemFns, QueryVTable}; use rustc_middle::query::{ AsLocalKey, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, queries, }; use rustc_middle::ty::TyCtxt; use rustc_query_system::Value; use rustc_query_system::dep_graph::SerializedDepNodeIndex; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ CycleError, CycleErrorHandling, HashResult, QueryCache, QueryDispatcher, QueryMap, QueryMode, QueryState, get_query_incr, get_query_non_incr, }; use rustc_span::{ErrorGuaranteed, Span}; use crate::plumbing::{__rust_begin_short_backtrace, encode_all_query_results, try_mark_green}; use crate::profiling_support::QueryKeyStringCache; #[macro_use] mod plumbing; pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all}; mod profiling_support; pub use self::profiling_support::alloc_self_profile_query_strings; /// Combines a [`QueryVTable`] with some additional compile-time booleans /// to implement [`QueryDispatcher`], for use by code in [`rustc_query_system`]. /// /// Baking these boolean flags into the type gives a modest but measurable /// improvement to compiler perf and compiler code size; see /// . struct SemiDynamicQueryDispatcher< 'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool, > { vtable: &'tcx QueryVTable<'tcx, C>, } // Manually implement Copy/Clone, because deriving would put trait bounds on the cache type. impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Copy for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> { } impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Clone for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> { fn clone(&self) -> Self { *self } } // This is `impl QueryDispatcher for SemiDynamicQueryDispatcher`. impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> QueryDispatcher<'tcx> for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> where for<'a> C::Key: HashStable>, { type Qcx = QueryCtxt<'tcx>; type Key = C::Key; type Value = C::Value; type Cache = C; #[inline(always)] fn name(self) -> &'static str { self.vtable.name } #[inline(always)] fn will_cache_on_disk_for_key(self, tcx: TyCtxt<'tcx>, key: &Self::Key) -> bool { self.vtable.will_cache_on_disk_for_key_fn.map_or(false, |f| f(tcx, key)) } #[inline(always)] fn query_state<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a QueryState<'tcx, Self::Key> where QueryCtxt<'tcx>: 'a, { // 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::>() } } #[inline(always)] fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache { // 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::() } } #[inline(always)] fn execute_query(self, tcx: TyCtxt<'tcx>, key: Self::Key) -> Self::Value { (self.vtable.execute_query)(tcx, key) } #[inline(always)] fn compute(self, qcx: QueryCtxt<'tcx>, key: Self::Key) -> Self::Value { (self.vtable.compute)(qcx.tcx, key) } #[inline(always)] fn try_load_from_disk( self, qcx: QueryCtxt<'tcx>, key: &Self::Key, prev_index: SerializedDepNodeIndex, index: DepNodeIndex, ) -> Option { // `?` 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: &Self::Key, index: SerializedDepNodeIndex, ) -> bool { self.vtable.is_loadable_from_disk_fn.map_or(false, |f| f(qcx.tcx, key, index)) } fn value_from_cycle_error( self, tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed, ) -> Self::Value { (self.vtable.value_from_cycle_error)(tcx, cycle_error, guar) } #[inline(always)] fn format_value(self) -> fn(&Self::Value) -> String { self.vtable.format_value } #[inline(always)] fn anon(self) -> bool { ANON } #[inline(always)] fn eval_always(self) -> bool { self.vtable.eval_always } #[inline(always)] fn depth_limit(self) -> bool { DEPTH_LIMIT } #[inline(always)] fn feedable(self) -> bool { 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 { self.vtable.hash_result } } /// Provides access to vtable-like operations for a query /// (by creating a [`QueryDispatcher`]), /// 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> { type UnerasedValue; type Dispatcher: QueryDispatcher<'tcx, Qcx = QueryCtxt<'tcx>>; const NAME: &'static &'static str; fn query_dispatcher(tcx: TyCtxt<'tcx>) -> Self::Dispatcher; fn restore_val( value: >::Value, ) -> Self::UnerasedValue; } pub fn query_system<'a>( local_providers: Providers, extern_providers: ExternProviders, on_disk_cache: Option, incremental: bool, ) -> QuerySystem<'a> { 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; }