Skip to content

rustc_queries! is very complicated #153064

@nnethercote

Description

@nnethercote

rustc_queries! is a proc macro at the heart of the compiler's query system. The call to it in compiler/rustc_middle/src/queries.rs defines all the queries. It's also really complicated. Until very recently, it produced five different things as output.

macro_rules! rustc_with_all_queries { .. }

This is a higher-order macro. You can call it, passing it another macro which processes entries like this:

[(arena_cache)] fn registered_tools(()) -> & 'tcx ty::RegisteredTools,

macro_rules! rustc_feedable_queries { .. }

Another higher-order macro, very similar to rustc_with_all_queries, but only including entries for feedable queries.

This was removed in #153009, hooray.

mod _description_fns

A module containing one generated description function per query.

The body comes from the desc modifier. E.g. this modifier:

desc { "getting HIR ID of `{}`", tcx.def_path_str(key) }

gets turned into this function:

pub fn local_def_id_to_hir_id<'tcx>(tcx: TyCtxt<'tcx>, key: LocalDefId) -> String {                                              
    format!("getting HIR ID of `{}`", tcx.def_path_str(key))
}                                              

mod _cache_on_disk_if_fns

A module containing one generated cache check function per cache_on_disk_if query.

The body comes from the cache_on_disk_if modifier, e.g. this modifier:

cache_on_disk_if { param.is_local() }

gets turned into this function:

pub fn const_param_default<'tcx>(_: TyCtxt<'tcx>, param: &crate::queries::const_param_default::Key<'tcx>) -> bool {                                    
    param.is_local()                           
} 

mod _analyzer_hints

This is a weird module that exists purely to help rust-analyzer users find query definitions. It contains one never-executed function per query. E.g.

fn type_alias_is_lazy<'tcx>() -> bool {
    let crate::query::Providers { type_alias_is_lazy: _, .. };
    // These are the types in compiler/rustc_middle/src/query/modifiers.rs, just for documentation purposes.
    crate::query::modifiers::desc;
    crate::query::modifiers::separate_provide_extern;
    loop {}
}

The exact spans on the various identifiers are very important to get right.

Summary

This one proc macro is doing a lot. It is incredibly hard to understand. I gained my understanding by println!ing the token stream and reading through things carefully. I don't know how else you could possibly understand it fully. It's clear that in the past people have modified it with only a partial understanding, leading to inconsistencies and weirdnesses, e.g. the inconsistent tcx binding syntax that was fixed in #152958.

The good news is that a lot of this functionality can be extracted from the proc macro and done instead in the declarative macros (e.g. define_callbacks!). Declarative macros are also not easy to read and understand, but they are much easier than proc macros. I think it should be possible to reduce the output from 2 macros + 3 mods down to 1 macro. (Though I'm not 100% sure about removing mod _analyzer_hints.) #153009 was a good start.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-query-systemArea: The rustc query system (https://rustc-dev-guide.rust-lang.org/query.html)A-technical-debtArea: Internal cleanup workC-cleanupCategory: PRs that clean code up or issues documenting cleanup.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions