Skip to content

macros should be parsed more lazily #6994

Closed
@jbclements

Description

@jbclements
Contributor

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.

Activity

Aatch

Aatch commented on Jun 8, 2013

@Aatch
Contributor

Nominating for well-defined.

bblum

bblum commented on Jul 23, 2013

@bblum
Contributor

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

kud1ing commented on Aug 9, 2013

@kud1ing

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

huonw

huonw commented on Aug 9, 2013

@huonw
Member

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

kud1ing

kud1ing commented on Aug 9, 2013

@kud1ing

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

catamorphism

catamorphism commented on Aug 29, 2013

@catamorphism
Contributor

Accepted for milestone 3, feature-complete

jbclements

jbclements commented on Aug 30, 2013

@jbclements
ContributorAuthor

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

pnkfelix

pnkfelix commented on Mar 20, 2014

@pnkfelix
Member

P-low, not 1.0.

huonw

huonw commented on Jul 13, 2014

@huonw
Member

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

jbclements commented on Jul 13, 2014

@jbclements
ContributorAuthor

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

huonw

huonw commented on Jul 13, 2014

@huonw
Member

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

jbclements commented on Jul 13, 2014

@jbclements
ContributorAuthor

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

12 remaining items

steveklabnik

steveklabnik commented on Apr 30, 2015

@steveklabnik
Member

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

bltavares

bltavares commented on May 30, 2016

@bltavares
Contributor

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
B1Z0N

B1Z0N commented on Dec 31, 2022

@B1Z0N

image

image

It seem to be working poorly on variadic arguments. Or am I missing something?

CODE

Here

macro_rules! code_macro {
  ($gen:ident, $res:ident) => {
    macro_rules! $res {
      ($($token:tt)*) => {
        http_code!($gen $($token)*)
      };
    }
  };
  ($closure:tt, $res:ident) => {
    let f = $closure;
    code_macro!(f, $res)
  };
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-parserArea: The lexing & parsing of Rust source code to an ASTA-syntaxextArea: Syntax extensionsP-lowLow priority

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @steveklabnik@bltavares@pnkfelix@jbclements@Aatch

        Issue actions

          macros should be parsed more lazily · Issue #6994 · rust-lang/rust