Rollup merge of #153161 - nnethercote:rejig-rustc_with_all_queries, r=Zalathar

Rejig `rustc_with_all_queries!`

There are three things relating to `rustc_with_all_queries!` that have been bugging me.

- `rustc_with_all_queries!`'s ability to receive `extra_fake_queries` lines like `[] fn Null(()) -> (),` where the only real thing is the `Null`, and everything is just pretending to be a normal query, ugh.
- `make_dep_kind_array!`: a macro produced by one macro (`define_dep_nodes!`) and used by another macro (`define_queries!`) in another crate, ugh.
- The `_dep_kind_vtable_ctors` module, which is a special module with no actual code that serves just a way of collecting vtable constructors from two different places so they can be referred to by `make_dep_kind_array!`, ugh.

By making some adjustments to how `rustc_with_all_queries!` works, all three of these things are eliminated.

r? @Zalathar
This commit is contained in:
Jonathan Brouwer
2026-03-02 20:10:35 +01:00
committed by GitHub
6 changed files with 159 additions and 96 deletions
+52 -19
View File
@@ -10,6 +10,7 @@
};
mod kw {
syn::custom_keyword!(non_query);
syn::custom_keyword!(query);
}
@@ -54,12 +55,37 @@ struct Query {
modifiers: QueryModifiers,
}
impl Parse for Query {
/// Declaration of a non-query dep kind.
/// ```ignore (illustrative)
/// /// Doc comment for `MyNonQuery`.
/// // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doc_comments
/// non_query MyNonQuery
/// // ^^^^^^^^^^ name
/// ```
struct NonQuery {
doc_comments: Vec<Attribute>,
name: Ident,
}
enum QueryEntry {
Query(Query),
NonQuery(NonQuery),
}
impl Parse for QueryEntry {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut doc_comments = check_attributes(input.call(Attribute::parse_outer)?)?;
// Try the non-query case first.
if input.parse::<kw::non_query>().is_ok() {
let name: Ident = input.parse()?;
return Ok(QueryEntry::NonQuery(NonQuery { doc_comments, name }));
}
// Parse the query declaration. Like `query type_of(key: DefId) -> Ty<'tcx>`
input.parse::<kw::query>()?;
if input.parse::<kw::query>().is_err() {
return Err(input.error("expected `query` or `non_query`"));
}
let name: Ident = input.parse()?;
// `(key: DefId)`
@@ -84,7 +110,7 @@ fn parse(input: ParseStream<'_>) -> Result<Self> {
doc_comments.push(doc_comment_from_desc(&modifiers.desc.expr_list)?);
}
Ok(Query { doc_comments, modifiers, name, key_pat, key_ty, return_ty })
Ok(QueryEntry::Query(Query { doc_comments, modifiers, name, key_pat, key_ty, return_ty }))
}
}
@@ -375,9 +401,9 @@ macro_rules! doc_link {
// macro producing a higher order macro that has all its token in the macro declaration we lose
// any meaningful spans, resulting in rust-analyzer being unable to make the connection between
// the query name and the corresponding providers field. The trick to fix this is to have
// `rustc_queries` emit a field access with the given name's span which allows it to successfully
// show references / go to definition to the corresponding provider assignment which is usually
// the more interesting place.
// `rustc_queries` emit a field access with the given name's span which allows it to
// successfully show references / go to definition to the corresponding provider assignment
// which is usually the more interesting place.
let ra_hint = quote! {
let crate::query::Providers { #name: _, .. };
};
@@ -393,9 +419,10 @@ fn #erased_name<'tcx>() #result {
}
pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
let queries = parse_macro_input!(input as List<Query>);
let queries = parse_macro_input!(input as List<QueryEntry>);
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! {};
@@ -411,6 +438,18 @@ macro_rules! assert {
}
for query in queries.0 {
let query = match query {
QueryEntry::Query(query) => query,
QueryEntry::NonQuery(NonQuery { doc_comments, name }) => {
// Get the exceptional non-query case out of the way first.
non_query_stream.extend(quote! {
#(#doc_comments)*
#name,
});
continue;
}
};
let Query { doc_comments, name, key_ty, return_ty, modifiers, .. } = &query;
// Normalize an absent return type into `-> ()` to make macro-rules parsing easier.
@@ -486,24 +525,18 @@ fn #name(#key_ty) #return_ty,
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).
///
/// This allows multiple simpler macros to each have access to the list
/// of queries.
/// 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
/// multiple simpler macros to each have access to these lists.
#[macro_export]
macro_rules! rustc_with_all_queries {
(
// The macro to invoke once, on all queries (plus extras).
// The macro to invoke once, on all queries and non-queries.
$macro:ident!
// Within [], an optional list of extra "query" signatures to
// pass to the given macro, in addition to the actual queries.
$( [$($extra_fake_queries:tt)*] )?
) => {
$macro! {
$( $($extra_fake_queries)* )?
#query_stream
queries { #query_stream }
non_queries { #non_query_stream }
}
}
}
+25 -31
View File
@@ -265,18 +265,20 @@ impl StableOrd for WorkProductId {
// Note: `$K` and `$V` are unused but present so this can be called by `rustc_with_all_queries`.
macro_rules! define_dep_nodes {
(
$(
$(#[$attr:meta])*
[$($modifiers:tt)*] fn $variant:ident($K:ty) -> $V:ty,
)*
) => {
#[macro_export]
macro_rules! make_dep_kind_array {
($mod:ident) => {[ $($mod::$variant()),* ]};
queries {
$(
$(#[$q_attr:meta])*
[$($modifiers:tt)*]
fn $q_name:ident($K:ty) -> $V:ty,
)*
}
/// This enum serves as an index into arrays built by `make_dep_kind_array`.
non_queries {
$(
$(#[$nq_attr:meta])*
$nq_name:ident,
)*
}
) => {
// This enum has more than u8::MAX variants so we need some kind of multi-byte
// encoding. The derived Encodable/Decodable uses leb128 encoding which is
// dense when only considering this enum. But DepKind is encoded in a larger
@@ -285,14 +287,18 @@ macro_rules! make_dep_kind_array {
#[allow(non_camel_case_types)]
#[repr(u16)] // Must be kept in sync with the rest of `DepKind`.
pub enum DepKind {
$( $( #[$attr] )* $variant),*
$( $(#[$nq_attr])* $nq_name, )*
$( $(#[$q_attr])* $q_name, )*
}
// This computes the number of dep kind variants. Along the way, it sanity-checks that the
// discriminants of the variants have been assigned consecutively from 0 so that they can
// be used as a dense index, and that all discriminants fit in a `u16`.
pub(crate) const DEP_KIND_NUM_VARIANTS: u16 = {
let deps = &[$(DepKind::$variant,)*];
let deps = &[
$(DepKind::$nq_name,)*
$(DepKind::$q_name,)*
];
let mut i = 0;
while i < deps.len() {
if i != deps[i].as_usize() {
@@ -306,7 +312,8 @@ pub enum DepKind {
pub(super) fn dep_kind_from_label_string(label: &str) -> Result<DepKind, ()> {
match label {
$( stringify!($variant) => Ok(self::DepKind::$variant), )*
$( stringify!($nq_name) => Ok(self::DepKind::$nq_name), )*
$( stringify!($q_name) => Ok(self::DepKind::$q_name), )*
_ => Err(()),
}
}
@@ -315,27 +322,14 @@ pub(super) fn dep_kind_from_label_string(label: &str) -> Result<DepKind, ()> {
/// DepNode groups for tests.
#[expect(non_upper_case_globals)]
pub mod label_strs {
$( pub const $variant: &str = stringify!($variant); )*
$( pub const $nq_name: &str = stringify!($nq_name); )*
$( pub const $q_name: &str = stringify!($q_name); )*
}
};
}
// Create various data structures for each query, and also for a few things
// that aren't queries. The key and return types aren't used, hence the use of `()`.
rustc_with_all_queries!(define_dep_nodes![
/// We use this for most things when incr. comp. is turned off.
[] fn Null(()) -> (),
/// We use this to create a forever-red node.
[] fn Red(()) -> (),
/// We use this to create a side effect node.
[] fn SideEffect(()) -> (),
/// We use this to create the anon node with zero dependencies.
[] fn AnonZeroDeps(()) -> (),
[] fn TraitSelect(()) -> (),
[] fn CompileCodegenUnit(()) -> (),
[] fn CompileMonoItem(()) -> (),
[] fn Metadata(()) -> (),
]);
// Create various data structures for each query, and also for a few things that aren't queries.
rustc_with_all_queries! { define_dep_nodes! }
// WARNING: `construct` is generic and does not know that `CompileCodegenUnit` takes `Symbol`s as keys.
// Be very careful changing this type signature!
+17
View File
@@ -2771,6 +2771,23 @@
cache_on_disk_if { *cnum == LOCAL_CRATE }
separate_provide_extern
}
//-----------------------------------------------------------------------------
// "Non-queries" are special dep kinds that are not queries.
//-----------------------------------------------------------------------------
/// We use this for most things when incr. comp. is turned off.
non_query Null
/// We use this to create a forever-red node.
non_query Red
/// We use this to create a side effect node.
non_query SideEffect
/// We use this to create the anon node with zero dependencies.
non_query AnonZeroDeps
non_query TraitSelect
non_query CompileCodegenUnit
non_query CompileMonoItem
non_query Metadata
}
rustc_with_all_queries! { define_callbacks! }
+9 -5
View File
@@ -375,11 +375,15 @@ macro_rules! define_callbacks {
(
// You might expect the key to be `$K:ty`, but it needs to be `$($K:tt)*` so that
// `query_helper_param_ty!` can match on specific type names.
$(
$(#[$attr:meta])*
[$($modifiers:tt)*]
fn $name:ident($($K:tt)*) -> $V:ty,
)*
queries {
$(
$(#[$attr:meta])*
[$($modifiers:tt)*]
fn $name:ident($($K:tt)*) -> $V:ty,
)*
}
// Non-queries are unused here.
non_queries { $($_:tt)* }
) => {
$(
#[allow(unused_lifetimes)]
@@ -1,3 +1,4 @@
use rustc_middle::arena::Arena;
use rustc_middle::bug;
use rustc_middle::dep_graph::{DepKindVTable, DepNodeKey, KeyFingerprintStyle};
use rustc_middle::query::QueryCache;
@@ -124,24 +125,51 @@ pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>(
}
}
/// Helper module containing a [`DepKindVTable`] constructor for each dep kind,
/// for use with [`rustc_middle::make_dep_kind_array`].
///
/// That macro will check that we gave it a constructor for every known dep kind.
mod _dep_kind_vtable_ctors {
// Re-export all of the vtable constructors for non-query and query dep kinds.
macro_rules! define_dep_kind_vtables {
(
queries {
$(
$(#[$attr:meta])*
[$($modifiers:tt)*]
fn $name:ident($K:ty) -> $V:ty,
)*
}
non_queries {
$(
$(#[$nq_attr:meta])*
$nq_name:ident,
)*
}
) => {{
// The small number of non-query vtables: `Null`, `Red`, etc.
let nq_vtables = [
$(
non_query::$nq_name(),
)*
];
// Non-query vtable constructors are defined in normal code.
pub(crate) use super::non_query::*;
// Query vtable constructors are defined via a macro.
pub(crate) use crate::_dep_kind_vtable_ctors_for_queries::*;
// The large number of query vtables.
let q_vtables: [DepKindVTable<'tcx>; _] = [
$(
$crate::dep_kind_vtables::make_dep_kind_vtable_for_query::<
$crate::query_impl::$name::VTableGetter,
>(
is_anon!([$($modifiers)*]),
if_cache_on_disk!([$($modifiers)*] true false),
is_eval_always!([$($modifiers)*]),
)
),*
];
(nq_vtables, q_vtables)
}}
}
pub fn make_dep_kind_vtables<'tcx>(
arena: &'tcx rustc_middle::arena::Arena<'tcx>,
) -> &'tcx [DepKindVTable<'tcx>] {
// Create an array of vtables, one for each dep kind (non-query and query).
let dep_kind_vtables: [DepKindVTable<'tcx>; _] =
rustc_middle::make_dep_kind_array!(_dep_kind_vtable_ctors);
arena.alloc_from_iter(dep_kind_vtables)
// Create an array of vtables, one for each dep kind (non-query and query).
pub fn make_dep_kind_vtables<'tcx>(arena: &'tcx Arena<'tcx>) -> &'tcx [DepKindVTable<'tcx>] {
let (nq_vtables, q_vtables) =
rustc_middle::rustc_with_all_queries! { define_dep_kind_vtables! };
// Non-query vtables must come before query vtables, to match the order of `DepKind`.
arena.alloc_from_iter(nq_vtables.into_iter().chain(q_vtables.into_iter()))
}
+11 -24
View File
@@ -414,15 +414,20 @@ pub(crate) fn force_from_dep_node_inner<'tcx, Q: GetQueryVTable<'tcx>>(
}
}
// Note: `$K` and `$V` are unused but present so this can be called by `rustc_with_all_queries`.
macro_rules! define_queries {
(
$(
$(#[$attr:meta])*
[$($modifiers:tt)*] fn $name:ident($K:ty) -> $V:ty,
)*
// Note: `$K` and `$V` are unused but present so this can be called by
// `rustc_with_all_queries`.
queries {
$(
$(#[$attr:meta])*
[$($modifiers:tt)*]
fn $name:ident($K:ty) -> $V:ty,
)*
}
// Non-queries are unused here.
non_queries { $($_:tt)* }
) => {
pub(crate) mod query_impl { $(pub(crate) mod $name {
use super::super::*;
use ::rustc_middle::query::erase::{self, Erased};
@@ -689,23 +694,5 @@ pub fn query_key_hash_verify_all<'tcx>(tcx: TyCtxt<'tcx>) {
})
}
}
/// Declares a dep-kind vtable constructor for each query.
mod _dep_kind_vtable_ctors_for_queries {
use ::rustc_middle::dep_graph::DepKindVTable;
use $crate::dep_kind_vtables::make_dep_kind_vtable_for_query;
$(
/// `DepKindVTable` constructor for this query.
pub(crate) fn $name<'tcx>() -> DepKindVTable<'tcx> {
use $crate::query_impl::$name::VTableGetter;
make_dep_kind_vtable_for_query::<VTableGetter>(
is_anon!([$($modifiers)*]),
if_cache_on_disk!([$($modifiers)*] true false),
is_eval_always!([$($modifiers)*]),
)
}
)*
}
}
}