]> git.proxmox.com Git - rustc.git/blame - src/doc/rustc-dev-guide/src/salsa.md
New upstream version 1.55.0+dfsg1
[rustc.git] / src / doc / rustc-dev-guide / src / salsa.md
CommitLineData
60c5eb7d
XL
1# How Salsa works
2
6a06907d
XL
3<!-- toc -->
4
60c5eb7d
XL
5This chapter is based on the explanation given by Niko Matsakis in this
6[video](https://www.youtube.com/watch?v=_muY4HjSqVw) about
6a06907d
XL
7[Salsa](https://github.com/salsa-rs/salsa). To find out more you may
8want to watch [Salsa In More
9Depth](https://www.youtube.com/watch?v=i_IhACacPRY), also by Niko
10Matsakis.
60c5eb7d 11
136023e0 12> As of <!-- date: 2021-07 --> July 2021, although Salsa is inspired by
6a06907d
XL
13> (among other things) rustc's query system, it is not used directly in rustc.
14> It _is_ used in chalk and extensively in `rust-analyzer`, but there are no
15> medium or long-term concrete plans to integrate it into the compiler.
60c5eb7d
XL
16
17## What is Salsa?
18
dfeec247
XL
19Salsa is a library for incremental recomputation. This means it allows reusing
20computations that were already done in the past to increase the efficiency
60c5eb7d
XL
21of future computations.
22
23The objectives of Salsa are:
24 * Provide that functionality in an automatic way, so reusing old computations
25 is done automatically by the library
26 * Doing so in a "sound", or "correct", way, therefore leading to the same
27 results as if it had been done from scratch
28
29Salsa's actual model is much richer, allowing many kinds of inputs and many
30different outputs.
31For example, integrating Salsa with an IDE could mean that the inputs could be
32the manifest (`Cargo.toml`), entire source files (`foo.rs`), snippets and so
33on; the outputs of such an integration could range from a binary executable, to
34lints, types (for example, if a user selects a certain variable and wishes to
35see its type), completions, etc.
36
37## How does it work?
38
6a06907d
XL
39The first thing that Salsa has to do is identify the "base inputs" that
40are not something computed but given as input.
60c5eb7d
XL
41
42Then Salsa has to also identify intermediate, "derived" values, which are
43something that the library produces, but, for each derived value there's a
44"pure" function that computes the derived value.
45
46For example, there might be a function `ast(x: Path) -> AST`. The produced
47`AST` isn't a final value, it's an intermidiate value that the library would
48use for the computation.
49
50This means that when you try to compute with the library, Salsa is going to
51compute various derived values, and eventually read the input and produce the
52result for the asked computation.
53
54In the course of computing, Salsa tracks which inputs were accessed and which
55values are derived. This information is used to determine what's going to
56happen when the inputs change: are the derived values still valid?
57
58This doesn't necessarily mean that each computation downstream from the input
59is going to be checked, which could be costly. Salsa only needs to check each
60downstream computation until it finds one that isn't changed. At that point, it
61won't check other derived computations since they wouldn't need to change.
62
63It's is helpful to think about this as a graph with nodes. Each derived value
64has a dependency on other values, which could themselves be either base or
65derived. Base values don't have a dependency.
66
67```ignore
68I <- A <- C ...
69 |
70J <- B <--+
71```
72
73When an input `I` changes, the derived value `A` could change. The derived
74value `B` , which does not depend on `I`, `A`, or any value derived from `A` or
75`I`, is not subject to change. Therefore, Salsa can reuse the computation done
76for `B` in the past, without having to compute it again.
77
78The computation could also terminate early. Keeping the same graph as before,
79say that input `I` has changed in some way (and input `J` hasn't) but, when
80computing `A` again, it's found that `A` hasn't changed from the previous
81computation. This leads to an "early termination", because there's no need to
82check if `C` needs to change, since both `C` direct inputs, `A` and `B`,
83haven't changed.
84
85## Key Salsa concepts
86
87### Query
88
89A query is some value that Salsa can access in the course of computation. Each
90query can have a number of keys (from 0 to many), and all queries have a
91result, akin to functions. 0-key queries are called "input" queries.
92
93### Database
94
95The database is basically the context for the entire computation, it's meant to
96store Salsa's internal state, all intermediate values for each query, and
97anything else that the computation might need. The database must know all the
98queries that the library is going to do before it can be built, but they don't
99need to be specified in the same place.
100
101After the database is formed, it can be accessed with queries that are very
102similar to functions. Since each query's result is stored in the database,
103when a query is invoked N times, it will return N **cloned** results, without
104having to recompute the query (unless the input has changed in such a way that
105it warrants recomputation).
106
107For each input query (0-key), a "set" method is generated, allowing the user to
108change the output of such query, and trigger previous memoized values to be
109potentially invalidated.
110
111### Query Groups
112
113A query group is a set of queries which have been defined together as a unit.
6a06907d
XL
114The database is formed by combining query groups. Query groups are akin to
115"Salsa modules".
60c5eb7d
XL
116
117A set of queries in a query group are just a set of methods in a trait.
118
119To create a query group a trait annotated with a specific attribute
120(`#[salsa::query_group(...)]`) has to be created.
121
122An argument must also be provided to said attribute as it will be used by Salsa
123to create a struct to be used later when the database is created.
124
125Example input query group:
126
127```rust,ignore
128/// This attribute will process this tree, produce this tree as output, and produce
129/// a bunch of intermidiate stuff that Salsa also uses. One of these things is a
130/// "StorageStruct", whose name we have specified in the attribute.
131///
132/// This query group is a bunch of **input** queries, that do not rely on any
133/// derived input.
134#[salsa::query_group(InputsStorage)]
135pub trait Inputs {
136 /// This attribute (`#[salsa::input]`) indicates that this query is a base
137 /// input, therefore `set_manifest` is going to be auto-generated
138 #[salsa::input]
139 fn manifest(&self) -> Manifest;
140
141 #[salsa::input]
142 fn source_text(&self, name: String) -> String;
143}
144```
145
146To create a **derived** query group, one must specify which other query groups
147this one depends on by specifying them as supertraits, as seen in the following
148example:
149
150```rust,ignore
151/// This query group is going to contain queries that depend on derived values a
152/// query group can access another query group's queries by specifying the
153/// dependency as a super trait query groups can be stacked as much as needed using
154/// that pattern.
155#[salsa::query_group(ParserStorage)]
156pub trait Parser: Inputs {
157 /// This query `ast` is not an input query, it's a derived query this means
158 /// that a definition is necessary.
159 fn ast(&self, name: String) -> String;
160}
161```
162
163When creating a derived query the implementation of said query must be defined
164outside the trait. The definition must take a database parameter as an `impl
165Trait` (or `dyn Trait`), where `Trait` is the query group that the definition
166belongs to, in addition to the other keys.
167
168```rust,ignore
169///This is going to be the definition of the `ast` query in the `Parser` trait.
170///So, when the query `ast` is invoked, and it needs to be recomputed, Salsa is going to call this function
171///and it's is going to give it the database as `impl Parser`.
172///The function doesn't need to be aware of all the queries of all the query groups
173fn ast(db: &impl Parser, name: String) -> String {
174 //! Note, `impl Parser` is used here but `dyn Parser` works just as well
175 /* code */
176 ///By passing an `impl Parser`, this is allowed
177 let source_text = db.input_file(name);
178 /* do the actual parsing */
179 return ast;
180}
181```
182
183Eventually, after all the query groups have been defined, the database can be
184created by declaring a struct.
185
186To specify which query groups are going to be part of the database an attribute
187(`#[salsa::database(...)]`) must be added. The argument of said attribute is a
188list of identifiers, specifying the query groups **storages**.
189
190```rust,ignore
191///This attribute specifies which query groups are going to be in the database
192#[salsa::database(InputsStorage, ParserStorage)]
193#[derive(Default)] //optional!
194struct MyDatabase {
195 ///You also need this one field
196 runtime : salsa::Runtime<MyDatabase>,
197}
198///And this trait has to be implemented
199impl salsa::Databse for MyDatabase {
200 fn salsa_runtime(&self) -> &salsa::Runtime<MyDatabase> {
201 &self.runtime
202 }
203}
204```
205
206Example usage:
207
208```rust,ignore
209fn main() {
210 let db = MyDatabase::default();
211 db.set_manifest(...);
212 db.set_source_text(...);
213 loop {
214 db.ast(...); //will reuse results
215 db.set_source_text(...);
216 }
217}
218```