0

Given this code:

macro_rules! macro_a {
    ($val:expr) => {
        let tmp = $val;
        println!("{}", tmp);
    };
}

fn main() {
    let tmp = false;
    macro_a!(42);
    println!("After macro_a: {}", tmp);
}

According to cargo expand, this expands to:

#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
    let tmp = false;
    let tmp = 42;
    {
        ::std::io::_print(format_args!("{0}\n", tmp));
    };
    {
        ::std::io::_print(format_args!("After macro_a: {0}\n", tmp));
    };
}

And when I mouse-hover over the variable tmp in the last println!(), VSCode shows me the hint let tmp: i32.

Yet, when I compile and run it, I get:

42
After macro_a: false

I would have expected:

42
After macro_a: 42

How does false sneak by the macro? Is this a compiler bug?


If I compile the output of cargo expand directly, I get the following output:

#![feature(prelude_import)]
#![feature(print_internals)]

#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
    let tmp = false;
    let tmp = 42;
    {
        ::std::io::_print(format_args!("{0}\n", tmp));
    };
    {
        ::std::io::_print(format_args!("After macro_a: {0}\n", tmp));
    };
}
$ cargo +nightly run
42
After macro_a: 42

What's going on here? Do macros introduce a hidden scope or something? Is this a bug in rustc or in cargo-expand? One of them have to be wrong, otherwise this behaviour wouldn't be inconsistent ...


If I leave out the first declaration of tmp, I get a compiler error:

macro_rules! macro_a {
    ($val:expr) => {
        let tmp = $val;
        println!("{}", tmp);
    };
}

fn main() {
    macro_a!(42);
    println!("After macro_a: {}", tmp);
}
error[E0425]: cannot find value `tmp` in this scope
  --> src/main.rs:10:35
   |
10 |     println!("After macro_a: {}", tmp);
   |                                   ^^^ not found in this scope

I thought this should work? Isn't a macro simply inserted into the code?


This compiles:

macro_rules! macro_a {
    ($name: ident, $val:expr) => {
        let $name = $val;
        println!("{}", $name);
    };
}

fn main() {
    macro_a!(tmp, 42);
    println!("After macro_a: {}", tmp);
}

What's the difference between the two?

Finomnis
  • 18,094
  • 1
  • 20
  • 27
  • @justinas I guess the people of `cargo-expand` already considered this case, so this behavior is deliberate? – Finomnis Feb 10 '23 at 12:10
  • I am actually not quite sure whether `cargo-expand` is faithful to how compiler expands macros, or does something more naive. – justinas Feb 10 '23 at 12:28
  • 1
    The problem is it's not possible to represent faithfully the extended macro with regular rust code. This would require renaming the variables declared in the macro to show that they belong to the local macro namespace and not the calling namespace. E.g. something like [this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f58bd13a8236d5ee375abd75981ecb42) (which of course is not valid Rust code) – Jmb Feb 10 '23 at 13:39

0 Answers0