Skip to content

Commit

Permalink
Rollup merge of rust-lang#96455 - dtolnay:writetmp, r=m-ou-se
Browse files Browse the repository at this point in the history
Make write/print macros eagerly drop temporaries

This PR fixes the 2 regressions in rust-lang#96434 (`println` and `eprintln`) and changes all the other similar macros (`write`, `writeln`, `print`, `eprint`) to match the old pre-rust-lang#94868 behavior of `println` and `eprintln`.

argument position | before rust-lang#94868 | after rust-lang#94868 | after this PR
--- |:---:|:---:|:---:
`write!($tmp, "…", …)` | :rage: | :rage: | :smiley_cat:
`write!(…, "…", $tmp)` | :rage: | :rage: | :smiley_cat:
`writeln!($tmp, "…", …)` | :rage: | :rage: | :smiley_cat:
`writeln!(…, "…", $tmp)` | :rage: | :rage: | :smiley_cat:
`print!("…", $tmp)` | :rage: | :rage: | :smiley_cat:
`println!("…", $tmp)` | :smiley_cat: | :rage: | :smiley_cat:
`eprint!("…", $tmp)` | :rage: | :rage: | :smiley_cat:
`eprintln!("…", $tmp)` | :smiley_cat: | :rage: | :smiley_cat:
`panic!("…", $tmp)` | :smiley_cat: | :smiley_cat: | :smiley_cat:

Example of code that is affected by this change:

```rust
use std::sync::Mutex;

fn main() {
    let mutex = Mutex::new(0);
    print!("{}", mutex.lock().unwrap()) /* no semicolon */
}
```

You can see several real-world examples like this in the Crater links at the top of rust-lang#96434. This code failed to compile prior to this PR as follows, but works after this PR.

```console
error[E0597]: `mutex` does not live long enough
 --> src/main.rs:5:18
  |
5 |     print!("{}", mutex.lock().unwrap()) /* no semicolon */
  |                  ^^^^^^^^^^^^---------
  |                  |
  |                  borrowed value does not live long enough
  |                  a temporary with access to the borrow is created here ...
6 | }
  | -
  | |
  | `mutex` 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`
```
  • Loading branch information
GuillaumeGomez committed May 14, 2022
2 parents b36be12 + a03f15a commit 0620b2e
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 12 deletions.
14 changes: 8 additions & 6 deletions library/core/src/macros/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,9 +496,10 @@ macro_rules! r#try {
#[stable(feature = "rust1", since = "1.0.0")]
#[cfg_attr(not(test), rustc_diagnostic_item = "write_macro")]
macro_rules! write {
($dst:expr, $($arg:tt)*) => {
$dst.write_fmt($crate::format_args!($($arg)*))
};
($dst:expr, $($arg:tt)*) => {{
let result = $dst.write_fmt($crate::format_args!($($arg)*));
result
}};
}

/// Write formatted data into a buffer, with a newline appended.
Expand Down Expand Up @@ -553,9 +554,10 @@ macro_rules! writeln {
($dst:expr $(,)?) => {
$crate::write!($dst, "\n")
};
($dst:expr, $($arg:tt)*) => {
$dst.write_fmt($crate::format_args_nl!($($arg)*))
};
($dst:expr, $($arg:tt)*) => {{
let result = $dst.write_fmt($crate::format_args_nl!($($arg)*));
result
}};
}

/// Indicates unreachable code.
Expand Down
12 changes: 6 additions & 6 deletions library/std/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ macro_rules! panic {
#[cfg_attr(not(test), rustc_diagnostic_item = "print_macro")]
#[allow_internal_unstable(print_internals)]
macro_rules! print {
($($arg:tt)*) => {
$crate::io::_print($crate::format_args!($($arg)*))
};
($($arg:tt)*) => {{
$crate::io::_print($crate::format_args!($($arg)*));
}};
}

/// Prints to the standard output, with a newline.
Expand Down Expand Up @@ -133,9 +133,9 @@ macro_rules! println {
#[cfg_attr(not(test), rustc_diagnostic_item = "eprint_macro")]
#[allow_internal_unstable(print_internals)]
macro_rules! eprint {
($($arg:tt)*) => {
$crate::io::_eprint($crate::format_args!($($arg)*))
};
($($arg:tt)*) => {{
$crate::io::_eprint($crate::format_args!($($arg)*));
}};
}

/// Prints to the standard error, with a newline.
Expand Down
70 changes: 70 additions & 0 deletions src/test/ui/macros/format-args-temporaries.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// check-pass

use std::fmt::{self, Display};

struct Mutex;

impl Mutex {
fn lock(&self) -> MutexGuard {
MutexGuard(self)
}
}

struct MutexGuard<'a>(&'a Mutex);

impl<'a> Drop for MutexGuard<'a> {
fn drop(&mut self) {
// Empty but this is a necessary part of the repro. Otherwise borrow
// checker is fine with 'a dangling at the time that MutexGuard goes out
// of scope.
}
}

impl<'a> MutexGuard<'a> {
fn write_fmt(&self, _args: fmt::Arguments) {}
}

impl<'a> Display for MutexGuard<'a> {
fn fmt(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
Ok(())
}
}

fn main() {
let _write = {
let out = Mutex;
let mutex = Mutex;
write!(out.lock(), "{}", mutex.lock()) /* no semicolon */
};

let _writeln = {
let out = Mutex;
let mutex = Mutex;
writeln!(out.lock(), "{}", mutex.lock()) /* no semicolon */
};

let _print = {
let mutex = Mutex;
print!("{}", mutex.lock()) /* no semicolon */
};

let _println = {
let mutex = Mutex;
println!("{}", mutex.lock()) /* no semicolon */
};

let _eprint = {
let mutex = Mutex;
eprint!("{}", mutex.lock()) /* no semicolon */
};

let _eprintln = {
let mutex = Mutex;
eprintln!("{}", mutex.lock()) /* no semicolon */
};

let _panic = {
let mutex = Mutex;
panic!("{}", mutex.lock()) /* no semicolon */
};
}

0 comments on commit 0620b2e

Please sign in to comment.