Refinement of Providers into Providers and ExternProviders

Signed-off-by: xizheyin <xizheyin@smail.nju.edu.cn>
This commit is contained in:
xizheyin 2025-06-15 20:40:13 +08:00
parent 59756939d5
commit 07e05bbc46
1 changed files with 39 additions and 32 deletions

View File

@ -71,22 +71,27 @@ are cheaply cloneable; insert an `Rc` if necessary).
If, however, the query is *not* in the cache, then the compiler will If, however, the query is *not* in the cache, then the compiler will
call the corresponding **provider** function. A provider is a function call the corresponding **provider** function. A provider is a function
implemented in a specific module and **manually registered** into the implemented in a specific module and **manually registered** into either
[`Providers`][providers_struct] struct during compiler initialization. the [`Providers`][providers_struct] struct (for local crate queries) or
The macro system generates the [`Providers`][providers_struct] struct, the [`ExternProviders`][extern_providers_struct] struct (for external crate queries)
which acts as a function table for all query implementations, where each during compiler initialization. The macro system generates both structs,
which act as function tables for all query implementations, where each
field is a function pointer to the actual provider. field is a function pointer to the actual provider.
**Note:** The `Providers` struct is generated by macros and acts as a function table for all query implementations. [providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.Providers.html
It is **not** a Rust trait, but a plain struct with function pointer fields. [extern_providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.ExternProviders.html
**Note:** Both the `Providers` and `ExternProviders` structs are generated by macros and act as function tables for all query implementations.
They are **not** Rust traits, but plain structs with function pointer fields.
**Providers are defined per-crate.** The compiler maintains, **Providers are defined per-crate.** The compiler maintains,
internally, a table of providers for every crate, at least internally, a table of providers for every crate, at least
conceptually. Right now, there are really two sets: the providers for conceptually. There are two sets of providers:
queries about the **local crate** (that is, the one being compiled) - The `Providers` struct for queries about the **local crate** (that is, the one being compiled)
and providers for queries about **external crates** (that is, - The `ExternProviders` struct for queries about **external crates** (that is,
dependencies of the local crate). Note that what determines the crate dependencies of the local crate)
that a query is targeting is not the *kind* of query, but the *key*.
Note that what determines the crate that a query is targeting is not the *kind* of query, but the *key*.
For example, when you invoke `tcx.type_of(def_id)`, that could be a For example, when you invoke `tcx.type_of(def_id)`, that could be a
local query or an external query, depending on what crate the `def_id` local query or an external query, depending on what crate the `def_id`
is referring to (see the [`self::keys::Key`][Key] trait for more is referring to (see the [`self::keys::Key`][Key] trait for more
@ -119,22 +124,22 @@ they define both a `provide` and a `provide_extern` function, through
### How providers are set up ### How providers are set up
When the tcx is created, it is given the providers by its creator using When the tcx is created, it is given both the local and external providers by its creator using
the [`Providers`][providers_struct] struct. This struct is generated by the `Providers` struct from `rustc_middle::util`. This struct contains both the local and external providers:
the macros here, but it is basically a big list of function pointers:
[providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.Providers.html
```rust,ignore ```rust,ignore
struct Providers { pub struct Providers {
type_of: for<'tcx> fn(TyCtxt<'tcx>, DefId) -> Ty<'tcx>, pub queries: crate::query::Providers, // Local crate providers
// ... one field for each query pub extern_queries: crate::query::ExternProviders, // External crate providers
pub hooks: crate::hooks::Providers,
} }
``` ```
Each of these provider structs is generated by the macros and contains function pointers for their respective queries.
#### How are providers registered? #### How are providers registered?
The `Providers` struct is filled in during compiler initialization, mainly by the `rustc_driver` crate. The provider structs are filled in during compiler initialization, mainly by the `rustc_driver` crate.
But the actual provider functions are implemented in various `rustc_*` crates (like `rustc_middle`, `rustc_hir_analysis`, etc). But the actual provider functions are implemented in various `rustc_*` crates (like `rustc_middle`, `rustc_hir_analysis`, etc).
To register providers, each crate exposes a [`provide`][provide_fn] function that looks like this: To register providers, each crate exposes a [`provide`][provide_fn] function that looks like this:
@ -142,17 +147,20 @@ To register providers, each crate exposes a [`provide`][provide_fn] function tha
[provide_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/fn.provide.html [provide_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/fn.provide.html
```rust,ignore ```rust,ignore
pub fn provide(providers: &mut Providers) { pub fn provide(providers: &mut rustc_middle::util::Providers) {
*providers = Providers { providers.queries.type_of = type_of;
type_of, // ... add more local providers here
// ... add more providers here
..*providers providers.extern_queries.type_of = extern_type_of;
}; // ... add more external providers here
providers.hooks.some_hook = some_hook;
// ... add more hooks here
} }
``` ```
- This function takes a mutable reference to the `Providers` struct and sets the fields to point to the correct provider functions. - This function takes a mutable reference to the `Providers` struct and sets the fields to point to the correct provider functions.
- You can also assign fields individually, e.g. `providers.type_of = type_of;`. - You can assign fields individually for each provider type (local, external, and hooks).
#### Adding a new provider #### Adding a new provider
@ -164,11 +172,10 @@ Suppose you want to add a new query called `fubar`. You would:
``` ```
2. Register it in the `provide` function: 2. Register it in the `provide` function:
```rust,ignore ```rust,ignore
pub fn provide(providers: &mut Providers) { pub fn provide(providers: &mut rustc_middle::util::Providers) {
*providers = Providers { providers.queries.fubar = fubar;
fubar, // If you need an external provider:
..*providers providers.extern_queries.fubar = extern_fubar;
};
} }
``` ```