mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Rollup merge of #154698 - nnethercote:rm-desc-cache-modules-2, r=Zalathar
Improve workings of the `desc` query modifier and custom query cycle handlers This PR does two things involving query modifiers. - It changes how the `desc` query modifier is handled, eliminating the generated `_description_fns` module. - It adds a new `handle_cycle_error` query modifier for queries that do custom cycle handling. Details are in the individual commits. r? @Zalathar
This commit is contained in:
@@ -128,6 +128,7 @@ fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||
}
|
||||
|
||||
struct Desc {
|
||||
// This ident is always `desc` but we need it for its span, for `crate::query::modifiers`.
|
||||
modifier: Ident,
|
||||
expr_list: Punctuated<Expr, Token![,]>,
|
||||
}
|
||||
@@ -141,6 +142,7 @@ struct QueryModifiers {
|
||||
desc: Desc,
|
||||
eval_always: Option<Ident>,
|
||||
feedable: Option<Ident>,
|
||||
handle_cycle_error: Option<Ident>,
|
||||
no_force: Option<Ident>,
|
||||
no_hash: Option<Ident>,
|
||||
separate_provide_extern: Option<Ident>,
|
||||
@@ -148,15 +150,18 @@ struct QueryModifiers {
|
||||
}
|
||||
|
||||
fn parse_query_modifiers(input: ParseStream<'_>) -> Result<QueryModifiers> {
|
||||
// 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 @@ macro_rules! try_insert {
|
||||
};
|
||||
}
|
||||
|
||||
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 @@ macro_rules! try_insert {
|
||||
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<Expr, token::Comma>) -> Result<Attrib
|
||||
Ok(parse_quote! { #[doc = #doc_string] })
|
||||
}
|
||||
|
||||
/// Contains token streams that are used to accumulate per-query helper
|
||||
/// functions, to be used by the final output of `rustc_queries!`.
|
||||
///
|
||||
/// Helper items typically have the same name as the query they relate to,
|
||||
/// and expect to be interpolated into a dedicated module.
|
||||
#[derive(Default)]
|
||||
struct HelperTokenStreams {
|
||||
description_fns_stream: proc_macro2::TokenStream,
|
||||
}
|
||||
|
||||
fn make_helpers_for_query(query: &Query, streams: &mut HelperTokenStreams) {
|
||||
let Query { name, key_pat, key_ty, modifiers, .. } = &query;
|
||||
|
||||
// Replace span for `name` to make rust-analyzer ignore it.
|
||||
let mut erased_name = name.clone();
|
||||
erased_name.set_span(Span::call_site());
|
||||
|
||||
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
|
||||
@@ -366,8 +363,10 @@ macro_rules! doc_link {
|
||||
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 @@ fn #name(#key_ty) #return_ty
|
||||
}
|
||||
|
||||
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 @@ mod _analyzer_hints {
|
||||
#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
|
||||
})
|
||||
}
|
||||
|
||||
@@ -583,6 +583,7 @@
|
||||
// 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 @@
|
||||
/// 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 @@
|
||||
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 @@
|
||||
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 @@
|
||||
) -> Result<ty::layout::TyAndLayout<'tcx>, &'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.
|
||||
|
||||
@@ -58,6 +58,14 @@
|
||||
/// 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
|
||||
|
||||
@@ -301,8 +301,10 @@ fn $name:ident($($K:tt)*) -> $V:ty
|
||||
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 @@ pub fn query_name(&self) -> &'static str {
|
||||
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() {
|
||||
|
||||
@@ -135,8 +135,10 @@ fn $name:ident($K:ty) -> $V:ty
|
||||
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,
|
||||
|
||||
@@ -9,48 +9,29 @@
|
||||
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<Q, T>(
|
||||
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<ty::layout::TyAndLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
|
||||
let _guar = err.delay_as_bug();
|
||||
let diag = search_for_cycle_permutation(
|
||||
&cycle.frames,
|
||||
|frames| {
|
||||
|
||||
@@ -48,11 +48,9 @@ pub fn query_system<'tcx>(
|
||||
on_disk_cache: Option<OnDiskCache>,
|
||||
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,
|
||||
|
||||
@@ -19,8 +19,10 @@ fn $name:ident($K:ty) -> $V:ty
|
||||
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 @@ pub(crate) fn make_query_vtable<'tcx>(incremental: bool)
|
||||
-> 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 @@ pub(crate) fn make_query_vtable<'tcx>(incremental: bool)
|
||||
#[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)
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user