Sub macro is ambiguous in sub scope

#![feature(macro_metavar_expr)]
macro_rules! init_env {
    ($self:ident) => {
        macro_rules! appear {
            () => {
                $self += 1
            };
        }
        macro_rules! tap {
            (i32) => {
                $self.abs()
            };
        }
        // ... many macro that use $self
    };
}
fn main() {
    let x = 0;
    init_env!(x);
    fn f(x: i32) {
        init_env!(x);
        tap!(); // <- error: ambiguous
    }
}

The tap! is ambiguous as there is a same named macro in parent scope.
How can I make macro expansion to prefer the inner most definition, like normal variable and function?
Or can I remove a macro definition in current scope?

The easiest workaround would be to reorder some of this stuff:

fn main() {
    fn f(x: i32) {
        init_env!(x);
        tap!(); // <- ?
    }
    let x = 0;
    init_env!(x);
}

Since these macro definitions "flow downwards".

Another approach, if you are fine with a little bit extra churn, would be to replace init_env!(x) with your own inlined macro definition:

macro_rules! self_ {() => ( x )}

And then have appear and so on be unconditionally defined, but using self_!() inside their body instead of $self.

1 Like

The problem of the second approach is

macro_rules! self_ {() => ( x )}

can only be write by hand, not by macro. demo

But make a larger wrapper is fine. demo

Yes, hence my saying:

since, indeed, there is a semantic difference between an inlined macro definition and a macro-generated one.


This, on the other hand, is very interesting! This now does hint at having a preprocessor pattern which could alleviate the syntax for the caller:

// Hypothetically, we could have:
#[replace_make_self_with_macro_definition]
fn main ()
{
    let x = 0i32;
    make_self!(x);
    tap!();
    fn f (x: i32)
    {
        make_self!(x);
        tap!();
    }
}

Where replace_make_self_with_macro_definition would be a proc-macro using a Visitor pattern to locate a

Stmt::Semi(Expr::Macro(ExprMacro { mac: Macro { path, tokens, .. }, .. }, ..), ..)
if path.is_ident("make_self")
  • from which you can extract the tokens and parse them as an Ident (the proc-macro equivalent of your $self:ident)

And replace that Stmt with:

parse_quote!(
    macro_rules! self_is {() => ( #that_ident )}
)

:smile:

1 Like