diff --git a/Cargo.lock b/Cargo.lock index 06d6928e1dfd5..d18276b030cb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4492,6 +4492,7 @@ version = "0.0.0" dependencies = [ "measureme", "rustc_abi", + "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_hir", diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index ebfcb50e9cde5..f859473799cae 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -273,52 +273,6 @@ fn doc_comment_from_desc(list: &Punctuated) -> Result(tcx: TyCtxt<'tcx>, #key_pat: &#key_ty) -> bool - #block - }); - } - - let Desc { expr_list, .. } = &modifiers.desc; - - let desc = quote! { - #[allow(unused_variables)] - pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, #key_pat: #key_ty) -> String { - format!(#expr_list) - } - }; - - streams.description_fns_stream.extend(quote! { - #desc - }); -} - /// Add hints for rust-analyzer fn add_to_analyzer_stream(query: &Query, analyzer_stream: &mut proc_macro2::TokenStream) { // Add links to relevant modifiers @@ -398,7 +352,6 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { let queries = parse_macro_input!(input as List); let mut query_stream = quote! {}; - let mut helpers = HelperTokenStreams::default(); let mut analyzer_stream = quote! {}; let mut errors = quote! {}; @@ -413,7 +366,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { } for query in queries.0 { - let Query { doc_comments, name, key_ty, return_ty, modifiers, .. } = &query; + let Query { doc_comments, name, key_pat, key_ty, return_ty, modifiers, .. } = &query; // Normalize an absent return type into `-> ()` to make macro-rules parsing easier. let return_ty = match return_ty { @@ -431,6 +384,10 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { } } + // Put a description closure inside the `desc` modifier: `(desc { })`. + let expr_list = &modifiers.desc.expr_list; + modifiers_out.push(quote! { (desc { |tcx, #key_pat| format!(#expr_list) }) }); + passthrough!( arena_cache, cycle_fatal, @@ -445,11 +402,23 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { return_result_from_ensure_ok, ); - // If there was a `cache_on_disk_if` modifier in the real input, pass - // on a synthetic `(cache_on_disk)` modifier that can be inspected by - // macro-rules macros. - if modifiers.cache_on_disk_if.is_some() { - modifiers_out.push(quote! { (cache_on_disk) }); + // If there was a `cache_on_disk_if` modifier, put a closure inside it: + // `(cache_on_disk { }`. + if let Some(CacheOnDiskIf { block, .. }) = &modifiers.cache_on_disk_if { + modifiers_out.push(quote! { + (cache_on_disk_if { + // `pass_by_value`: some keys are marked with `rustc_pass_by_value`, but we + // take keys by reference here. + // FIXME: `pass_by_value` is badly named; `allow(rustc::pass_by_value)` + // actually means "allow pass by reference of `rustc_pass_by_value` types". + // + // The type annotations are required to avoid compile errors, which is annoying + // because it necessitates extra `use` items in the file using + // `rustc_with_all_queries!`. + #[allow(rustc::pass_by_value)] + |tcx: TyCtxt<'_>, #key_pat: &#key_ty| #block + }) + }); } // This uses the span of the query definition for the commas, @@ -482,11 +451,8 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { } add_to_analyzer_stream(&query, &mut analyzer_stream); - make_helpers_for_query(&query, &mut helpers); } - let HelperTokenStreams { description_fns_stream, cache_on_disk_if_fns_stream } = helpers; - TokenStream::from(quote! { /// Higher-order macro that invokes the specified macro with a prepared /// list of all query signatures (including modifiers). @@ -516,25 +482,6 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { #analyzer_stream } - /// Functions that format a human-readable description of each query - /// and its key, as specified by the `desc` query modifier. - /// - /// (The leading `_` avoids collisions with actual query names when - /// expanded in `rustc_middle::queries`, and makes this macro-generated - /// module easier to search for.) - pub mod _description_fns { - use super::*; - #description_fns_stream - } - - // FIXME(Zalathar): Instead of declaring these functions directly, can - // we put them in a macro and then expand that macro downstream in - // `rustc_query_impl`, where the functions are actually used? - pub mod _cache_on_disk_if_fns { - use super::*; - #cache_on_disk_if_fns_stream - } - #errors }) } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index e303a8aeab917..ad6cd198c6567 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -96,7 +96,6 @@ use rustc_session::cstore::{ CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib, }; use rustc_session::lint::LintExpectationId; -use rustc_span::def_id::LOCAL_CRATE; use rustc_span::source_map::Spanned; use rustc_span::{DUMMY_SP, LocalExpnId, Span, Symbol}; use rustc_target::spec::PanicStrategy; @@ -120,7 +119,6 @@ use crate::mir::interpret::{ use crate::mir::mono::{ CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono, }; -use crate::query::describe_as_module; use crate::query::plumbing::CyclePlaceholder; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, @@ -135,7 +133,6 @@ use crate::traits::{ }; use crate::ty::fast_reject::SimplifiedType; use crate::ty::layout::ValidityRequirement; -use crate::ty::print::PrintTraitRefExt; use crate::ty::util::AlwaysRequiresDrop; use crate::ty::{ self, CrateInherentImpls, GenericArg, GenericArgsRef, LitToConstInput, PseudoCanonicalInput, diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 9141f49bd45de..a3fa6df0f662f 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -63,18 +63,6 @@ pub enum CycleErrorHandling { Stash, } -pub type WillCacheOnDiskForKeyFn<'tcx, Key> = fn(tcx: TyCtxt<'tcx>, key: &Key) -> bool; - -pub type TryLoadFromDiskFn<'tcx, Key, Value> = fn( - tcx: TyCtxt<'tcx>, - key: &Key, - prev_index: SerializedDepNodeIndex, - index: DepNodeIndex, -) -> Option; - -pub type IsLoadableFromDiskFn<'tcx, Key> = - fn(tcx: TyCtxt<'tcx>, key: &Key, index: SerializedDepNodeIndex) -> bool; - pub type HashResult = Option, &V) -> Fingerprint>; #[derive(Clone, Debug)] @@ -129,7 +117,7 @@ pub struct QueryVTable<'tcx, C: QueryCache> { pub cycle_error_handling: CycleErrorHandling, pub state: QueryState<'tcx, C::Key>, pub cache: C, - pub will_cache_on_disk_for_key_fn: Option>, + pub will_cache_on_disk_for_key_fn: fn(tcx: TyCtxt<'tcx>, key: &C::Key) -> bool, /// Function pointer that calls `tcx.$query(key)` for this query and /// discards the returned value. @@ -145,8 +133,16 @@ pub struct QueryVTable<'tcx, C: QueryCache> { /// This should be the only code that calls the provider function. pub invoke_provider_fn: fn(tcx: TyCtxt<'tcx>, key: C::Key) -> C::Value, - pub try_load_from_disk_fn: Option>, - pub is_loadable_from_disk_fn: Option>, + pub try_load_from_disk_fn: fn( + tcx: TyCtxt<'tcx>, + key: &C::Key, + prev_index: SerializedDepNodeIndex, + index: DepNodeIndex, + ) -> Option, + + pub is_loadable_from_disk_fn: + fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool, + pub hash_result: HashResult, pub value_from_cycle_error: fn(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> C::Value, @@ -174,43 +170,6 @@ impl<'tcx, C: QueryCache> fmt::Debug for QueryVTable<'tcx, C> { } impl<'tcx, C: QueryCache> QueryVTable<'tcx, C> { - #[inline(always)] - pub fn will_cache_on_disk_for_key(&self, tcx: TyCtxt<'tcx>, key: &C::Key) -> bool { - self.will_cache_on_disk_for_key_fn.map_or(false, |f| f(tcx, key)) - } - - #[inline(always)] - pub fn try_load_from_disk( - &self, - tcx: TyCtxt<'tcx>, - key: &C::Key, - prev_index: SerializedDepNodeIndex, - index: DepNodeIndex, - ) -> Option { - // `?` will return None immediately for queries that never cache to disk. - self.try_load_from_disk_fn?(tcx, key, prev_index, index) - } - - #[inline] - pub fn is_loadable_from_disk( - &self, - tcx: TyCtxt<'tcx>, - key: &C::Key, - index: SerializedDepNodeIndex, - ) -> bool { - self.is_loadable_from_disk_fn.map_or(false, |f| f(tcx, key, index)) - } - - /// Synthesize an error value to let compilation continue after a cycle. - pub fn value_from_cycle_error( - &self, - tcx: TyCtxt<'tcx>, - cycle_error: &CycleError, - guar: ErrorGuaranteed, - ) -> C::Value { - (self.value_from_cycle_error)(tcx, cycle_error, guar) - } - pub fn construct_dep_node(&self, tcx: TyCtxt<'tcx>, key: &C::Key) -> DepNode { DepNode::construct(tcx, self.dep_kind, key) } diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml index 449d2ed5334a8..0b7efa0c20f89 100644 --- a/compiler/rustc_query_impl/Cargo.toml +++ b/compiler/rustc_query_impl/Cargo.toml @@ -7,6 +7,7 @@ edition = "2024" # tidy-alphabetical-start measureme = "12.0.1" rustc_abi = { path = "../rustc_abi" } +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_query_impl/src/execution.rs b/compiler/rustc_query_impl/src/execution.rs index 655dbe52df3e6..cd52fca9a97e6 100644 --- a/compiler/rustc_query_impl/src/execution.rs +++ b/compiler/rustc_query_impl/src/execution.rs @@ -120,7 +120,7 @@ fn handle_cycle_error<'tcx, C: QueryCache>( match query.cycle_error_handling { CycleErrorHandling::Error => { let guar = error.emit(); - query.value_from_cycle_error(tcx, cycle_error, guar) + (query.value_from_cycle_error)(tcx, cycle_error, guar) } CycleErrorHandling::Fatal => { error.emit(); @@ -129,7 +129,7 @@ fn handle_cycle_error<'tcx, C: QueryCache>( } CycleErrorHandling::DelayBug => { let guar = error.delay_as_bug(); - query.value_from_cycle_error(tcx, cycle_error, guar) + (query.value_from_cycle_error)(tcx, cycle_error, guar) } CycleErrorHandling::Stash => { let guar = if let Some(root) = cycle_error.cycle.first() @@ -139,7 +139,7 @@ fn handle_cycle_error<'tcx, C: QueryCache>( } else { error.emit() }; - query.value_from_cycle_error(tcx, cycle_error, guar) + (query.value_from_cycle_error)(tcx, cycle_error, guar) } } } @@ -506,7 +506,9 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache>( // First we try to load the result from the on-disk cache. // Some things are never cached on disk. - if let Some(result) = query.try_load_from_disk(tcx, key, prev_dep_node_index, dep_node_index) { + if let Some(result) = + (query.try_load_from_disk_fn)(tcx, key, prev_dep_node_index, dep_node_index) + { if std::intrinsics::unlikely(tcx.sess.opts.unstable_opts.query_dep_graph) { dep_graph_data.mark_debug_loaded_from_disk(*dep_node) } @@ -539,7 +541,7 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache>( // We always expect to find a cached result for things that // can be forced from `DepNode`. debug_assert!( - !query.will_cache_on_disk_for_key(tcx, key) + !(query.will_cache_on_disk_for_key_fn)(tcx, key) || !tcx.key_fingerprint_style(dep_node.kind).reconstructible(), "missing on-disk cache entry for {dep_node:?}" ); @@ -547,7 +549,7 @@ fn try_load_from_disk_and_cache_in_memory<'tcx, C: QueryCache>( // Sanity check for the logic in `ensure`: if the node is green and the result loadable, // we should actually be able to load it. debug_assert!( - !query.is_loadable_from_disk(tcx, key, prev_dep_node_index), + !(query.is_loadable_from_disk_fn)(tcx, key, prev_dep_node_index), "missing on-disk cache entry for loadable {dep_node:?}" ); @@ -644,7 +646,7 @@ fn check_if_ensure_can_skip_execution<'tcx, C: QueryCache>( // In ensure-done mode, we can only skip execution for this key if // there's a disk-cached value available to load later if needed, // which guarantees the query provider will never run for this key. - let is_loadable = query.is_loadable_from_disk(tcx, key, serialized_dep_node_index); + let is_loadable = (query.is_loadable_from_disk_fn)(tcx, key, serialized_dep_node_index); EnsureCanSkip { skip_execution: is_loadable, dep_node: Some(dep_node) } } } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 890c0b2ef92c7..55e59b58b7817 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -5,17 +5,25 @@ #![feature(core_intrinsics)] #![feature(min_specialization)] #![feature(rustc_attrs)] +#![feature(stmt_expr_attributes)] #![feature(try_blocks)] // tidy-alphabetical-end +use rustc_ast::tokenstream::TokenStream; use rustc_data_structures::sync::AtomicU64; +use rustc_hir::def::DefKind; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_middle::dep_graph; +use rustc_middle::mir::interpret::GlobalId; +use rustc_middle::mir::mono::CollectionMode; use rustc_middle::queries::{self, ExternProviders, Providers}; use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; use rustc_middle::query::plumbing::{QuerySystem, QuerySystemFns, QueryVTable}; -use rustc_middle::query::{AsLocalKey, QueryCache, QueryMode}; -use rustc_middle::ty::TyCtxt; -use rustc_span::Span; +use rustc_middle::query::{AsLocalKey, QueryCache, QueryMode, describe_as_module}; +use rustc_middle::ty::print::PrintTraitRefExt; +use rustc_middle::ty::{self, PseudoCanonicalInput, TyCtxt}; +use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LocalModDefId}; +use rustc_span::{LocalExpnId, Span}; pub use crate::dep_kind_vtables::make_dep_kind_vtables; pub use crate::job::{QueryJobMap, break_query_cycles, print_query_stack}; diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 7b5fd76c31958..c5c0ca4cf24ed 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -242,32 +242,52 @@ macro_rules! call_provider { /// Expands to one of two token trees, depending on whether the current query /// has the `cache_on_disk_if` modifier. -macro_rules! if_cache_on_disk { +macro_rules! if_cache_on_disk_if { ([] $yes:tt $no:tt) => { $no }; - // The `cache_on_disk_if` modifier generates a synthetic `(cache_on_disk)`, - // modifier, for use by this macro and similar macros. - ([(cache_on_disk) $($rest:tt)*] $yes:tt $no:tt) => { + // Ignore the closure. + ([(cache_on_disk_if { $_:expr }) $($rest:tt)*] $yes:tt $no:tt) => { $yes }; ([$other:tt $($modifiers:tt)*] $yes:tt $no:tt) => { - if_cache_on_disk!([$($modifiers)*] $yes $no) + if_cache_on_disk_if!([$($modifiers)*] $yes $no) + }; +} + +/// Extract the closure from the `cache_on_disk_if` modifier if present, otherwise generate a +/// trivial failing closure. +macro_rules! cache_on_disk_if_fn { + ([] $($item:tt)*) => { + |_tcx, _key| false + }; + ([(cache_on_disk_if { $cache_closure:expr }) $($rest:tt)*]) => { + $cache_closure + }; + ([$other:tt $($modifiers:tt)*]) => { + cache_on_disk_if_fn! { [$($modifiers)*] } }; } /// Conditionally expands to some token trees, if the current query has the /// `cache_on_disk_if` modifier. -macro_rules! item_if_cache_on_disk { +macro_rules! item_if_cache_on_disk_if { ([] $($item:tt)*) => {}; - ([(cache_on_disk) $($rest:tt)*] $($item:tt)*) => { + ([(cache_on_disk_if { $_:expr }) $($rest:tt)*] $($item:tt)*) => { $($item)* }; ([$other:tt $($modifiers:tt)*] $($item:tt)*) => { - item_if_cache_on_disk! { [$($modifiers)*] $($item)* } + item_if_cache_on_disk_if! { [$($modifiers)*] $($item)* } }; } +/// Extract the description closure from the `desc` modifier, which must be present. +macro_rules! desc_fn { + ([] $($item:tt)*) => { compile_error!("query missing `desc`") }; + ([(desc { $desc_fn_closure:expr }) $($rest:tt)*]) => { $desc_fn_closure }; + ([$other:tt $($modifiers:tt)*]) => { desc_fn! { [$($modifiers)*] } }; +} + /// The deferred part of a deferred query stack frame. fn mk_query_stack_frame_extra<'tcx, Cache>( (tcx, vtable, key): (TyCtxt<'tcx>, &'tcx QueryVTable<'tcx, Cache>, Cache::Key), @@ -338,7 +358,7 @@ pub(crate) fn encode_query_results_inner<'a, 'tcx, C, V>( assert!(all_inactive(&query.state)); query.cache.iter(&mut |key, value, dep_node| { - if query.will_cache_on_disk_for_key(tcx, key) { + if (query.will_cache_on_disk_for_key_fn)(tcx, key) { let dep_node = SerializedDepNodeIndex::new(dep_node.index()); // Record position of the cache entry. @@ -391,7 +411,7 @@ pub(crate) fn try_load_from_on_disk_cache_inner<'tcx, C: QueryCache>( dep_node.key_fingerprint ) }); - if query.will_cache_on_disk_for_key(tcx, &key) { + if (query.will_cache_on_disk_for_key_fn)(tcx, &key) { // Call `tcx.$query(key)` for its side-effect of loading the disk-cached // value into memory. (query.call_query_method_fn)(tcx, key); @@ -561,21 +581,18 @@ macro_rules! define_queries { cycle_error_handling: cycle_error_handling!([$($modifiers)*]), state: Default::default(), cache: Default::default(), - will_cache_on_disk_for_key_fn: if_cache_on_disk!([$($modifiers)*] { - Some(::rustc_middle::queries::_cache_on_disk_if_fns::$name) - } { - None - }), + will_cache_on_disk_for_key_fn: cache_on_disk_if_fn!([$($modifiers)*]), call_query_method_fn: |tcx, key| { // Call the query method for its side-effect of loading a value // from disk-cache; the caller doesn't need the value. let _ = tcx.$name(key); }, invoke_provider_fn: self::invoke_provider_fn::__rust_begin_short_backtrace, - try_load_from_disk_fn: if_cache_on_disk!([$($modifiers)*] { - Some(|tcx, key, prev_index, index| { + try_load_from_disk_fn: if_cache_on_disk_if!([$($modifiers)*] { + |tcx, key, prev_index, index| { // Check the `cache_on_disk_if` condition for this key. - if !::rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) { + let cache_on_disk_if_fn = cache_on_disk_if_fn!([$($modifiers)*]); + if !cache_on_disk_if_fn(tcx, key) { return None; } @@ -584,25 +601,28 @@ macro_rules! define_queries { // Arena-alloc the value if appropriate, and erase it. Some(queries::$name::provided_to_erased(tcx, value)) - }) + } } { - None + |_tcx, _key, _prev_index, _index| None }), - is_loadable_from_disk_fn: if_cache_on_disk!([$($modifiers)*] { - Some(|tcx, key, index| -> bool { - ::rustc_middle::queries::_cache_on_disk_if_fns::$name(tcx, key) && + is_loadable_from_disk_fn: if_cache_on_disk_if!([$($modifiers)*] { + |tcx, key, index| -> bool { + let cache_on_disk_if_fn = cache_on_disk_if_fn!([$($modifiers)*]); + cache_on_disk_if_fn(tcx, key) && $crate::plumbing::loadable_from_disk(tcx, index) - }) + } } { - None + |_tcx, _key, _index| false }), value_from_cycle_error: |tcx, cycle, guar| { - let result: queries::$name::Value<'tcx> = Value::from_cycle_error(tcx, cycle, guar); + let result: queries::$name::Value<'tcx> = + Value::from_cycle_error(tcx, cycle, guar); erase::erase_val(result) }, hash_result: hash_result!([$($modifiers)*][queries::$name::Value<'tcx>]), - format_value: |value| format!("{:?}", erase::restore_val::>(*value)), - description_fn: $crate::queries::_description_fns::$name, + format_value: |value| + format!("{:?}", erase::restore_val::>(*value)), + description_fn: desc_fn!([$($modifiers)*]), execute_query_fn: if incremental { query_impl::$name::get_query_incr::__rust_end_short_backtrace } else { @@ -669,7 +689,7 @@ macro_rules! define_queries { ) } - item_if_cache_on_disk! { [$($modifiers)*] + item_if_cache_on_disk_if! { [$($modifiers)*] pub(crate) fn encode_query_results<'tcx>( tcx: TyCtxt<'tcx>, encoder: &mut CacheEncoder<'_, 'tcx>, @@ -730,7 +750,7 @@ macro_rules! define_queries { > ] = &[ $( - if_cache_on_disk!([$($modifiers)*] { + if_cache_on_disk_if!([$($modifiers)*] { Some(query_impl::$name::encode_query_results) } { None