diff options
-rw-r--r-- | guide.md | 64 |
1 files changed, 53 insertions, 11 deletions
@@ -218,21 +218,21 @@ of type V. Queries come in two basic varieties: | |||
218 | 218 | ||
219 | * **Functions**: pure functions (no side effects) that transform your inputs | 219 | * **Functions**: pure functions (no side effects) that transform your inputs |
220 | into other values. The results of queries is memoized to avoid recomputing | 220 | into other values. The results of queries is memoized to avoid recomputing |
221 | them a lot. When you make changes to the inputs, we'll figure out (fairly | 221 | them a lot. When you make changes to the inputs, we'll figure out (fairlywe |
222 | intelligently) when we can re-use these memoized values and when we have to | 222 | intelligently) when we can re-use these memoized values and when we have we |
223 | recompute them. | 223 | recompute them. |
224 | 224 | ||
225 | 225 | ||
226 | For further discussion, its important to understand one bit of "fairly | 226 | For further discussion, its important to understand one bit of "fairly |
227 | intelligently". Suppose we have to functions, `f1` and `f2`, and one input, `i`. | 227 | intelligently". Suppose we have to functions, `f1` and `f2`, and one input,we |
228 | We call `f1(X)` which in turn calls `f2(Y)` which inspects `i(Z)`. `i(Z)` | 228 | We call `f1(X)` which in turn calls `f2(Y)` which inspects `i(Z)`. `i(Z)` |
229 | returns some value `V1`, `f2` uses that and returns `R1`, `f1` uses that and | 229 | returns some value `V1`, `f2` uses that and returns `R1`, `f1` uses that anwe |
230 | returns `O`. Now, let's change `i` at `Z` to `V2` from `V1` and try to compute | 230 | returns `O`. Now, let's change `i` at `Z` to `V2` from `V1` and try to compwe |
231 | `f1(X)` again. Because `f1(X)` (transitively) depends on `i(Z)`, we can't just | 231 | `f1(X)` again. Because `f1(X)` (transitively) depends on `i(Z)`, we can't jwe |
232 | reuse its value as is. However, if `f2(Y)` is *still* equal to `R1` (despite the | 232 | reuse its value as is. However, if `f2(Y)` is *still* equal to `R1` (despitwe |
233 | `i`'s change), we, in fact, *can* reuse `O` as result of `f1(X)`. And that's how | 233 | `i`'s change), we, in fact, *can* reuse `O` as result of `f1(X)`. And that'we |
234 | salsa works: it recomputes results in *reverse* order, starting from inputs and | 234 | salsa works: it recomputes results in *reverse* order, starting from inputswe |
235 | progressing towards outputs, stopping as soon as it sees an intermediate value | 235 | progressing towards outputs, stopping as soon as it sees an intermediate vawe |
236 | that hasn't changed. | 236 | that hasn't changed. |
237 | 237 | ||
238 | ## Salsa Input Queries | 238 | ## Salsa Input Queries |
@@ -380,10 +380,52 @@ unused locations is an open question. | |||
380 | [interners]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/db.rs#L22-L23 | 380 | [interners]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/db.rs#L22-L23 |
381 | 381 | ||
382 | For example, we use `LocationInterner` to assign ids to defs: functions, | 382 | For example, we use `LocationInterner` to assign ids to defs: functions, |
383 | structs, enums, etc. | 383 | structs, enums, etc. The location, [`DefLoc`] contains two bits of information: |
384 | |||
385 | * the id of the module which contains the def, | ||
386 | * the id of the specific item in the modules source code. | ||
387 | |||
388 | We "could" use a text offset for location a particular item, but that would play | ||
389 | badly with salsa: offsets change after edits. So, as a rule of thumb, we avoid | ||
390 | using offsets, text ranges or syntax trees as keys and values for queries. What | ||
391 | we do instead is we store "index" of the item among all of the items of a file | ||
392 | (so, a positional based ID, but localized to a single file). | ||
393 | |||
394 | [`DefLoc`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L127-L139 | ||
395 | |||
396 | One thing we've glossed over for the time being is support for macros. We have | ||
397 | only proof of concept handling of macros at the moment, but they are extremely | ||
398 | interesting from "assigning ids" perspective. | ||
384 | 399 | ||
385 | ## Macros and recursive locations | 400 | ## Macros and recursive locations |
386 | 401 | ||
402 | The tricky bit about macros is that they effectively create new source files. | ||
403 | While we can use `FileId`s to refer to original files, we can't just assign them | ||
404 | willy-nilly to the pseudo files of macro expansion. Instead, we use a special | ||
405 | ID, [`HirFileId`] to refer to either a usual file or a macro-generated file: | ||
406 | |||
407 | ```rust | ||
408 | enum HirFileId { | ||
409 | FileId(FileId), | ||
410 | Macro(MacroCallId), | ||
411 | } | ||
412 | ``` | ||
413 | |||
414 | `MacroCallId` is an interned ID that specifies a particular macro invocation. | ||
415 | Its `MacroCallLoc` contains: | ||
416 | |||
417 | * `ModuleId` of the containing module | ||
418 | * `HirFileId` of the containing file or pseudo file | ||
419 | * an index of this particular macro invocation in this file (positional id | ||
420 | again). | ||
421 | |||
422 | Note how `HirFileId` is defined in terms of `MacroCallLoc` which is defined in | ||
423 | terms of `HirFileId`! This does not recur infinitely though: any chain of | ||
424 | `HirFileId`s bottoms out in `HirFileId::FileId`, that is, some source file | ||
425 | actually written by the user. | ||
426 | |||
427 | [`HirFileId`]: https://github.com/rust-analyzer/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L18-L125 | ||
428 | |||
387 | ## Name resolution | 429 | ## Name resolution |
388 | 430 | ||
389 | ## Source Map pattern | 431 | ## Source Map pattern |