Skip to content

cannot end function with item-generating macro invoked with curly braces #34418

Closed
@durka

Description

@durka
Contributor

Consider the following macro and all the ways to invoke it:

macro_rules! make_item {
    ($a:ident) => { struct $a; }
}

make_item!(A); // ok (semicolon required)
make_item! { B } // ok (semicolon not required)

fn c() { make_item!(C); } // ok
fn d() { make_item!(D) } // bad (expected: missing semicolon)
fn e() { make_item! { E } } // bad (BUG: semicolon should not be required)
fn f() { make_item! { F }; } // ok (workaround)
fn gh() { make_item! { G } make_item!(H); } // ok (further evidence case E is a bug)

I believe that case E should be accepted, however it gives the error that the macro is invalid in expression position. My theory (before investigating) is that the tail of the function is considered as expression position because the macro expander forgets the possibility that a macro may generate items.

Activity

changed the title [-]cannot end function with item-genering macro invoked with curly braces[/-] [+]cannot end function with item-generating macro invoked with curly braces[/+] on Jun 23, 2016
durka

durka commented on Jun 23, 2016

@durka
ContributorAuthor

I'm working on a fix.

jseyfried

jseyfried commented on Jun 23, 2016

@jseyfried
Contributor

cc me

durka

durka commented on Jun 23, 2016

@durka
ContributorAuthor

OK, a little harder to fix than we thought. By the time we get to expanding macros in a block, it's already an ast::Block which consists of statements and an optional expression. I think the decision of whether the last thing in a block is the last statement, or the expression, is purely syntactical -- whether it ends in a semicolon. We can change the expansion to be .fold_stmt() instead of .fold_expr(), but I believe that will break fn foo() -> Vec<i32> { vec! { 1, 2 } } because StmtKind::Expr must have unit type. So I don't know how to do what I want, which is:

  • try expanding the macro as an expression (somehow suppress diagnostics)
    • success: done
    • fail: try expanding the macro as a statement
      • success: add it to the end of the block's stmt list and turn the expr to None
      • fail: error

We could change expand_block to always expand the trailing expr as a stmt, and if it happens to expand to a StmtKind::Expr, then keep it as a trailing expr, otherwise move it to the stmt list. The docs for StmtKind say that StmtKind::Expr must have unit type (thus my assumption that this strategy would break the above Vec example), but it was pointed out that types aren't known at expansion time, so maybe it's okay to blatantly disregard this documented invariant. I won't really be able to try this until next week.

jseyfried

jseyfried commented on Jun 23, 2016

@jseyfried
Contributor

@durka

We could change expand_block to always expand the trailing expr as a stmt, and if it happens to expand to a StmtKind::Expr, then keep it as a trailing expr, otherwise move it to the stmt list.

I like this idea.

durka

durka commented on Jun 23, 2016

@durka
ContributorAuthor

Why does Block even have the Option<P<Expr>>, one wonders, when StmtKind::Expr exists?

jseyfried

jseyfried commented on Jun 23, 2016

@jseyfried
Contributor

Good question...

durka

durka commented on Jun 23, 2016

@durka
ContributorAuthor

fold_stmt can return a number of stmts, so really it's: expand the trailing expr as statements, if the last one is StmtKind::Expr then peel it off as the trailing expr, append the rest to the stmt list.

jseyfried

jseyfried commented on Jun 23, 2016

@jseyfried
Contributor

Exactly!
Ideally we'll refactor away the Option<P<Expr>> from the block so it's all statements.

jseyfried

jseyfried commented on Jun 23, 2016

@jseyfried
Contributor

I'm going to try to remove the field expr of Block in the next breaking batch, which should land in a day or two.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @durka@jseyfried

        Issue actions

          cannot end function with item-generating macro invoked with curly braces · Issue #34418 · rust-lang/rust