How to determine hygienic context for "non-atomic" code fragments · Issue #50122 · rust-lang/rust (original) (raw)

Reminder: Syntactic/hygienic context is formally a "chain of expansions" and informally "the place where something is actually written". For example, in this example

macro m($b: expr) { a + $b }

let x = m!(b);

a's context is inside the macro m and b's - outside of the macro.
With Macro 2.0 hygiene names are resolved in locations where they are "actually written".

For "atomic" tokens like identifiers or punctuation signs the context is unambiguous, but complex entities like expressions or types can be combined from tokens introduced in different contexts, look for example at this code ultimately expanding into println!("Hello world!")

macro context_parens($name: tt, bang:tt,bang: tt, bang:tt,args: tt) { namename namebang ( $args ) }

macro context_hello($name: tt, $bang: tt) { context_parens!($name, $bang, "Hello world!") }

macro context_bang($name: tt) { context_hello!($name, !) }

macro context_println() { context_bang!(println) }

fn main() { context_println!(); }

So, what is the "call site" context of the println macro in this case?
Where should we resolve identifiers with call-site hygiene for macros invoked like this?

Contexts of "non-atomic" entities are important for several other reason than determining call-site hygiene, for example in Struct { field1, field2, ..rest } fields fieldN where N > 2 are checked for privacy in the context of ..rest fragment, but that fragment may also be a Frankenstein's monster combined from pieces with different contexts.


Proposed solution:

For each complex entity figure out and document an atomic entity that is "essential" for that complex entity and that serves as a source of hygienic context for the complex entity.

For example, for binary operator expressions the context may be determined by the context of the operator: context($a + <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>b</mi><mo stretchy="false">)</mo><mo>=</mo><mi>c</mi><mi>o</mi><mi>n</mi><mi>t</mi><mi>e</mi><mi>x</mi><mi>t</mi><mo stretchy="false">(</mo><mo>+</mo><mo stretchy="false">)</mo><mi mathvariant="normal">‘</mi><mo separator="true">,</mo><mi>f</mi><mi>o</mi><mi>r</mi><mi>t</mi><mi>h</mi><mi>e</mi><mi mathvariant="normal">&quot;</mi><mi>r</mi><mi>e</mi><mi>m</mi><mi>a</mi><mi>i</mi><mi>n</mi><mi>i</mi><mi>n</mi><mi>g</mi><mi>f</mi><mi>i</mi><mi>e</mi><mi>l</mi><mi>d</mi><mi>s</mi><mi mathvariant="normal">&quot;</mi><mi>f</mi><mi>r</mi><mi>a</mi><mi>g</mi><mi>m</mi><mi>e</mi><mi>n</mi><mi>t</mi><mi mathvariant="normal">‘</mi><mi mathvariant="normal">.</mi><mi mathvariant="normal">.</mi></mrow><annotation encoding="application/x-tex">b) = context(+), for the "remaining fields" fragment ..</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">b</span><span class="mclose">)</span><span class="mspace" style="margin-right:0.2778em;"></span><span class="mrel">=</span><span class="mspace" style="margin-right:0.2778em;"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-0.25em;"></span><span class="mord mathnormal">co</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord mathnormal">e</span><span class="mord mathnormal">x</span><span class="mord mathnormal">t</span><span class="mopen">(</span><span class="mord">+</span><span class="mclose">)</span><span class="mord">‘</span><span class="mpunct">,</span><span class="mspace" style="margin-right:0.1667em;"></span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord mathnormal" style="margin-right:0.02778em;">or</span><span class="mord mathnormal">t</span><span class="mord mathnormal">h</span><span class="mord mathnormal">e</span><span class="mord">&quot;</span><span class="mord mathnormal">re</span><span class="mord mathnormal">mainin</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord mathnormal">i</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.01968em;">l</span><span class="mord mathnormal">d</span><span class="mord mathnormal">s</span><span class="mord">&quot;</span><span class="mord mathnormal" style="margin-right:0.10764em;">f</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">a</span><span class="mord mathnormal" style="margin-right:0.03588em;">g</span><span class="mord mathnormal">m</span><span class="mord mathnormal">e</span><span class="mord mathnormal">n</span><span class="mord mathnormal">t</span><span class="mord">‘..</span></span></span></span>rest mentioned above the context may be determined by the context of .., etc.

I'm... not sure what that essential atomic token would be for macro invocations, probably ! for bang macros and [] for attribute macros.
(Note that paired delimiters like (), [] and {} always have the same context in a pair).