diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 75be5a1686035..3880894572c93 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -128,6 +128,7 @@ impl Parse for List { } struct Desc { + // This ident is always `desc` but we need it for its span, for `crate::query::modifiers`. modifier: Ident, expr_list: Punctuated, } @@ -141,6 +142,7 @@ struct QueryModifiers { desc: Desc, eval_always: Option, feedable: Option, + handle_cycle_error: Option, no_force: Option, no_hash: Option, separate_provide_extern: Option, @@ -148,15 +150,18 @@ struct QueryModifiers { } fn parse_query_modifiers(input: ParseStream<'_>) -> Result { + // tidy-alphabetical-start let mut arena_cache = None; let mut cache_on_disk = None; + let mut depth_limit = None; let mut desc = None; + let mut eval_always = None; + let mut feedable = None; + let mut handle_cycle_error = None; let mut no_force = None; let mut no_hash = None; - let mut eval_always = None; - let mut depth_limit = None; let mut separate_provide_extern = None; - let mut feedable = None; + // tidy-alphabetical-end while !input.is_empty() { let modifier: Ident = input.parse()?; @@ -170,29 +175,31 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { }; } - if modifier == "desc" { + if modifier == "arena_cache" { + try_insert!(arena_cache = modifier); + } else if modifier == "cache_on_disk" { + try_insert!(cache_on_disk = modifier); + } else if modifier == "depth_limit" { + try_insert!(depth_limit = modifier); + } else if modifier == "desc" { // Parse a description modifier like: // `desc { "foo {}", tcx.item_path(key) }` let attr_content; braced!(attr_content in input); let expr_list = attr_content.parse_terminated(Expr::parse, Token![,])?; try_insert!(desc = Desc { modifier, expr_list }); - } else if modifier == "cache_on_disk" { - try_insert!(cache_on_disk = modifier); - } else if modifier == "arena_cache" { - try_insert!(arena_cache = modifier); + } else if modifier == "eval_always" { + try_insert!(eval_always = modifier); + } else if modifier == "feedable" { + try_insert!(feedable = modifier); + } else if modifier == "handle_cycle_error" { + try_insert!(handle_cycle_error = modifier); } else if modifier == "no_force" { try_insert!(no_force = modifier); } else if modifier == "no_hash" { try_insert!(no_hash = modifier); - } else if modifier == "eval_always" { - try_insert!(eval_always = modifier); - } else if modifier == "depth_limit" { - try_insert!(depth_limit = modifier); } else if modifier == "separate_provide_extern" { try_insert!(separate_provide_extern = modifier); - } else if modifier == "feedable" { - try_insert!(feedable = modifier); } else { return Err(Error::new(modifier.span(), "unknown query modifier")); } @@ -201,15 +208,18 @@ fn parse_query_modifiers(input: ParseStream<'_>) -> Result { return Err(input.error("no description provided")); }; Ok(QueryModifiers { + // tidy-alphabetical-start arena_cache, cache_on_disk, + depth_limit, desc, + eval_always, + feedable, + handle_cycle_error, no_force, no_hash, - eval_always, - depth_limit, separate_provide_extern, - feedable, + // tidy-alphabetical-end }) } @@ -238,24 +248,40 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { arena_cache, cache_on_disk, depth_limit, - desc: _, + desc, eval_always, feedable, + handle_cycle_error, no_force, no_hash, separate_provide_extern, // tidy-alphabetical-end } = &query.modifiers; + // tidy-alphabetical-start let arena_cache = arena_cache.is_some(); let cache_on_disk = cache_on_disk.is_some(); let depth_limit = depth_limit.is_some(); + let desc = { + // Put a description closure in the `desc` modifier. + let key_pat = &query.key_pat; + let key_ty = &query.key_ty; + let desc_expr_list = &desc.expr_list; + quote! { + { + #[allow(unused_variables)] + |tcx: TyCtxt<'tcx>, #key_pat: #key_ty| format!(#desc_expr_list) + } + } + }; let eval_always = eval_always.is_some(); let feedable = feedable.is_some(); + let handle_cycle_error = handle_cycle_error.is_some(); let no_force = no_force.is_some(); let no_hash = no_hash.is_some(); let returns_error_guaranteed = returns_error_guaranteed(&query.return_ty); let separate_provide_extern = separate_provide_extern.is_some(); + // tidy-alphabetical-end // Giving an input span to the modifier names in the modifier list seems // to give slightly more helpful errors when one of the callback macros @@ -268,8 +294,10 @@ fn make_modifiers_stream(query: &Query) -> proc_macro2::TokenStream { arena_cache: #arena_cache, cache_on_disk: #cache_on_disk, depth_limit: #depth_limit, + desc: #desc, eval_always: #eval_always, feedable: #feedable, + handle_cycle_error: #handle_cycle_error, no_force: #no_force, no_hash: #no_hash, returns_error_guaranteed: #returns_error_guaranteed, @@ -305,37 +333,6 @@ fn doc_comment_from_desc(list: &Punctuated) -> Result(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 @@ -366,8 +363,10 @@ fn add_to_analyzer_stream(query: &Query, analyzer_stream: &mut proc_macro2::Toke arena_cache, cache_on_disk, depth_limit, + // `desc` is handled above eval_always, feedable, + handle_cycle_error, no_force, no_hash, separate_provide_extern, @@ -409,7 +408,6 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream { let mut query_stream = quote! {}; let mut non_query_stream = quote! {}; - let mut helpers = HelperTokenStreams::default(); let mut analyzer_stream = quote! {}; let mut errors = quote! {}; @@ -462,11 +460,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 } = helpers; - TokenStream::from(quote! { /// Higher-order macro that invokes the specified macro with (a) a list of all query /// signatures (including modifiers), and (b) a list of non-query names. This allows @@ -490,17 +485,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 - } - #errors }) } diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 35e33fd2d5457..63be8a63e9f27 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -583,6 +583,7 @@ rustc_queries! { // messages about cycles that then abort.) query check_representability(key: LocalDefId) { desc { "checking if `{}` is representable", tcx.def_path_str(key) } + handle_cycle_error // We don't want recursive representability calls to be forced with // incremental compilation because, if a cycle occurs, we need the // entire cycle to be in memory for diagnostics. @@ -593,6 +594,7 @@ rustc_queries! { /// details, particularly on the modifiers. query check_representability_adt_ty(key: Ty<'tcx>) { desc { "checking if `{}` is representable", key } + handle_cycle_error no_force } @@ -1032,6 +1034,7 @@ rustc_queries! { query variances_of(def_id: DefId) -> &'tcx [ty::Variance] { desc { "computing the variances of `{}`", tcx.def_path_str(def_id) } cache_on_disk + handle_cycle_error separate_provide_extern } @@ -1164,6 +1167,7 @@ rustc_queries! { query fn_sig(key: DefId) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { desc { "computing function signature of `{}`", tcx.def_path_str(key) } cache_on_disk + handle_cycle_error separate_provide_extern } @@ -1756,6 +1760,7 @@ rustc_queries! { ) -> Result, &'tcx ty::layout::LayoutError<'tcx>> { depth_limit desc { "computing layout of `{}`", key.value } + handle_cycle_error } /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers. diff --git a/compiler/rustc_middle/src/query/modifiers.rs b/compiler/rustc_middle/src/query/modifiers.rs index 2d22a548b7351..4fd91caa94cd7 100644 --- a/compiler/rustc_middle/src/query/modifiers.rs +++ b/compiler/rustc_middle/src/query/modifiers.rs @@ -58,6 +58,14 @@ pub(crate) struct eval_always; /// Generate a `feed` method to set the query's value from another query. pub(crate) struct feedable; +/// # `handle_cycle_error` query modifier +/// +/// The default behaviour for a query cycle is to emit a cycle error and halt +/// compilation. Queries with this modifier will instead use a custom handler, +/// which must be provided at `rustc_query_impl::handle_cycle_error::$name`, +/// where `$name` is the query name. +pub(crate) struct handle_cycle_error; + /// # `no_force` query modifier /// /// Dep nodes of queries with this modifier will never be "forced" when trying diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 2e1e614b8fb4c..6b207be245ba7 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -301,8 +301,10 @@ macro_rules! define_callbacks { 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, @@ -435,8 +437,7 @@ macro_rules! define_callbacks { pub fn description(&self, tcx: TyCtxt<'tcx>) -> String { let (name, description) = ty::print::with_no_queries!(match self { $( - TaggedQueryKey::$name(key) => - (stringify!($name), _description_fns::$name(tcx, *key)), + TaggedQueryKey::$name(key) => (stringify!($name), ($desc)(tcx, *key)), )* }); if tcx.sess.verbose_internals() { diff --git a/compiler/rustc_query_impl/src/dep_kind_vtables.rs b/compiler/rustc_query_impl/src/dep_kind_vtables.rs index b70fe3008cb10..d12db3784f711 100644 --- a/compiler/rustc_query_impl/src/dep_kind_vtables.rs +++ b/compiler/rustc_query_impl/src/dep_kind_vtables.rs @@ -135,8 +135,10 @@ macro_rules! define_dep_kind_vtables { 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, diff --git a/compiler/rustc_query_impl/src/handle_cycle_error.rs b/compiler/rustc_query_impl/src/handle_cycle_error.rs index 22f8ac9837f6d..07565254969c8 100644 --- a/compiler/rustc_query_impl/src/handle_cycle_error.rs +++ b/compiler/rustc_query_impl/src/handle_cycle_error.rs @@ -9,48 +9,29 @@ use rustc_errors::{Applicability, Diag, MultiSpan, pluralize, struct_span_code_e use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_middle::bug; -use rustc_middle::queries::{QueryVTables, TaggedQueryKey}; +use rustc_middle::queries::TaggedQueryKey; use rustc_middle::query::Cycle; -use rustc_middle::query::erase::erase_val; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{ErrorGuaranteed, Span}; use crate::job::create_cycle_error; -pub(crate) fn specialize_query_vtables<'tcx>(vtables: &mut QueryVTables<'tcx>) { - vtables.fn_sig.handle_cycle_error_fn = |tcx, key, _, err| { - let guar = err.delay_as_bug(); - erase_val(fn_sig(tcx, key, guar)) - }; - - vtables.check_representability.handle_cycle_error_fn = - |tcx, _, cycle, _err| check_representability(tcx, cycle); - - vtables.check_representability_adt_ty.handle_cycle_error_fn = - |tcx, _, cycle, _err| check_representability(tcx, cycle); - - vtables.variances_of.handle_cycle_error_fn = |tcx, key, _, err| { - let _guar = err.delay_as_bug(); - erase_val(variances_of(tcx, key)) - }; - - vtables.layout_of.handle_cycle_error_fn = |tcx, _, cycle, err| { - let _guar = err.delay_as_bug(); - erase_val(Err(layout_of(tcx, cycle))) - } -} - +// Default cycle handler used for all queries that don't use the `handle_cycle_error` query +// modifier. pub(crate) fn default(err: Diag<'_>) -> ! { let guar = err.emit(); guar.raise_fatal() } -fn fn_sig<'tcx>( +pub(crate) fn fn_sig<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, - guar: ErrorGuaranteed, + _: Cycle<'tcx>, + err: Diag<'_>, ) -> ty::EarlyBinder<'tcx, ty::PolyFnSig<'tcx>> { + let guar = err.delay_as_bug(); + let err = Ty::new_error(tcx, guar); let arity = if let Some(node) = tcx.hir_get_if_local(def_id) @@ -71,7 +52,25 @@ fn fn_sig<'tcx>( ))) } -fn check_representability<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> ! { +pub(crate) fn check_representability<'tcx>( + tcx: TyCtxt<'tcx>, + _key: LocalDefId, + cycle: Cycle<'tcx>, + _err: Diag<'_>, +) { + check_representability_inner(tcx, cycle); +} + +pub(crate) fn check_representability_adt_ty<'tcx>( + tcx: TyCtxt<'tcx>, + _key: Ty<'tcx>, + cycle: Cycle<'tcx>, + _err: Diag<'_>, +) { + check_representability_inner(tcx, cycle); +} + +fn check_representability_inner<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> ! { let mut item_and_field_ids = Vec::new(); let mut representable_ids = FxHashSet::default(); for frame in &cycle.frames { @@ -102,7 +101,13 @@ fn check_representability<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> ! { guar.raise_fatal() } -fn variances_of<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx [ty::Variance] { +pub(crate) fn variances_of<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + _cycle: Cycle<'tcx>, + err: Diag<'_>, +) -> &'tcx [ty::Variance] { + let _guar = err.delay_as_bug(); let n = tcx.generics_of(def_id).own_params.len(); tcx.arena.alloc_from_iter(iter::repeat_n(ty::Bivariant, n)) } @@ -126,7 +131,13 @@ fn search_for_cycle_permutation( otherwise() } -fn layout_of<'tcx>(tcx: TyCtxt<'tcx>, cycle: Cycle<'tcx>) -> &'tcx ty::layout::LayoutError<'tcx> { +pub(crate) fn layout_of<'tcx>( + tcx: TyCtxt<'tcx>, + _key: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, + cycle: Cycle<'tcx>, + err: Diag<'_>, +) -> Result, &'tcx ty::layout::LayoutError<'tcx>> { + let _guar = err.delay_as_bug(); let diag = search_for_cycle_permutation( &cycle.frames, |frames| { diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index de03b48394b10..27bfe1451f64f 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -48,11 +48,9 @@ pub fn query_system<'tcx>( on_disk_cache: Option, incremental: bool, ) -> QuerySystem<'tcx> { - let mut query_vtables = query_impl::make_query_vtables(incremental); - handle_cycle_error::specialize_query_vtables(&mut query_vtables); QuerySystem { arenas: Default::default(), - query_vtables, + query_vtables: query_impl::make_query_vtables(incremental), side_effects: Default::default(), on_disk_cache, local_providers, diff --git a/compiler/rustc_query_impl/src/query_impl.rs b/compiler/rustc_query_impl/src/query_impl.rs index 2d3dae04181e5..101bf2c4e80f7 100644 --- a/compiler/rustc_query_impl/src/query_impl.rs +++ b/compiler/rustc_query_impl/src/query_impl.rs @@ -19,8 +19,10 @@ macro_rules! define_queries { 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, @@ -143,7 +145,6 @@ macro_rules! define_queries { -> QueryVTable<'tcx, rustc_middle::queries::$name::Cache<'tcx>> { use rustc_middle::queries::$name::Value; - QueryVTable { name: stringify!($name), eval_always: $eval_always, @@ -176,9 +177,13 @@ macro_rules! define_queries { #[cfg(not($cache_on_disk))] try_load_from_disk_fn: |_tcx, _key, _prev_index, _index| None, - // The default just emits `err` and then aborts. - // `handle_cycle_error::specialize_query_vtables` overwrites this default - // for certain queries. + #[cfg($handle_cycle_error)] + handle_cycle_error_fn: |tcx, key, cycle, err| { + use rustc_middle::query::erase::erase_val; + + erase_val($crate::handle_cycle_error::$name(tcx, key, cycle, err)) + }, + #[cfg(not($handle_cycle_error))] handle_cycle_error_fn: |_tcx, _key, _cycle, err| { $crate::handle_cycle_error::default(err) },