Remove explicit tcx bindings from query entries.

As currently written, these have big problems.
- `desc` and `cache_on_disk_if` modifiers use different syntaxes to
  introduce `tcx`.
- `desc` is mis-implemented such that the explicit binding isn't even
  necessary and the key name can be botched, as the previous two commits
  showed. (It's the `let (#tcx, #key) = (tcx, key);` line that messes
  things up.)

It's simpler and less error-prone to simply not require explicit `tcx`
bindings, and instead just make it implicitly available to these code
snippets.
This commit is contained in:
Nicholas Nethercote
2026-02-20 17:01:50 +11:00
parent cb083142f2
commit ecb778fc61
2 changed files with 20 additions and 37 deletions
+12 -33
View File
@@ -103,13 +103,11 @@ fn parse(input: ParseStream<'_>) -> Result<Self> {
struct Desc {
modifier: Ident,
tcx_binding: Option<Ident>,
expr_list: Punctuated<Expr, Token![,]>,
}
struct CacheOnDiskIf {
modifier: Ident,
tcx_binding: Option<Pat>,
block: Block,
}
@@ -192,35 +190,16 @@ macro_rules! try_insert {
if modifier == "desc" {
// Parse a description modifier like:
// `desc { |tcx| "foo {}", tcx.item_path(key) }`
// `desc { "foo {}", tcx.item_path(key) }`
let attr_content;
braced!(attr_content in input);
let tcx_binding = if attr_content.peek(Token![|]) {
attr_content.parse::<Token![|]>()?;
let tcx = attr_content.parse()?;
attr_content.parse::<Token![|]>()?;
Some(tcx)
} else {
None
};
let expr_list = attr_content.parse_terminated(Expr::parse, Token![,])?;
try_insert!(desc = Desc { modifier, tcx_binding, expr_list });
try_insert!(desc = Desc { modifier, expr_list });
} else if modifier == "cache_on_disk_if" {
// Parse a cache-on-disk modifier like:
//
// `cache_on_disk_if { true }`
// `cache_on_disk_if { key.is_local() }`
// `cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) }`
let tcx_binding = if input.peek(token::Paren) {
let args;
parenthesized!(args in input);
let tcx = Pat::parse_single(&args)?;
Some(tcx)
} else {
None
};
// `cache_on_disk_if { tcx.is_typeck_child(key.to_def_id()) }`
let block = input.parse()?;
try_insert!(cache_on_disk_if = CacheOnDiskIf { modifier, tcx_binding, block });
try_insert!(cache_on_disk_if = CacheOnDiskIf { modifier, block });
} else if modifier == "arena_cache" {
try_insert!(arena_cache = modifier);
} else if modifier == "cycle_fatal" {
@@ -313,24 +292,24 @@ fn make_helpers_for_query(query: &Query, streams: &mut HelperTokenStreams) {
erased_name.set_span(Span::call_site());
// Generate a function to check whether we should cache the query to disk, for some key.
if let Some(CacheOnDiskIf { tcx_binding, block, .. }) = modifiers.cache_on_disk_if.as_ref() {
let tcx = tcx_binding.as_ref().map(|t| quote! { #t }).unwrap_or_else(|| quote! { _ });
// we're taking `key` by reference, but some rustc types usually prefer being passed by value
if let Some(CacheOnDiskIf { block, .. }) = modifiers.cache_on_disk_if.as_ref() {
// `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".
streams.cache_on_disk_if_fns_stream.extend(quote! {
#[allow(unused_variables, rustc::pass_by_value)]
#[inline]
pub fn #erased_name<'tcx>(#tcx: TyCtxt<'tcx>, #key_pat: &crate::queries::#name::Key<'tcx>) -> bool
pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, #key_pat: &#key_ty) -> bool
#block
});
}
let Desc { tcx_binding, expr_list, .. } = &modifiers.desc;
let tcx = tcx_binding.as_ref().map_or_else(|| quote! { _ }, |t| quote! { #t });
let Desc { expr_list, .. } = &modifiers.desc;
let desc = quote! {
#[allow(unused_variables)]
pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, key: #key_ty) -> String {
let (#tcx, #key_pat) = (tcx, key);
pub fn #erased_name<'tcx>(tcx: TyCtxt<'tcx>, #key_pat: #key_ty) -> String {
format!(#expr_list)
}
};
+8 -4
View File
@@ -25,9 +25,13 @@
//! Query modifiers are special flags that alter the behavior of a query. They are parsed and processed by the `rustc_macros`
//! The main modifiers are:
//!
//! - `desc { ... }`: Sets the human-readable description for diagnostics and profiling. Required for every query.
//! - `desc { ... }`: Sets the human-readable description for diagnostics and profiling. Required
//! for every query. The block should contain a `format!`-style string literal followed by
//! optional arguments. The query key identifier is available for use within the block, as is
//! `tcx`.
//! - `arena_cache`: Use an arena for in-memory caching of the query result.
//! - `cache_on_disk_if { ... }`: Cache the query result to disk if the provided block evaluates to true.
//! - `cache_on_disk_if { ... }`: Cache the query result to disk if the provided block evaluates to
//! true. The query key identifier is available for use within the block, as is `tcx`.
//! - `cycle_fatal`: If a dependency cycle is detected, abort compilation with a fatal error.
//! - `cycle_delay_bug`: If a dependency cycle is detected, emit a delayed bug instead of aborting immediately.
//! - `cycle_stash`: If a dependency cycle is detected, stash the error for later handling.
@@ -1211,7 +1215,7 @@
query check_liveness(key: LocalDefId) -> &'tcx rustc_index::bit_set::DenseBitSet<abi::FieldIdx> {
arena_cache
desc { "checking liveness of variables in `{}`", tcx.def_path_str(key.to_def_id()) }
cache_on_disk_if(tcx) { tcx.is_typeck_child(key.to_def_id()) }
cache_on_disk_if { tcx.is_typeck_child(key.to_def_id()) }
}
/// Return the live symbols in the crate for dead code check.
@@ -1244,7 +1248,7 @@
query typeck(key: LocalDefId) -> &'tcx ty::TypeckResults<'tcx> {
desc { "type-checking `{}`", tcx.def_path_str(key) }
cache_on_disk_if(tcx) { !tcx.is_typeck_child(key.to_def_id()) }
cache_on_disk_if { !tcx.is_typeck_child(key.to_def_id()) }
}
query used_trait_imports(key: LocalDefId) -> &'tcx UnordSet<LocalDefId> {