diff --git a/src/query.md b/src/query.md index 782c5b4b..0c8e136b 100644 --- a/src/query.md +++ b/src/query.md @@ -67,9 +67,15 @@ are cheaply cloneable; insert an `Rc` if necessary). ### Providers If, however, the query is *not* in the cache, then the compiler will -try to find a suitable **provider**. A provider is a function that has -been defined and linked into the compiler somewhere that contains the -code to compute the result of the query. +call the corresponding **provider** function. A provider is a function +implemented in a specific module and **manually registered** into the +[`Providers`][providers_struct] struct during compiler initialization. +The macro system generates the [`Providers`][providers_struct] struct, +which acts as a function table for all query implementations, where each +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. +It is **not** a Rust trait, but a plain struct with function pointer fields. **Providers are defined per-crate.** The compiler maintains, internally, a table of providers for every crate, at least @@ -97,62 +103,6 @@ fn provider<'tcx>( Providers take two arguments: the `tcx` and the query key. They return the result of the query. -### How providers are setup - -When the tcx is created, it is given the providers by its creator using -the [`Providers`][providers_struct] struct. This struct is generated by -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 -struct Providers { - type_of: for<'tcx> fn(TyCtxt<'tcx>, DefId) -> Ty<'tcx>, - ... -} -``` - -At present, we have one copy of the struct for local crates, and one -for external crates, though the plan is that we may eventually have -one per crate. - -These `Providers` structs are ultimately created and populated by -`rustc_driver`, but it does this by distributing the work -throughout the other `rustc_*` crates. This is done by invoking -various [`provide`][provide_fn] functions. These functions tend to look -something like this: - -[provide_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/fn.provide.html - -```rust,ignore -pub fn provide(providers: &mut Providers) { - *providers = Providers { - type_of, - ..*providers - }; -} -``` - -That is, they take an `&mut Providers` and mutate it in place. Usually -we use the formulation above just because it looks nice, but you could -as well do `providers.type_of = type_of`, which would be equivalent. -(Here, `type_of` would be a top-level function, defined as we saw -before.) So, if we want to add a provider for some other query, -let's call it `fubar`, into the crate above, we might modify the `provide()` -function like so: - -```rust,ignore -pub fn provide(providers: &mut Providers) { - *providers = Providers { - type_of, - fubar, - ..*providers - }; -} - -fn fubar<'tcx>(tcx: TyCtxt<'tcx>, key: DefId) -> Fubar<'tcx> { ... } -``` - N.B. Most of the `rustc_*` crates only provide **local providers**. Almost all **extern providers** wind up going through the [`rustc_metadata` crate][rustc_metadata], which loads the information @@ -164,6 +114,63 @@ they define both a `provide` and a `provide_extern` function, through [rustc_metadata]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/index.html [wasm_import_module_map]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/back/symbol_export/fn.wasm_import_module_map.html +### How providers are set up + +When the tcx is created, it is given the providers by its creator using +the [`Providers`][providers_struct] struct. This struct is generated by +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 +struct Providers { + type_of: for<'tcx> fn(TyCtxt<'tcx>, DefId) -> Ty<'tcx>, + // ... one field for each query +} +``` + +#### How are providers registered? + +The `Providers` struct is 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). + +To register providers, each crate exposes a [`provide`][provide_fn] function that looks like this: + +[provide_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/fn.provide.html + +```rust,ignore +pub fn provide(providers: &mut Providers) { + *providers = Providers { + type_of, + // ... add more providers here + ..*providers + }; +} +``` + +- 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;`. + +#### Adding a new provider + +Suppose you want to add a new query called `fubar`. You would: + +1. Implement the provider function: + ```rust,ignore + fn fubar<'tcx>(tcx: TyCtxt<'tcx>, key: DefId) -> Fubar<'tcx> { ... } + ``` +2. Register it in the `provide` function: + ```rust,ignore + pub fn provide(providers: &mut Providers) { + *providers = Providers { + fubar, + ..*providers + }; + } + ``` + +--- + ## Adding a new query How do you add a new query?