diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index bd62bc5c3056c..3a115a8b8b6c6 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -496,10 +496,9 @@ 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)*) => {{ - let result = $dst.write_fmt($crate::format_args!($($arg)*)); - result - }}; + ($dst:expr, $($arg:tt)*) => { + $dst.write_fmt($crate::format_args!($($arg)*)) + }; } /// Write formatted data into a buffer, with a newline appended. @@ -554,10 +553,9 @@ macro_rules! writeln { ($dst:expr $(,)?) => { $crate::write!($dst, "\n") }; - ($dst:expr, $($arg:tt)*) => {{ - let result = $dst.write_fmt($crate::format_args_nl!($($arg)*)); - result - }}; + ($dst:expr, $($arg:tt)*) => { + $dst.write_fmt($crate::format_args_nl!($($arg)*)) + }; } /// Indicates unreachable code. diff --git a/src/test/ui/macros/format-args-temporaries-async.rs b/src/test/ui/macros/format-args-temporaries-async.rs new file mode 100644 index 0000000000000..d959329b9fce2 --- /dev/null +++ b/src/test/ui/macros/format-args-temporaries-async.rs @@ -0,0 +1,37 @@ +// check-pass +// edition:2021 + +use std::fmt::{self, Display}; +use std::future::Future; +use std::io; +use std::pin::Pin; +use std::task::{Context, Poll}; + +struct AsyncStdout; + +impl AsyncStdout { + fn write_fmt<'a>(&'a mut self, _args: fmt::Arguments) -> WriteFmtFuture<'a, Self> + where + Self: Unpin, + { + WriteFmtFuture(self) + } +} + +struct WriteFmtFuture<'a, T>(&'a mut T); + +impl<'a, T> Future for WriteFmtFuture<'a, T> { + type Output = io::Result<()>; + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + unimplemented!() + } +} + +async fn async_main() { + let _write = write!(&mut AsyncStdout, "...").await; + let _writeln = writeln!(&mut AsyncStdout, "...").await; +} + +fn main() { + let _ = async_main; +} diff --git a/src/test/ui/macros/format-args-temporaries-in-write.rs b/src/test/ui/macros/format-args-temporaries-in-write.rs new file mode 100644 index 0000000000000..339ccbc33ac98 --- /dev/null +++ b/src/test/ui/macros/format-args-temporaries-in-write.rs @@ -0,0 +1,50 @@ +// check-fail + +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. + } +} + +struct Out; + +impl Out { + 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() { + // FIXME(dtolnay): We actually want both of these to work. I think it's + // sadly unimplementable today though. + + let _write = { + let mutex = Mutex; + write!(Out, "{}", mutex.lock()) /* no semicolon */ + //~^ ERROR `mutex` does not live long enough + }; + + let _writeln = { + let mutex = Mutex; + writeln!(Out, "{}", mutex.lock()) /* no semicolon */ + //~^ ERROR `mutex` does not live long enough + }; +} diff --git a/src/test/ui/macros/format-args-temporaries-in-write.stderr b/src/test/ui/macros/format-args-temporaries-in-write.stderr new file mode 100644 index 0000000000000..03ecc4b4418c6 --- /dev/null +++ b/src/test/ui/macros/format-args-temporaries-in-write.stderr @@ -0,0 +1,43 @@ +error[E0597]: `mutex` does not live long enough + --> $DIR/format-args-temporaries-in-write.rs:41:27 + | +LL | write!(Out, "{}", mutex.lock()) /* no semicolon */ + | ^^^^^^^^^^^^ + | | + | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +LL | +LL | }; + | -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard` + | | + | `mutex` dropped here while still borrowed + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL + | +LL | $dst.write_fmt($crate::format_args!($($arg)*)); + | + + +error[E0597]: `mutex` does not live long enough + --> $DIR/format-args-temporaries-in-write.rs:47:29 + | +LL | writeln!(Out, "{}", mutex.lock()) /* no semicolon */ + | ^^^^^^^^^^^^ + | | + | borrowed value does not live long enough + | a temporary with access to the borrow is created here ... +LL | +LL | }; + | -- ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `MutexGuard` + | | + | `mutex` dropped here while still borrowed + | +help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped + --> $SRC_DIR/core/src/macros/mod.rs:LL:COL + | +LL | $dst.write_fmt($crate::format_args_nl!($($arg)*)); + | + + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0597`. diff --git a/src/test/ui/macros/format-args-temporaries.rs b/src/test/ui/macros/format-args-temporaries.rs index ddd4c9754bfa4..59323828bc37f 100644 --- a/src/test/ui/macros/format-args-temporaries.rs +++ b/src/test/ui/macros/format-args-temporaries.rs @@ -20,10 +20,6 @@ impl<'a> Drop for MutexGuard<'a> { } } -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(()) @@ -31,18 +27,6 @@ impl<'a> Display for MutexGuard<'a> { } 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 */