Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

macros should be parsed more lazily #6994

Closed
jbclements opened this Issue Jun 7, 2013 · 22 comments

Comments

Projects
None yet
9 participants
@jbclements
Copy link
Contributor

jbclements commented Jun 7, 2013

This is a FIXME issue; currently, macro invocations are parsed too eagerly. In particular, the current system decides before expansion time whether a rhs pattern-use (e.g. $y) is bound or unbound. This is incompatible with macro-producing macros that use a given identifier in a pattern position. It's also incompatible with macros inside macros, where inner binding patterns are believed to be pattern uses. The solution is to delay this decision until the macro is encountered at expansion-time, by which time all outer macros have been expanded.

Here's an example of a macro that doesn't work in the current system:

// the z flows into and out of two macros (g & f) along one path, and one (just g) along the
// other, so the result of the whole thing should be "let z_123 = 3; z_123
macro_rules! g (
    ($x:ident) =>
    (
        {macro_rules! f(
            ($y:ident)=>({let $y=3;$x})
        );
         f!($x)})
)

fn a(){g!(z)}

Trying to expand this yields this error:

Running /usr/local/bin/rustc:
/tmp/g.rs:7:12: 7:13 error: unknown macro variable `y`
/tmp/g.rs:7             ($y:ident)=>({let $y=3;$x})
                        ^

... indicating that the $y is believed to be a use, rather than a binder.

@Aatch

This comment has been minimized.

Copy link
Contributor

Aatch commented Jun 8, 2013

Nominating for well-defined.

@bblum

This comment has been minimized.

Copy link
Contributor

bblum commented Jul 23, 2013

double triage before triage meeting. i think this could maybe be well-covered; we understand how it should work, and it's not a backwards-compat hazard.

@kud1ing

This comment has been minimized.

Copy link

kud1ing commented Aug 9, 2013

I think the tag "A-macos" is unfortunate, since this is not Mac OS specific. Or is it?

@huonw

This comment has been minimized.

Copy link
Member

huonw commented Aug 9, 2013

I imagine it's just a typo because macos is almost macros. It reproduces on linux. (Removed that tag.)

@kud1ing

This comment has been minimized.

Copy link

kud1ing commented Aug 9, 2013

I guess so. Maybe rename "A-macos" to "A-mac_os" or "A-mac"?

@catamorphism

This comment has been minimized.

Copy link
Contributor

catamorphism commented Aug 29, 2013

Accepted for milestone 3, feature-complete

@jbclements

This comment has been minimized.

Copy link
Contributor Author

jbclements commented Aug 30, 2013

Wow! glad to see this wind up on a milestone...

@pnkfelix

This comment has been minimized.

Copy link
Member

pnkfelix commented Mar 20, 2014

P-low, not 1.0.

@huonw

This comment has been minimized.

Copy link
Member

huonw commented Jul 13, 2014

Would this be fixable by allowing $ escapes? E.g.

// the z flows into and out of two macros (g & f) along one path, and one (just g) along the
// other, so the result of the whole thing should be "let z_123 = 3; z_123
macro_rules! g (
    ($x:ident) =>
    (
        {macro_rules! f(
            ($$y:ident)=>({let $$y=3;$x})
        );
         f!($x)})
)

fn a(){g!(z)}

The $$'s would "expand" to a single $ in the expanded code.

@jbclements

This comment has been minimized.

Copy link
Contributor Author

jbclements commented Jul 13, 2014

No, don't go down that road; I don't believe that will lead to a nice solution.

@huonw

This comment has been minimized.

Copy link
Member

huonw commented Jul 13, 2014

OK.

It would be nice if the eventual solution was not specific to nested macro_rules!, since one may wish to have a macro like e.g. foo (($e: expr) => { quote_expr!(cx, $$a + $e) }) (using the escaping for clarity), that is, substitute the passed in expression directly, but pass in a literal $a for quote_expr to handle internally... foo!(2) should become quote_expr!(cx, $a + 2).

(I could easily imagine other more common procedural macros that may wish to "overload" the $... syntax, e.g. a sql!(SELECT * FROM foo WHERE bar = $x) macro.)

@jbclements

This comment has been minimized.

Copy link
Contributor Author

jbclements commented Jul 13, 2014

Hang on... where is the $a bound, in your example?

@huonw

This comment has been minimized.

Copy link
Member

huonw commented Jul 13, 2014

Sorry, was on my phone making it difficult to explain fully. I meant something like

let a: i32 = ...;

macro_rules! foo {
    ($e: expr) => { quote_expr!(cx, $$a + $e) }
}

foo!(1);
foo!(2);

The above is theoretically equivalent to the following, which is unquoting/splicing a into the Gc<syntax::ast::Expr> that quote_expr returns

let a: i32 = ...;
quote_expr!(cx, $a + 1);
quote_expr!(cx, $a + 2);

(The let a: i32 = ...; could also be scoped inside the macro definition.)

@jbclements

This comment has been minimized.

Copy link
Contributor Author

jbclements commented Jul 15, 2014

in this case, you shouldn't need $s at all; the free variable a should always refer to the environment of the definition of the macro.

@jbclements

This comment has been minimized.

Copy link
Contributor Author

jbclements commented Jul 15, 2014

I'm thinking more about this issue, and I think it's misnamed. The fundamental issue here is how the transcription engine matches up bindings of macro variables with uses of those variables.

The solution, I think, is pretty straightforward: the transcription engine should just ignore transcription
variables that it doesn't recognize.

Also, hygiene should stick its nose in, here. There should be a renaming of all pattern identifiers. I think the sneaky side-effect of this will be to make the $'s in the right-hand sides redundant.

@huonw

This comment has been minimized.

Copy link
Member

huonw commented Jul 15, 2014

in this case, you shouldn't need $s at all; the free variable a should always refer to the environment of the definition of the macro.

No, quote_expr!(cx, a + 1) is very different to quote_expr!(cx, $a + 1). Simillarly, sql!(SELECT * FROM table WHERE foo = a) is different to sql!(SELECT * FROM table WHERE foo = $a).

The ones with $ are inserting the value of a from the program with the macro, i.e. if let a = 1i32; then the $a is the same as writing 1. The ones without a are just using a as an identifier (for the quoting, it becomes an expression representing Ident("a") + Literal(1); for sql, it's filtering for the foo column equalling the a column).

@jbclements

This comment has been minimized.

Copy link
Contributor Author

jbclements commented Jul 15, 2014

Hey! Let's argue! :)

I understand now better what you're looking for. Specifically, you want to use $ as an unquote mechanism in procedural macros, in order to splice in other syntactic terms and values from the context of the macro definition (specifically, I missed the fact that you were using quote_expr! before, sorry). This is plausible, but I'd like to point out that it's not the same as what $a does currently—it's not an unquoting mechanism. This is perhaps most clear when the identifier 'a' is bound to a sequence rather than a single syntactic term, but even in the single-match case, the RHS use of $a is not actually inserting the value of an 'a' variable defined in a surrounding context.

I think that something like this is possible, but I don't think that re-using $ is the right way to go.

Lemme fix this, and then we'll see where we are.

@jbclements

This comment has been minimized.

Copy link
Contributor Author

jbclements commented Jul 15, 2014

Oh dear Lord... I may have spoken too quickly. Or perhaps I meant to say, I may have spoken way too late. I see that the source is already peppered with uses of $ that may in fact be unquotes. Reading more...

@jbclements

This comment has been minimized.

Copy link
Contributor Author

jbclements commented Jul 15, 2014

Yes, I see that it absolutely is used that way. That's a bad pun, and I think it might be painful....

@huonw

This comment has been minimized.

Copy link
Member

huonw commented Jul 15, 2014

The problem would be resolved IMO if we have some technique that works for those cases that's not $variablename. (That said, $ is a nice-ish character, so having it as flexible as possible would be nice, but obviously not required.)

@steveklabnik

This comment has been minimized.

Copy link
Member

steveklabnik commented Apr 30, 2015

Triage: OP's code, with a semicolon, still generates the same error.

@bltavares

This comment has been minimized.

Copy link
Contributor

bltavares commented May 30, 2016

Triage: There is still the same error happening on rustc 1.11.0-nightly (6e00b5556 2016-05-29)

// the z flows into and out of two macros (g & f) along one path, and one (just g) along the
// other, so the result of the whole thing should be "let z_123 = 3; z_123
macro_rules! g (
    ($x:ident) =>
    (
        {macro_rules! f(
            ($y:ident)=>({let $y=3;$x})
        );
         f!($x)})
);

fn a(){g!(z)}

Error:

/tmp/zshKZx8Ym:8:14: 8:22 error: unknown macro variable `y`
/tmp/zshKZx8Ym:8             ($y:ident)=>({let $y=3;$x})
                              ^~~~~~~~
/tmp/zshKZx8Ym:8:31: 8:33 error: unknown macro variable `y`
/tmp/zshKZx8Ym:8             ($y:ident)=>({let $y=3;$x})
                                               ^~
error: aborting due to 2 previous errors

bors added a commit that referenced this issue Jul 20, 2016

Auto merge of #34925 - jseyfried:nested_macros, r=eddyb
Support nested `macro_rules!`

Fixes #6994.
r? @eddyb

bors added a commit that referenced this issue Jul 21, 2016

Auto merge of #34925 - jseyfried:nested_macros, r=eddyb
Support nested `macro_rules!`

Fixes #6994.
r? @eddyb

bors added a commit that referenced this issue Jul 21, 2016

Auto merge of #34925 - jseyfried:nested_macros, r=eddyb
Support nested `macro_rules!`

Fixes #6994.
r? @eddyb

bors added a commit that referenced this issue Jul 22, 2016

Auto merge of #34925 - jseyfried:nested_macros, r=eddyb
Support nested `macro_rules!`

Fixes #6994.
r? @eddyb

bors added a commit that referenced this issue Jul 23, 2016

Auto merge of #34925 - jseyfried:nested_macros, r=eddyb
Support nested `macro_rules!`

Fixes #6994.
r? @eddyb

@bors bors closed this in #34925 Jul 23, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.