Skip to content

Hygiene broken in multi-statement macros #31856

Closed
@durka

Description

@durka
Contributor

Multi-statement macros (macros that expand to multiple statements) are hard to use because hygiene runs separately on each line, meaning it's very easy to "lose" identifiers.

Here are some examples. The macro is trying to declare a variable in the scope where it was called, but it takes multiple statements to construct.

/*
// no good: $n declared in inner scope
macro_rules! m {
    ($n:ident) => ({
        let a = 1;
        let $n = a;
    })
}
*/

/*
// no good: hygiene throws away "a"
macro_rules! m {
    ($n:ident) => (
        let a = 1;
        let $n = a;
    )
}
*/

// workaround
macro_rules! m {
    ($n:ident) => (
        let $n = {
            let a = 1;
            a
        };
    )
}

fn main() {
    m!(b);
    println!("{}", b);
}

This is a long-standing bug, not a regression. I previously reported this here and here (can likely be closed) but it wasn't very visible. ping @Sgeo @chris-chambers

Activity

Sgeo

Sgeo commented on Feb 24, 2016

@Sgeo
Contributor

I am experimentally playing with the idea of using hygiene to hide one object, then exposing another object that refers to the hidden object. The macro I have is

macro_rules! take_mut {
    ($mut_ref:expr => $into:pat) => {
        let mut scope = Scope::new();
        let $into = Hole::new(&mut scope, $mut_ref);
    }
}

Trying to use this fails with


<anon>:42:36: 42:41 error: unresolved name `scope` [E0425]
<anon>:42         let $into = Hole::new(&mut scope, $mut_ref);
                                             ^~~~~
<anon>:58:9: 58:46 note: in this expansion of take_mut! (defined in <anon>)
<anon>:42:36: 42:41 help: see the detailed explanation for E0425
<anon>:42:36: 42:41 error: unresolved name `scope` [E0425]
<anon>:42         let $into = Hole::new(&mut scope, $mut_ref);
                                             ^~~~~
<anon>:59:9: 59:46 note: in this expansion of take_mut! (defined in <anon>)
<anon>:42:36: 42:41 help: see the detailed explanation for E0425
error: aborting due to 2 previous errors
playpen: application terminated with error code 101
durka

durka commented on Feb 25, 2016

@durka
ContributorAuthor

For the record, you can write code that relies on this bug. This code

macro_rules! m {
    ($i:ident) => {
        let mut $i = 2;
        $i = $i + 1;
    }
}

fn no_macro() {
    let mut a = 1;
    let mut a = 2;
    a = a + 1;

    println!("{}", a);
}

fn yes_macro() {
    let mut a = 1;
    m!(a);

    println!("{}", a);
}

produces code that looks the same under pretty-expansion, but no_macro (which represents what the code would look like were this bug fixed) prints 3, while yes_macro prints 2.

added
A-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)
on Mar 11, 2016
added a commit that references this issue on Apr 15, 2016

Rollup merge of rust-lang#32923 - jseyfried:fix_hygiene, r=nrc

6a0cfbc
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-macrosArea: All kinds of macros (custom derive, macro_rules!, proc macros, ..)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @steveklabnik@durka@Sgeo

        Issue actions

          Hygiene broken in multi-statement macros · Issue #31856 · rust-lang/rust