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

Hygiene broken in multi-statement macros #31856

Closed
durka opened this issue Feb 24, 2016 · 2 comments
Closed

Hygiene broken in multi-statement macros #31856

durka opened this issue Feb 24, 2016 · 2 comments
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..)

Comments

@durka
Copy link
Contributor

durka commented Feb 24, 2016

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

@Sgeo
Copy link
Contributor

Sgeo commented Feb 24, 2016

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
Copy link
Contributor Author

durka commented Feb 25, 2016

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.

@steveklabnik steveklabnik added the A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..) label Mar 11, 2016
jseyfried added a commit to jseyfried/rust that referenced this issue Apr 14, 2016
jseyfried added a commit to jseyfried/rust that referenced this issue Apr 15, 2016
Manishearth added a commit to Manishearth/rust that referenced this issue Apr 15, 2016
Fix macro hygiene bug

This fixes rust-lang#32922 (EDIT: and fixes rust-lang#31856), macro hygiene bugs.
It is a [breaking-change]. For example, the following would break:
```rust
fn main() {
    let x = true;
    macro_rules! foo { () => {
        let x = 0;
        macro_rules! bar { () => {x} }
        let _: bool = bar!();
        //^ `bar!()` used to resolve the first `x` (a bool),
        //| but will now resolve to the second x (an i32).
    }}
    foo! {};
}
```

r? @nrc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-macros Area: All kinds of macros (custom derive, macro_rules!, proc macros, ..)
Projects
None yet
Development

No branches or pull requests

3 participants