]> git.proxmox.com Git - rustc.git/blob - vendor/salsa/book/src/rfcs/RFC0001-Query-Group-Traits.md
New upstream version 1.46.0~beta.2+dfsg1
[rustc.git] / vendor / salsa / book / src / rfcs / RFC0001-Query-Group-Traits.md
1 # Motivation
2
3 - Support `dyn QueryGroup` for each query group trait as well as `impl QueryGroup`
4 - `dyn QueryGroup` will be much more convenient, at the cost of runtime efficiency
5 - Don't require you to redeclare each query in the final database, just the query groups
6
7 # User's guide
8
9 ## Declaring a query group
10
11 User's will declare query groups by decorating a trait with `salsa::query_group`:
12
13 ```rust
14 #[salsa::query_group(MyGroupStorage)]
15 trait MyGroup {
16 // Inputs are annotated with `#[salsa::input]`. For inputs, the final trait will include
17 // a `set_my_input(&mut self, key: K1, value: V1)` method automatically added,
18 // as well as possibly other mutation methods.
19 #[salsa::input]
20 fn my_input(&self, key: K1) -> V1;
21
22 // "Derived" queries are just a getter.
23 fn my_query(&self, key: K2) -> V2;
24 }
25 ```
26
27 The `query_group` attribute is a procedural macro. It takes as
28 argument the name of the **storage struct** for the query group --
29 this is a struct, generated by the macro, which represents the query
30 group as a whole. It is attached to a trait definition which defines the
31 individual queries in the query group.
32
33 The macro generates three things that users interact with:
34
35 - the trait, here named `MyGroup`. This will be used when writing the definitions
36 for the queries and other code that invokes them.
37 - the storage struct, here named `MyGroupStorage`. This will be used later when
38 constructing the final database.
39 - query structs, named after each query but converted to camel-case
40 and with the word query (e.g., `MyInputQuery` for `my_input`). These
41 types are rarely needed, but are presently useful for things like
42 invoking the GC. These types violate our rule that "things the user
43 needs to name should be given names by the user", but we choose not
44 to fully resolve this question in this RFC.
45
46 In addition, the macro generates a number of structs that users should
47 not have to be aware of. These are described in the "reference guide"
48 section.
49
50 ### Controlling query modes
51
52 Input queries, as described in the trait, are specified via the
53 `#[salsa::input]` attribute.
54
55 Derived queries can be customized by the following attributes,
56 attached to the getter method (e.g., `fn my_query(..)`):
57
58 - `#[salsa::invoke(foo::bar)]` specifies the path to the function to invoke
59 when the query is called (default is `my_query`).
60 - `#[salsa::volatile]` specifies a "volatile" query, which is assumed to
61 read untracked input and hence must be re-executed on every revision.
62 - `#[salsa::dependencies]` specifies a "dependencies-only" query, which is assumed to
63 read untracked input and hence must be re-executed on every revision.
64
65 ## Creating the database
66
67 Creating a salsa database works by using a `#[salsa::database(..)]`
68 attribute. The `..` content should be a list of paths leading to the
69 storage structs for each query group that the database will
70 implement. It is no longer necessary to list the individual
71 queries. In addition to the `salsa::database` query, the struct must
72 have access to a `salsa::Runtime` and implement the `salsa::Database`
73 trait. Hence the complete declaration looks roughly like so:
74
75 ```rust
76 #[salsa::database(MyGroupStorage)]
77 struct MyDatabase {
78 runtime: salsa::Runtime<MyDatabase>,
79 }
80
81 impl salsa::Database for MyDatabase {
82 fn salsa_runtime(&self) -> salsa::Runtime<MyDatabase> {
83 &self.runtime
84 }
85 }
86 ```
87
88 This (procedural) macro generates various impls and types that cause
89 `MyDatabase` to implement all the traits for the query groups it
90 supports, and which customize the storage in the runtime to have all
91 the data needed. Users should not have to interact with these details,
92 and they are written out in the reference guide section.
93
94 # Reference guide
95
96 The goal here is not to give the *full* details of how to do the
97 lowering, but to describe the key concepts. Throughout the text, we
98 will refer to names (e.g., `MyGroup` or `MyGroupStorage`) that appear
99 in the example from the User's Guide -- this indicates that we use
100 whatever name the user provided.
101
102 ## The `plumbing::QueryGroup` trait
103
104 The `QueryGroup` trait is a new trait added to the plumbing module. It
105 is implemented by the query group storage struct `MyGroupStorage`. Its
106 role is to link from that struct to the various bits of data that the
107 salsa runtime needs:
108
109 ```rust
110 pub trait QueryGroup<DB: Database> {
111 type GroupStorage;
112 type GroupKey;
113 }
114 ```
115
116 This trait is implemented by the **storage struct** (`MyGroupStorage`)
117 in our example. You can see there is a bit of confusing nameing going
118 on here -- what we call (for user's) the "storage struct" actually
119 does not wind up containing the true *storage* (that is, the hasmaps
120 and things salsa uses). Instead, it merely implements the `QueryGroup`
121 trait, which has associated types that lead us to structs we need:
122
123 - the **group storage** contains the hashmaps and things for all the queries in the group
124 - the **group key** is an enum with variants for each of the
125 queries. It basically stores all the data needed to identify some
126 particular *query value* from within the group -- that is, the name
127 of the query, plus the keys used to invoke it.
128
129 As described further on, the `#[salsa::query_group]` macro is
130 responsible will generate an impl of this trait for the
131 `MyGroupStorage` struct, along with the group storage and group key
132 type definitions.
133
134 ## The `plumbing::HasQueryGroup<G>` trait
135
136 The `HasQueryGroup<G>` struct a new trait added to the plumbing
137 module. It is implemented by the database struct `MyDatabase` for
138 every query group that `MyDatabase` supports. Its role is to offer
139 methods that move back and forth between the context of the *full
140 database* to the context of an *individual query group*:
141
142 ```rust
143 pub trait HasQueryGroup<G>: Database
144 where
145 G: QueryGroup<Self>,
146 {
147 /// Access the group storage struct from the database.
148 fn group_storage(db: &Self) -> &G::GroupStorage;
149
150 /// "Upcast" a group key into a database key.
151 fn database_key(group_key: G::GroupKey) -> Self::DatabaseKey;
152 }
153 ```
154
155 Here the "database key" is an enum that contains variants for each
156 group. Its role is to take group key and puts it into the context of
157 the entire database.
158
159 ## The `Query` trait
160
161 The query trait (pre-existing) is extended to include links to its
162 group, and methods to convert from the group storage to the query
163 storage, plus methods to convert from a query key up to the group key:
164
165 ```rust
166 pub trait Query<DB: Database>: Debug + Default + Sized + 'static {
167 /// Type that you you give as a parameter -- for queries with zero
168 /// or more than one input, this will be a tuple.
169 type Key: Clone + Debug + Hash + Eq;
170
171 /// What value does the query return?
172 type Value: Clone + Debug;
173
174 /// Internal struct storing the values for the query.
175 type Storage: plumbing::QueryStorageOps<DB, Self> + Send + Sync;
176
177 /// Associate query group struct.
178 type Group: plumbing::QueryGroup<
179 DB,
180 GroupStorage = Self::GroupStorage,
181 GroupKey = Self::GroupKey,
182 >;
183
184 /// Generated struct that contains storage for all queries in a group.
185 type GroupStorage;
186
187 /// Type that identifies a particular query within the group + its key.
188 type GroupKey;
189
190 /// Extact storage for this query from the storage for its group.
191 fn query_storage(group_storage: &Self::GroupStorage) -> &Self::Storage;
192
193 /// Create group key for this query.
194 fn group_key(key: Self::Key) -> Self::GroupKey;
195 }
196 ```
197
198 ## Converting to/from the context of the full database generically
199
200 Putting all the previous plumbing traits together, this means
201 that given:
202
203 - a database `DB` that implements `HasGroupStorage<G>`;
204 - a group struct `G` that implements `QueryGroup<DB>`; and,
205 - and a query struct `Q` that implements `Query<DB, Group = G>`
206
207 we can (generically) get the storage for the individual query
208 `Q` out from the database `db` via a two-step process:
209
210 ```rust
211 let group_storage = HasGroupStorage::group_storage(db);
212 let query_storage = Query::query_storage(group_storage);
213 ```
214
215 Similarly, we can convert from the key to an individual query
216 up to the "database key" in a two-step process:
217
218 ```rust
219 let group_key = Query::group_key(key);
220 let db_key = HasGroupStorage::database_key(group_key);
221 ```
222
223 ## Lowering query groups
224
225 The role of the `#[salsa::query_group(MyGroupStorage)] trait MyGroup {
226 .. }` macro is primarily to generate the group storage struct and the
227 impl of `QueryGroup`. That involves generating the following things:
228
229 - the query trait `MyGroup` itself, but with:
230 - `salsa::foo` attributes stripped
231 - `#[salsa::input]` methods expanded to include setters:
232 - `fn set_my_input(&mut self, key: K1, value__: V1);`
233 - `fn set_constant_my_input(&mut self, key: K1, value__: V1);`
234 - the query group storage struct `MyGroupStorage`
235 - We also generate an impl of `QueryGroup<DB>` for `MyGroupStorage`,
236 linking to the internal strorage struct and group key enum
237 - the individual query types
238 - Ideally, we would use Rust hygiene to hide these struct, but as
239 that is not currently possible they are given names based on the
240 queries, but converted to camel-case (e.g., `MyInputQuery` and `MyQueryQuery`).
241 - They implement the `salsa::Query` trait.
242 - the internal group storage struct
243 - Ideally, we would use Rust hygiene to hide this struct, but as
244 that is not currently possible it is entitled
245 `MyGroupGroupStorage<DB>`. Note that it is generic with respect to
246 the database `DB`. This is because the actual query storage
247 requires sometimes storing database key's and hence we need to
248 know the final database type.
249 - It contains one field per query with a link to the storage information
250 for that query:
251 - `my_query: <MyQueryQuery as salsa::plumbing::Query<DB>>::Storage`
252 - (the `MyQueryQuery` type is also generated, see the "individual query types" below)
253 - The internal group storage struct offers a public, inherent method
254 `for_each_query`:
255 - `fn for_each_query(db: &DB, op: &mut dyn FnMut(...)`
256 - this is invoked by the code geneated by `#[salsa::database]` when implementing the
257 `for_each_query` method of the `plumbing::DatabaseOps` trait
258 - the group key
259 - Again, ideally we would use hygiene to hide the name of this struct,
260 but since we cannot, it is entitled `MyGroupGroupKey`
261 - It is an enum which contains one variant per query with the value being the key:
262 - `my_query(<MyQueryQuery as salsa::plumbing::Query<DB>>::Key)`
263 - The group key enum offers a public, inherent method `maybe_changed_since`:
264 - `fn maybe_changed_since<DB>(db: &DB, db_descriptor: &DB::DatabaseKey, revision: Revision)`
265 - it is invoked when implementing `maybe_changed_since` for the database key
266
267 ## Lowering database storage
268
269 The `#[salsa::database(MyGroup)]` attribute macro creates the links to the query groups.
270 It generates the following things:
271
272 - impl of `HasQueryGroup<MyGroup>` for `MyDatabase`
273 - Naturally, there is one such impl for each query group.
274 - the database key enum
275 - Ideally, we would use Rust hygiene to hide this enum, but currently
276 it is called `__SalsaDatabaseKey`.
277 - The database key is an enum with one variant per query group:
278 - `MyGroupStorage(<MyGroupStorage as QueryGroup<MyDatabase>>::GroupKey)`
279 - the database storage struct
280 - Ideally, we would use Rust hygiene to hide this enum, but currently
281 it is called `__SalsaDatabaseStorage`.
282 - The database storage struct contains one field per query group, storing
283 its internal storage:
284 - `my_group_storage: <MyGroupStorage as QueryGroup<MyDatabase>>::GroupStorage`
285 - impl of `plumbing::DatabaseStorageTypes` for `MyDatabase`
286 - This is a plumbing trait that links to the database storage / database key types.
287 - The `salsa::Runtime` uses it to determine what data to include. The query types
288 use it to determine a database-key.
289 - impl of `plumbing::DatabaseOps` for `MyDatabase`
290 - This contains a `for_each_query` method, which is implemented by invoking, in turn,
291 the inherent methods defined on each query group storage struct.
292 - impl of `plumbing::DatabaseKey` for the database key enum
293 - This contains a method `maybe_changed_since`. We implement this by
294 matching to get a particular group key, and then invoking the
295 inherent method on the group key struct.
296
297 # Alternatives
298
299 This proposal results from a fair amount of iteration. Compared to the
300 status quo, there is one primary downside. We also explain a few things here that
301 may not be obvious.
302
303 ## Why include a group storage struct?
304
305 You might wonder why we need the `MyGroupStorage` struct at all. It is a touch of boilerplate,
306 but there are several advantages to it:
307
308 - You can't attach associated types to the trait itself. This is because the "type version"
309 of the trait (`dyn MyGroup`) may not be available, since not all traits are dyn-capable.
310 - We try to keep to the principle that "any type that might be named
311 externally from the macro is given its name by the user". In this
312 case, the `[salsa::database]` attribute needed to name group storage
313 structs.
314 - In earlier versions, we tried to auto-generate these names, but
315 this failed because sometimes users would want to `pub use` the
316 query traits and hide their original paths.
317 - (One exception to this principle today are the per-query structs.)
318 - We expect that we can use the `MyGroupStorage` to achieve more
319 encapsulation in the future. While the struct must be public and
320 named from the database, the *trait* (and query key/value types)
321 actually does not have to be.
322
323 ## Downside: Size of a database key
324
325 Database keys now wind up with two discriminants: one to identify the
326 group, and one to identify the query. That's a bit sad. This could be
327 overcome by using unsafe code: the idea would be that a group/database
328 key would be stored as the pair of an integer and a `union`. Each
329 group within a given database would be assigned a range of integer
330 values, and the unions would store the actual key values. We leave
331 such a change for future work.
332
333 # Future possibilities
334
335 Here are some ideas we might want to do later.
336
337 ## No generics
338
339 We leave generic parameters on the query group trait etc for future work.
340
341 ## Public / private
342
343 We'd like the ability to make more details from the query groups
344 private. This will require some tinkering.
345
346 ## Inline query definitions
347
348 Instead of defining queries in separate functions, it might be nice to
349 have the option of defining query methods in the trait itself:
350
351 ```rust
352 #[salsa::query_group(MyGroupStorage)]
353 trait MyGroup {
354 #[salsa::input]
355 fn my_input(&self, key: K1) -> V1;
356
357 fn my_query(&self, key: K2) -> V2 {
358 // define my-query right here!
359 }
360 }
361 ```
362
363 It's a bit tricky to figure out how to handle this, so that is left
364 for future work. Also, it would mean that the method body itself is
365 inside of a macro (the procedural macro) which can make IDE
366 integration harder.
367
368 ## Non-query functions
369
370 It might be nice to be able to include functions in the trait that are
371 *not* queries, but rather helpers that compose queries. This should be
372 pretty easy, just need a suitable `#[salsa]` attribute.
373