Skip to content

Commit 72110fd

Browse files
xizheyinBoxyUwU
andcommitted
Update and add more details for Providers
Signed-off-by: xizheyin <xizheyin@smail.nju.edu.cn> Co-authored-by: Boxy <rust@boxyuwu.dev>
1 parent d6acec2 commit 72110fd

File tree

1 file changed

+110
-32
lines changed

1 file changed

+110
-32
lines changed

src/query.md

Lines changed: 110 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -69,22 +69,24 @@ are cheaply cloneable; insert an `Rc` if necessary).
6969

7070
If, however, the query is *not* in the cache, then the compiler will
7171
call the corresponding **provider** function. A provider is a function
72-
implemented in a specific module and **manually registered** into the
73-
[`Providers`][providers_struct] struct during compiler initialization.
74-
The macro system generates the [`Providers`][providers_struct] struct,
75-
which acts as a function table for all query implementations, where each
72+
implemented in a specific module and **manually registered** into either
73+
the [`Providers`][providers_struct] struct (for local crate queries) or
74+
the [`ExternProviders`][extern_providers_struct] struct (for external crate queries)
75+
during compiler initialization. The macro system generates both structs,
76+
which act as function tables for all query implementations, where each
7677
field is a function pointer to the actual provider.
7778

78-
**Note:** The `Providers` struct is generated by macros and acts as a function table for all query implementations.
79-
It is **not** a Rust trait, but a plain struct with function pointer fields.
79+
**Note:** Both the `Providers` and `ExternProviders` structs are generated by macros and act as function tables for all query implementations.
80+
They are **not** Rust traits, but plain structs with function pointer fields.
8081

8182
**Providers are defined per-crate.** The compiler maintains,
8283
internally, a table of providers for every crate, at least
83-
conceptually. Right now, there are really two sets: the providers for
84-
queries about the **local crate** (that is, the one being compiled)
85-
and providers for queries about **external crates** (that is,
86-
dependencies of the local crate). Note that what determines the crate
87-
that a query is targeting is not the *kind* of query, but the *key*.
84+
conceptually. There are two sets of providers:
85+
- The `Providers` struct for queries about the **local crate** (that is, the one being compiled)
86+
- The `ExternProviders` struct for queries about **external crates** (that is,
87+
dependencies of the local crate)
88+
89+
Note that what determines the crate that a query is targeting is not the *kind* of query, but the *key*.
8890
For example, when you invoke `tcx.type_of(def_id)`, that could be a
8991
local query or an external query, depending on what crate the `def_id`
9092
is referring to (see the [`self::keys::Key`][Key] trait for more
@@ -117,59 +119,136 @@ they define both a `provide` and a `provide_extern` function, through
117119

118120
### How providers are set up
119121

120-
When the tcx is created, it is given the providers by its creator using
121-
the [`Providers`][providers_struct] struct. This struct is generated by
122-
the macros here, but it is basically a big list of function pointers:
123-
124-
[providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.Providers.html
122+
When the tcx is created, it is given both the local and external providers by its creator using
123+
the `Providers` struct from `rustc_middle::util`. This struct contains both the local and external providers:
125124

126125
```rust,ignore
127-
struct Providers {
128-
type_of: for<'tcx> fn(TyCtxt<'tcx>, DefId) -> Ty<'tcx>,
129-
// ... one field for each query
126+
pub struct Providers {
127+
pub queries: crate::query::Providers, // Local crate providers
128+
pub extern_queries: crate::query::ExternProviders, // External crate providers
129+
pub hooks: crate::hooks::Providers,
130130
}
131131
```
132132

133+
Each of these provider structs is generated by the macros and contains function pointers for their respective queries.
134+
133135
#### How are providers registered?
134136

135-
The `Providers` struct is filled in during compiler initialization, mainly by the `rustc_driver` crate.
136-
But the actual provider functions are implemented in various `rustc_*` crates (like `rustc_middle`, `rustc_hir_analysis`, etc).
137+
The `util::Providers` struct is filled in during compiler initialization, by the `rustc_interface` crate from the `DEFAULT_QUERY_PROVIDERS` static.
138+
The actual provider functions are defined across various `rustc_*` crates (like `rustc_middle`, `rustc_hir_analysis`, etc).
137139

138140
To register providers, each crate exposes a [`provide`][provide_fn] function that looks like this:
139141

140142
[provide_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/fn.provide.html
141143

142144
```rust,ignore
143-
pub fn provide(providers: &mut Providers) {
144-
*providers = Providers {
145+
pub fn provide(providers: &mut query::Providers) {
146+
*providers = query::Providers {
145147
type_of,
146148
// ... add more providers here
147149
..*providers
148150
};
149151
}
150152
```
151153

152-
- This function takes a mutable reference to the `Providers` struct and sets the fields to point to the correct provider functions.
153-
- You can also assign fields individually, e.g. `providers.type_of = type_of;`.
154+
Note that this function accepts `query::Providers` not `util::Providers`. It is exceedingly rare to need a `provide` function that doesn't just accept `query::Providers`. If more than the `queries` field of `util::Providers` is being updated then `util::Providers` can be accepted instead:
155+
```rust,ignore
156+
pub fn provide(providers: &mut rustc_middle::util::Providers) {
157+
providers.queries.type_of = type_of;
158+
// ... add more local providers here
159+
160+
providers.extern_queries.type_of = extern_type_of;
161+
// ... add more external providers here
162+
163+
providers.hooks.some_hook = some_hook;
164+
// ... add more hooks here
165+
}
166+
```
167+
168+
Note that `util::Providers` implements `DerefMut` to `query::Providers` so callers of the `provide` functions can pass in a `util::Providers` and it will just work for provider functions that accept `query::Providers` too
169+
170+
- This function takes a mutable reference to the `query::Providers` struct and sets the fields to point to the correct provider functions.
171+
- You can also assign queries individually, e.g. `providers.type_of = type_of;`.
172+
- You can assign fields individually for each provider type (local, external, and hooks).
154173

155174
#### Adding a new provider
156175

157-
Suppose you want to add a new query called `fubar`. You would:
176+
Suppose you want to add a new query called `fubar`. This section focuses on wiring up the providers; for how to declare the query itself in the big `rustc_queries!` macro, see [Adding a new query](#adding-a-new-query) below.
177+
178+
In practice you usually:
158179

159-
1. Implement the provider function:
180+
1. Decide which crate "owns" the query (for example `rustc_hir_analysis`, `rustc_mir_build`, or another `rustc_*` crate).
181+
2. In that crate, look for an existing `provide` function:
182+
```rust,ignore
183+
pub fn provide(providers: &mut query::Providers) {
184+
// existing assignments
185+
}
186+
```
187+
If it exists, you will extend it to set the field for your new query. If the crate does not yet have a `provide` function, add one and make sure it is included in `DEFAULT_QUERY_PROVIDERS` in the `rustc_interface` crate so that it actually gets called during initialization (see the discussion above).
188+
3. Implement the provider function itself:
160189
```rust,ignore
161-
fn fubar<'tcx>(tcx: TyCtxt<'tcx>, key: DefId) -> Fubar<'tcx> { ... }
190+
fn fubar<'tcx>(tcx: TyCtxt<'tcx>, key: LocalDefId) -> Fubar<'tcx> { ... }
162191
```
163-
2. Register it in the `provide` function:
192+
4. Register it in the crate's `provide` function:
164193
```rust,ignore
165-
pub fn provide(providers: &mut Providers) {
166-
*providers = Providers {
194+
pub fn provide(providers: &mut query::Providers) {
195+
*providers = query::Providers {
167196
fubar,
168197
..*providers
169198
};
170199
}
171200
```
172201
202+
### How queries interact with external crate metadata
203+
204+
When a query is made for an external crate (i.e., a dependency), the query system needs to load the information from that crate's metadata.
205+
This is handled by the [`rustc_metadata` crate][rustc_metadata], which is responsible for decoding and providing the information stored in the `.rmeta` files.
206+
207+
The process works like this:
208+
209+
1. When a query is made, the query system first checks if the `DefId` refers to a local or external crate by checking if `def_id.krate == LOCAL_CRATE`.
210+
This determines whether to use the local provider from [`Providers`][providers_struct] or the external provider from [`ExternProviders`][extern_providers_struct].
211+
212+
2. For external crates, the query system will look for a provider in the [`ExternProviders`][extern_providers_struct] struct.
213+
The `rustc_metadata` crate registers these external providers through the `provide_extern` function in `rustc_metadata/src/rmeta/decoder/cstore_impl.rs`. Just like:
214+
```rust
215+
pub fn provide_extern(providers: &mut ExternProviders) {
216+
providers.foo = |tcx, def_id| {
217+
// Load and decode metadata for external crate
218+
let cdata = CStore::from_tcx(tcx).get_crate_data(def_id.krate);
219+
cdata.foo(def_id.index)
220+
};
221+
// Register other external providers...
222+
}
223+
```
224+
225+
3. The metadata is stored in a binary format in `.rmeta` files that contains pre-computed information about the external crate, such as types, function signatures, trait implementations, and other information needed by the compiler. When an external query is made, the `rustc_metadata` crate:
226+
- Loads the `.rmeta` file for the external crate
227+
- Decodes the metadata using the `Decodable` trait
228+
- Returns the decoded information to the query system
229+
230+
This approach avoids recompiling external crates, allows for faster compilation of dependent crates, and enables incremental compilation to work across crate boundaries.
231+
Here is a simplified example, when you call `tcx.type_of(def_id)` for a type defined in an external crate, the query system will:
232+
1. Detect that the `def_id` refers to an external crate by checking `def_id.krate != LOCAL_CRATE`
233+
2. Call the appropriate provider from `ExternProviders` which was registered by `rustc_metadata`
234+
3. The provider will load and decode the type information from the external crate's metadata
235+
4. Return the decoded type to the caller
236+
237+
This is why most `rustc_*` crates only need to provide local providers - the external providers are handled by the metadata system.
238+
The only exception is when a crate needs to provide special handling for external queries, in which case it would implement both local and external providers.
239+
240+
When we define a new query that should work across crates, it does not automatically become cross-crate just because it is listed in `rustc_queries!`. You will typically need to:
241+
242+
- Add the query to `rustc_queries!` with appropriate modifiers (for example whether it is cached on disk).
243+
- Implement a local provider in the owning crate and register it via that crate's `provide` function.
244+
- Add an external provider in `rustc_metadata` via `provide_extern`, and ensure the query's result is encoded and decoded in the crate metadata.
245+
246+
An example of introducing such a cross-crate query can be found in commit [`996a185`](https://github.com/rust-lang/rust/commit/996a185) in the `rust-lang/rust` repository.
247+
248+
[rustc_metadata]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/index.html
249+
[providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.Providers.html
250+
[extern_providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.ExternProviders.html
251+
173252
---
174253
175254
## Adding a new query
@@ -263,4 +342,3 @@ More discussion and issues:
263342
[GitHub issue #42633]: https://github.com/rust-lang/rust/issues/42633
264343
[Incremental Compilation Beta]: https://internals.rust-lang.org/t/incremental-compilation-beta/4721
265344
[Incremental Compilation Announcement]: https://blog.rust-lang.org/2016/09/08/incremental.html
266-

0 commit comments

Comments
 (0)