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
TODO: how a crate transitions from the state "macros exist as written in source" to "all macros are expanded"
277
277
278
278
Expansion Heirarchies and Syntax Context
279
-
- Many AST nodes have some sort of syntax context, especially nodes from macros. The context consists of a chain of expansions leading to `ExpnId::root`. A non-macro-expanded node has syntax context 0 (`SyntaxContext::empty()`) which represents just the root node.
279
+
- Many AST nodes have some sort of syntax context, especially nodes from macros.
280
+
- When we ask what is the syntax context of a node, the answer actually differs by what we are trying to do. Thus, we don't just keep track of a single context. There are in fact 3 different types of context used for different things.
281
+
- Each type of context is tracked by an "expansion heirarchy". As we expand macros, new macro calls or macro definitions may be generated, leading to some nesting. This nesting is where the heirarchies come from. Each heirarchy tracks some different aspect, though, as we will see.
280
282
- There are 3 expansion heirarchies
281
283
- They all start at ExpnId::root, which is its own parent
284
+
- The context of a node consists of a chain of expansions leading to `ExpnId::root`. A non-macro-expanded node has syntax context 0 (`SyntaxContext::empty()`) which represents just the root node.
285
+
- There are vectors in `HygieneData` that contain expansion info.
286
+
- There are entries here for both `SyntaxContext::empty()` and `ExpnId::root`, but they aren't used much.
282
287
288
+
1. Tracks expansion order: when a macro invocation is in the output of another macro.
289
+
...
290
+
expn_id2
291
+
expn_id1
292
+
InternalExpnData::parent is the child->parent link. That is the expn_id1 points to expn_id2 points to ...
283
293
294
+
Ex:
295
+
macro_rules! foo { () => { println!(); } }
296
+
fn main() { foo!(); }
284
297
298
+
// Then AST nodes that are finally generated would have parent(expn_id_println) -> parent(expn_id_foo), right?
285
299
300
+
2. Tracks macro definitions: when we are expanding one macro another macro definition is revealed in its output.
301
+
...
302
+
SyntaxContext2
303
+
SyntaxContext1
304
+
SyntaxContextData::parent is the child->parent link here.
305
+
SyntaxContext is the whole chain in this hierarchy, and SyntaxContextData::outer_expns are individual elements in the chain.
286
306
287
307
288
308
289
-
# Discussion about hygiene
290
-
291
-
292
-
```txt
293
-
294
-
295
-
296
-
Vadim Petrochenkov: All expansion hyerarchies (there are several of them) start
297
-
at ExpnId::root.
298
-
299
-
Vadim Petrochenkov: Vectors in HygieneData has entries for both ctxt == 0 and
300
-
expn_id == 0.
301
-
302
-
Vadim Petrochenkov: I don't think anyone looks into them much though.
303
-
304
-
Vadim Petrochenkov: Speaking of multiple hierarchies...
305
-
306
-
Vadim Petrochenkov: One is parent (expn_id1) -> parent(expn_id2) -> ...
307
-
308
-
Vadim Petrochenkov: This is the order in which macros are expanded.
309
-
310
-
Vadim Petrochenkov: Well.
311
-
312
-
Vadim Petrochenkov: When we are expanding one macro another macro is revealed
313
-
in its output.
314
-
315
-
Vadim Petrochenkov: That's the parent-child relation in this hierarchy.
316
-
317
-
Vadim Petrochenkov: InternalExpnData::parent is the child->parent link.
318
-
319
-
mark-i-m: So in the above chain expn_id1 is the child?
320
-
321
-
Vadim Petrochenkov: Yes.
322
-
323
-
Vadim Petrochenkov: The second one is parent (SyntaxContext1) ->
324
-
parent(SyntaxContext2) -> ...
325
-
326
-
Vadim Petrochenkov: This is about nested macro definitions. When we are
327
-
expanding one macro another macro definition is revealed in its output.
328
309
329
-
Vadim Petrochenkov: SyntaxContextData::parent is the child->parent link here.
330
310
331
-
Vadim Petrochenkov: So, SyntaxContext is the whole chain in this hierarchy, and
332
-
outer_expns are individual elements in the chain.
333
-
334
-
mark-i-m: So for example, suppose I have the following:
335
-
336
-
macro_rules! foo { () => { println!(); } }
337
-
338
-
fn main() { foo!(); }
339
-
340
-
Then AST nodes that are finally generated would have parent(expn_id_println) ->
341
-
parent(expn_id_foo), right?
342
-
343
-
Vadim Petrochenkov: Pretty common construction (at least it was, before
344
-
refactorings) is SyntaxContext::empty().apply_mark(expn_id), which means...
345
-
346
-
Vadim Petrochenkov:
347
-
348
-
Then AST nodes that are finally generated would have
and outer_expns are individual elements in the chain.
314
+
```txt
356
315
357
-
Sorry, what is outer_expns?
358
316
359
-
Vadim Petrochenkov: SyntaxContextData::outer_expn
317
+
Vadim Petrochenkov: Pretty common construction (at least it was, before refactorings) is SyntaxContext::empty().apply_mark(expn_id), which means a token produced by a built-in macro (which is defined in the root effectively).
360
318
361
-
Vadim Petrochenkov: ...which means a token produced by a built-in macro (which
362
-
is defined in the root effectively).
319
+
Vadim Petrochenkov: Or a stable proc macro, which are always considered to be defined in the root because they are always cross-crate, and we don't have the cross-crate hygiene implemented, ha-ha.
363
320
364
321
mark-i-m: Where does the expn_id come from?
365
322
366
-
Vadim Petrochenkov: Or a stable proc macro, which are always considered to be
367
-
defined in the root because they are always cross-crate, and we don't have the
368
-
cross-crate hygiene implemented, ha-ha.
369
-
370
-
Vadim Petrochenkov:
371
-
372
-
Where does the expn_id come from?
373
-
374
323
Vadim Petrochenkov: ID of the built-in macro call like line!().
375
324
376
-
Vadim Petrochenkov: Assigned continuously from 0 to N as soon as we discover
377
-
new macro calls.
325
+
Vadim Petrochenkov: Assigned continuously from 0 to N as soon as we discover new macro calls.
378
326
379
-
mark-i-m: Sorry, I didn't quite understand. Do you mean that only built-in
380
-
macros receive continuous IDs?
327
+
mark-i-m: Sorry, I didn't quite understand. Do you mean that only built-in macros receive continuous IDs?
381
328
382
-
Vadim Petrochenkov: So, the second hierarchy has a catch - the context
Vadim Petrochenkov: So, the second hierarchy has a catch - the context transplantation hack - https://github.com/rust-lang/rust/pull/51762#issuecomment-401400732.
385
330
386
331
Vadim Petrochenkov:
387
332
388
333
Do you mean that only built-in macros receive continuous IDs?
389
334
390
335
Vadim Petrochenkov: No, all macro calls receive ID.
391
336
392
-
Vadim Petrochenkov: Built-ins have the typical pattern
393
-
SyntaxContext::empty().apply_mark(expn_id) for syntax contexts produced by
394
-
them.
337
+
Vadim Petrochenkov: Built-ins have the typical pattern SyntaxContext::empty().apply_mark(expn_id) for syntax contexts produced by them.
395
338
396
339
mark-i-m: I see, but this pattern is only used for built-ins, right?
397
340
@@ -407,23 +350,17 @@ Vadim Petrochenkov: but hierarchy 3 is root -> ident
407
350
408
351
Vadim Petrochenkov: ExpnInfo::call_site is the child-parent link in this case.
409
352
410
-
mark-i-m: When we expand, do we expand foo first or bar? Why is there a
411
-
hierarchy 1 here? Is that foo expands first and it expands to something that
412
-
contains bar!(ident)?
353
+
mark-i-m: When we expand, do we expand foo first or bar? Why is there a hierarchy 1 here? Is that foo expands first and it expands to something that contains bar!(ident)?
413
354
414
355
Vadim Petrochenkov: Ah, yes, let's assume both foo and bar are identity macros.
415
356
416
-
Vadim Petrochenkov: Then foo!(bar!(ident)) -> expand -> bar!(ident) -> expand
0 commit comments