You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: src/macro-expansion.md
+81-39Lines changed: 81 additions & 39 deletions
Original file line number
Diff line number
Diff line change
@@ -14,45 +14,87 @@ we will look at the specifics of expanding different types of macros.
14
14
15
15
## Expansion and AST Integration
16
16
17
-
TODO: expand these notes (har har)...
18
-
19
-
- Expansion happens over a whole crate at once.
20
-
- We run `fully_expand_fragment` on the crate
21
-
- If `fully_expand_fragment` is run not on a whole crate, it means that we are performing eager expansion.
22
-
- We do this for some built-ins that expect literals (not exposed to users).
23
-
- It performs a subset of actions performed by non-eager expansion, so the discussion below focuses on eager expansion.
24
-
- Original description here: https://github.com/rust-lang/rust/pull/53778#issuecomment-419224049
25
-
- Algorithm: `fully_expand_fragment` works in iterations. We repeat until there are no unresolved macros left.
26
-
- Resolve imports in our partially built crate as much as possible.
27
-
- (link to name-resolution chapter) names resolved from "closer" scopes (e.g. current block) to further ones (e.g. prelude)
28
-
- A resolution fails differently for different scopes, e.g. for a module scope it means no unexpanded macros and no unresolved glob imports in that module.
29
-
- Collect as many macro invocations as possible from our partially built crate
30
-
(fn-like, attributes, derives) from the crate and add them to the queue.
31
-
- Take a macro from the queue, and attempt to resolve it.
32
-
- If it's resolved - run its expander function that consumes tokens or AST and produces tokens or AST (depending on the macro kind). (If it's not resolved, then put it back into the queue.)
33
-
- At this point, we know everything about the macro itself and can call `set_expn_data` to fill in its properties in the global data -- that is the hygiene data associated with `ExpnId`.
34
-
- The macro's expander function returns a piece of AST (or tokens). We need to integrate that piece of AST into the big existing partially built AST.
35
-
- If the macro produces tokens (e.g. a proc macro), we will have to parse into an AST, which may produce parse errors.
36
-
- During expansion, we create `SyntaxContext`s (heirarchy 2).
37
-
- This is essentially where the "token-like mass" becomes a proper set-in-stone AST with side-tables
38
-
- These three passes happen one after another on every AST fragment freshly expanded from a macro
39
-
-`NodeId`s are assigned by `InvocationCollector`
40
-
- also collects new macro calls from this new AST piece and adds them to the queue
41
-
- def_paths are created and `DefId`s are assigned to them by `DefCollector`
42
-
-`Name`s are put into modules (from the resolver's point of view) by `BuildReducedGraphVisitor`
43
-
- After expanding a single macro and integrating its output continue to the next iteration of `fully_expand_fragment`.
44
-
- If we make no progress in an iteration, then we have reached a compilation error (e.g. an undefined macro).
45
-
46
-
- We attempt to recover from failures (unresolved macros or imports) for the sake of diagnostics
47
-
- recovery can't cause compilation to suceed. We know that it will fail at this point.
48
-
- we expand errors into `ExprKind::Err` or something like that for unresolved macros
49
-
- this allows compilation to continue past the first error so that we can report more errors at a time
50
-
51
-
### Relationship to name resolution
52
-
53
-
- name resolution is done for macro and import names during expansion and integration into the AST, as discussed above
54
-
- For all other names we certainly know whether a name is resolved successfully or not on the first attempt, because no new names can appear, due to hygiene
55
-
- They are resolved in a later pass, see `librustc_resolve/late.rs`
17
+
First of all, expansion happens at the crate level. Given a raw source code for
18
+
a crate, the compiler will produce a massive AST with all macros expanded, all
19
+
modules inlined, etc.
20
+
21
+
The primary entry point for this process is the
22
+
[`MacroExpander::fully_expand_fragment`][fef] method. Usually, we run this
23
+
method on a whole crate. If it is not run on a full crate, it means we are
24
+
doing _eager macro expansion_. Eager expansion means that we expand the
25
+
arguments of a macro invocation before the macro invocation itself. This is
26
+
implemented only for a few special built-in macros that expect literals (it's
27
+
not a generally available feature of Rust). Eager expansion generally performs
28
+
a subset of the things that lazy (normal) expansion does, so we will focus on
29
+
lazy expansion for the rest of this chapter.
30
+
31
+
At a high level, [`fully_expand_fragment`][fef] works in iterations. We keep a
32
+
queue of unresolved macro invocations (that is, macros we haven't found the
33
+
definition of yet). We repeatedly try to pick a macro from the queue, resolve
34
+
it, expand it, and integrate it back. If we can't make progress in an
35
+
iteration, this represents a compile error. Here is the [algorithm][original]:
0 commit comments