Skip to content

Commit eda41ad

Browse files
committed
Auto merge of rust-lang#104134 - dtolnay:panictemporaries, r=joshtriplett
Shorten lifetime of panic temporaries in panic_fmt case This fixes an issue called out by `@fasterthanlime` in https://octodon.social/`@fasterthanlime/109304454114856561.` Macros like `todo!("…")` and `panic!("…", …)` drop their `format_args` temporary at the nearest enclosing semicolon **outside** the macro invocation, not inside the macro invocation. Due to the formatting internals being type-erased in a way that is not thread-safe, this blocks futures from being `Send` if there is an `await` anywhere between the panic call and the nearest enclosing semicolon. **Example:** ```rust #![allow(unreachable_code)] async fn f(_: u8) {} async fn g() { f(todo!("...")).await; } fn require_send(_: impl Send) {} fn main() { require_send(g()); } ``` **Before:** ```console error: future cannot be sent between threads safely --> src/main.rs:15:18 | 15 | require_send(g()); | ^^^ future returned by `g` is not `Send` | = help: the trait `Sync` is not implemented for `core::fmt::Opaque` note: future is not `Send` as this value is used across an await --> src/main.rs:9:20 | 9 | f(todo!("...")).await; | ------------ ^^^^^^ await occurs here, with `$crate::format_args!($($arg)+)` maybe used later | | | has type `ArgumentV1<'_>` which is not `Send` note: `$crate::format_args!($($arg)+)` is later dropped here --> src/main.rs:9:26 | 9 | f(todo!("...")).await; | ^ note: required by a bound in `require_send` --> src/main.rs:12:25 | 12 | fn require_send(_: impl Send) {} | ^^^^ required by this bound in `require_send` ``` **After:** works. Arguably there is a rustc fix that could work here too, instead of a standard library change. Rustc could be taught that the code shown above is fine to compile because the `await` is unreachable and so temporaries before the `await` do not need to get put in the anonymous compiler-generated `Future` struct, regardless of syntactically where they're supposed to be dropped according to the language semantics. I would be open to that, though my recollection is that in the past we have been very hesitant about introducing any smarts into Drop placement. People want the language rules about where temporaries are dropped to be as simplistic and predictable as possible.
2 parents 8e8116c + 4be97e0 commit eda41ad

File tree

5 files changed

+42
-25
lines changed

5 files changed

+42
-25
lines changed

library/core/src/panic.rs

+10-6
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,11 @@ pub macro panic_2015 {
3535
("{}", $arg:expr $(,)?) => (
3636
$crate::panicking::panic_display(&$arg)
3737
),
38-
($fmt:expr, $($arg:tt)+) => (
39-
$crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+))
40-
),
38+
($fmt:expr, $($arg:tt)+) => ({
39+
// Semicolon to prevent temporaries inside the formatting machinery from
40+
// being considered alive in the caller after the panic_fmt call.
41+
$crate::panicking::panic_fmt($crate::const_format_args!($fmt, $($arg)+));
42+
}),
4143
}
4244

4345
#[doc(hidden)]
@@ -53,9 +55,11 @@ pub macro panic_2021 {
5355
("{}", $arg:expr $(,)?) => (
5456
$crate::panicking::panic_display(&$arg)
5557
),
56-
($($t:tt)+) => (
57-
$crate::panicking::panic_fmt($crate::const_format_args!($($t)+))
58-
),
58+
($($t:tt)+) => ({
59+
// Semicolon to prevent temporaries inside the formatting machinery from
60+
// being considered alive in the caller after the panic_fmt call.
61+
$crate::panicking::panic_fmt($crate::const_format_args!($($t)+));
62+
}),
5963
}
6064

6165
#[doc(hidden)]

library/std/src/panic.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ pub macro panic_2015 {
2626
$crate::rt::panic_display(&$arg)
2727
}),
2828
($fmt:expr, $($arg:tt)+) => ({
29-
$crate::rt::panic_fmt($crate::const_format_args!($fmt, $($arg)+))
29+
// Semicolon to prevent temporaries inside the formatting machinery from
30+
// being considered alive in the caller after the panic_fmt call.
31+
$crate::rt::panic_fmt($crate::const_format_args!($fmt, $($arg)+));
3032
}),
3133
}
3234

src/tools/clippy/tests/ui/diverging_sub_expression.stderr

+1-9
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,11 @@ error: sub-expression diverges
3030
LL | 3 => true || diverge(),
3131
| ^^^^^^^^^
3232

33-
error: sub-expression diverges
34-
--> $DIR/diverging_sub_expression.rs:36:30
35-
|
36-
LL | _ => true || panic!("boo"),
37-
| ^^^^^^^^^^^^^
38-
|
39-
= note: this error originates in the macro `$crate::panic::panic_2021` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info)
40-
4133
error: sub-expression diverges
4234
--> $DIR/diverging_sub_expression.rs:38:26
4335
|
4436
LL | _ => true || break,
4537
| ^^^^^
4638

47-
error: aborting due to 7 previous errors
39+
error: aborting due to 6 previous errors
4840

tests/ui/macros/panic-temporaries.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// check-pass
2+
// edition:2021
3+
4+
#![allow(unreachable_code)]
5+
6+
async fn f(_: u8) {}
7+
8+
async fn g() {
9+
// Todo returns `!`, so the await is never reached, and in particular the
10+
// temporaries inside the formatting machinery are not still alive at the
11+
// await point.
12+
f(todo!("...")).await;
13+
}
14+
15+
fn require_send(_: impl Send) {}
16+
17+
fn main() {
18+
require_send(g());
19+
}

tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout

+9-9
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn arbitrary_consuming_method_for_demonstration_purposes() {
2626

2727
{
2828
::std::rt::panic_fmt(format_args!("Assertion failed: elem as usize\nWith captures:\n elem = {0:?}\n",
29-
__capture0))
29+
__capture0));
3030
}
3131
}
3232
};
@@ -42,7 +42,7 @@ fn addr_of() {
4242
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
4343
{
4444
::std::rt::panic_fmt(format_args!("Assertion failed: &elem\nWith captures:\n elem = {0:?}\n",
45-
__capture0))
45+
__capture0));
4646
}
4747
}
4848
};
@@ -58,7 +58,7 @@ fn binary() {
5858
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
5959
{
6060
::std::rt::panic_fmt(format_args!("Assertion failed: elem == 1\nWith captures:\n elem = {0:?}\n",
61-
__capture0))
61+
__capture0));
6262
}
6363
}
6464
};
@@ -71,7 +71,7 @@ fn binary() {
7171
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
7272
{
7373
::std::rt::panic_fmt(format_args!("Assertion failed: elem >= 1\nWith captures:\n elem = {0:?}\n",
74-
__capture0))
74+
__capture0));
7575
}
7676
}
7777
};
@@ -84,7 +84,7 @@ fn binary() {
8484
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
8585
{
8686
::std::rt::panic_fmt(format_args!("Assertion failed: elem > 0\nWith captures:\n elem = {0:?}\n",
87-
__capture0))
87+
__capture0));
8888
}
8989
}
9090
};
@@ -97,7 +97,7 @@ fn binary() {
9797
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
9898
{
9999
::std::rt::panic_fmt(format_args!("Assertion failed: elem < 3\nWith captures:\n elem = {0:?}\n",
100-
__capture0))
100+
__capture0));
101101
}
102102
}
103103
};
@@ -110,7 +110,7 @@ fn binary() {
110110
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
111111
{
112112
::std::rt::panic_fmt(format_args!("Assertion failed: elem <= 3\nWith captures:\n elem = {0:?}\n",
113-
__capture0))
113+
__capture0));
114114
}
115115
}
116116
};
@@ -123,7 +123,7 @@ fn binary() {
123123
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
124124
{
125125
::std::rt::panic_fmt(format_args!("Assertion failed: elem != 3\nWith captures:\n elem = {0:?}\n",
126-
__capture0))
126+
__capture0));
127127
}
128128
}
129129
};
@@ -139,7 +139,7 @@ fn unary() {
139139
(&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0);
140140
{
141141
::std::rt::panic_fmt(format_args!("Assertion failed: *elem\nWith captures:\n elem = {0:?}\n",
142-
__capture0))
142+
__capture0));
143143
}
144144
}
145145
};

0 commit comments

Comments
 (0)