Repository: rust
Issue: odd compiler error with assert_eq! in unusual corner case, possibly related to missing semicolon in macro definition
Labels: A-diagnostics,T-compiler

Originally discussed in [this thread on r/rust](https://www.reddit.com/r/rust/comments/p0xgs2/hey_rustaceans_got_an_easy_question_ask_here/h8l247v/). Due to a quirk in how the `assert_eq!` macro is defined, I get a compiler error in code that I think should be compiling successfully. The error is related to an odd-looking-but-arguably-legal missing semicolon:

```rust
fn main() {
    let foo = std::sync::Mutex::new(String::from("foo"));
    assert_eq!(foo.lock().unwrap().as_str(), "foo")  // no semicolon here
}
```

Now I wouldn't normally write that line, but if I think about what it "should" do, I'd say `assert_eq!` is supposed to "return" `()`, so this should be legal. However, it's not legal. We get this compiler error ([playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ef98ae795144a04b90c407308895ff3e)):

```
error[E0597]: `foo` does not live long enough
  --> src/main.rs:17:16
   |
17 |     assert_eq!(foo.lock().unwrap().as_str(), "foo")
   |                ^^^----------------
   |                |
   |                borrowed value does not live long enough
   |                a temporary with access to the borrow is created here ...
18 | }
   | -
   | |
   | `foo` dropped here while still borrowed
   | ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard`
   |
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
   |
47 |         };
   |          ^
```

I don't fully understand all the machinery behind this error, but I believe what's happening is that the `assert_eq!` macro is using a clever trick with an internal `match` statement to extend the life of temporaries, and here that trick works a little _too well_. The temporaries get extended all the way to the end of the function, at which point the compiler is worried that they might outlive the local variable they borrow. Or something like that. Here's a slightly simplified version of `assert_eq!` that reproduces this behavior ([playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b745f3ab5a1e9e204a61b684ebb92746)):

```rust
macro_rules! assert_eq {
    ($left:expr, $right:expr $(,)?) => {{
        match (&$left, &$right) {  //           <-- here is the clever match trick
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    panic!("assertion failed");
                }
            }
        }
    }};
}
```

It seems like this wacky corner case can be fixed. If we add a semicolon here in the macro, the error goes away ([playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9e536a760b78707680e49df760fac0b5)):

```rust
macro_rules! assert_eq {
    ($left:expr, $right:expr $(,)?) => {{
        match (&$left, &$right) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    panic!("assertion failed");
                }
            }
        };  //                                   <-- semicolon added here
    }};
}
```

Anyway, that seems to fix _this_ error. But I'm not good enough with macros to be able to predict whether it might cause weird corner cases elsewhere that I'm not thinking of. Folks who know better: Does this seem like a safe change? If so, is it desirable? If so, would it make sense to look for other macros using the same trick, to see if they might need similar fixes? (At a glance, `assert_ne!` and `assert_matches!` might.)