/ Mozilla

Macros pt6 - more issues

I discovered another couple of issues with Rust macros (both affect the macro_rules flavour).

Nested macros and arguments

These don't work because of the way macros do substitution. When expanding a macro, the expander looks for token strings starting with $ to expand. If there is a variable which is not bound by the outer macro, then it is an error. E.g.,

macro_rules! foo {
    () => {
        macro_rules! bar {
            ($x: ident) => { $x }
        }
        bar!(foo);
    }
}

When we try to expand foo!(), the expander errors out because it can't find a value for $x, it doesn't know that macro_rules bar is binding $x.

The proper solution here is to make macros aware of binding and lexical scoping etc. However, I'm not sure that is possible because macros are not parsed until after expansion. We might be able to fix this by just being less eager to report these errors. We wouldn't get proper lexical scoping, i.e., all macro variables would need to have different names, but at least the easy cases would work.

Matching expression fragments

Example:

macro_rules! foo {
    ( if $e:expr { $s:stmt } ) => {
        if $e {
            $s
        }
    }
}

fn main() {
    let x = 1;
    foo! {
        if 0 < x {
            ()
        }
    }
}

This gives an error because it tries to parse x { as the start of a struct literal. We have a hack in the parser where in some contexts where we parse an expression, we explicitly forbid struct literals from appearing so that we can correctly parse a following block. This is not usually apparent, but in this case, where the macro expects an expr, what we'd like to have is 'an expression but not a struct literal'. However, exposing this level of detail about the parser implementation to macro authors (not even procedural macro authors!) feels bad. Not sure how to tackle this one.

Relatedly, it would be nice to be able to match other fragments of the AST, for example the interior of a block. Again, there is the issue of how much of the internals we wish to expose.

(HT @bmastenbrook for the second issue).