Skip to content

Commit 046fb71

Browse files
committed
Optimize panic!(<str lit>) into method call
1 parent bd3ac03 commit 046fb71

File tree

9 files changed

+117
-34
lines changed

9 files changed

+117
-34
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3509,6 +3509,7 @@ dependencies = [
35093509
name = "rustc_builtin_macros"
35103510
version = "0.0.0"
35113511
dependencies = [
3512+
"rustc-literal-escaper",
35123513
"rustc_ast",
35133514
"rustc_ast_pretty",
35143515
"rustc_attr_parsing",

compiler/rustc_builtin_macros/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ doctest = false
88

99
[dependencies]
1010
# tidy-alphabetical-start
11+
rustc-literal-escaper = "0.0.5"
1112
rustc_ast = { path = "../rustc_ast" }
1213
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
1314
rustc_attr_parsing = { path = "../rustc_attr_parsing" }

compiler/rustc_builtin_macros/src/edition_panic.rs

Lines changed: 81 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,35 @@
1-
use rustc_ast::token::Delimiter;
2-
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
1+
use rustc_ast::token::{Delimiter, Lit, LitKind, TokenKind};
2+
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
33
use rustc_ast::*;
44
use rustc_expand::base::*;
55
use rustc_span::edition::Edition;
6-
use rustc_span::{Span, sym};
6+
use rustc_span::{Ident, Span, Symbol, sym};
7+
8+
// Use an enum to ensure that no new macro calls are added without also updating the message in the
9+
// optimized path below.
10+
enum InnerCall {
11+
Panic2015,
12+
Panic2021,
13+
Unreachable2015,
14+
Unreachable2021,
15+
}
16+
17+
impl InnerCall {
18+
fn symbol(&self) -> Symbol {
19+
match self {
20+
Self::Panic2015 => sym::panic_2015,
21+
Self::Panic2021 => sym::panic_2021,
22+
Self::Unreachable2015 => sym::unreachable_2015,
23+
Self::Unreachable2021 => sym::unreachable_2021,
24+
}
25+
}
26+
}
727

828
/// This expands to either
929
/// - `$crate::panic::panic_2015!(...)` or
1030
/// - `$crate::panic::panic_2021!(...)`
11-
/// depending on the edition.
31+
/// depending on the edition. If the entire message is known at compile time,
32+
/// `core::panicking::panic` may be called as an optimization.
1233
///
1334
/// This is used for both std::panic!() and core::panic!().
1435
///
@@ -19,39 +40,90 @@ pub(crate) fn expand_panic<'cx>(
1940
sp: Span,
2041
tts: TokenStream,
2142
) -> MacroExpanderResult<'cx> {
22-
let mac = if use_panic_2021(sp) { sym::panic_2021 } else { sym::panic_2015 };
43+
let mac = if use_panic_2021(sp) { InnerCall::Panic2021 } else { InnerCall::Panic2015 };
2344
expand(mac, cx, sp, tts)
2445
}
2546

2647
/// This expands to either
2748
/// - `$crate::panic::unreachable_2015!(...)` or
2849
/// - `$crate::panic::unreachable_2021!(...)`
29-
/// depending on the edition.
50+
/// depending on the edition. If the entire message is known at compile time,
51+
/// `core::panicking::panic` may be called as an optimization.
3052
pub(crate) fn expand_unreachable<'cx>(
3153
cx: &'cx mut ExtCtxt<'_>,
3254
sp: Span,
3355
tts: TokenStream,
3456
) -> MacroExpanderResult<'cx> {
35-
let mac = if use_panic_2021(sp) { sym::unreachable_2021 } else { sym::unreachable_2015 };
57+
let mac =
58+
if use_panic_2021(sp) { InnerCall::Unreachable2021 } else { InnerCall::Unreachable2015 };
3659
expand(mac, cx, sp, tts)
3760
}
3861

3962
fn expand<'cx>(
40-
mac: rustc_span::Symbol,
63+
mac: InnerCall,
4164
cx: &'cx ExtCtxt<'_>,
4265
sp: Span,
4366
tts: TokenStream,
4467
) -> MacroExpanderResult<'cx> {
4568
let sp = cx.with_call_site_ctxt(sp);
4669

70+
// If the call is of the form `panic!(<string literal>)` and there are no formatting arguments
71+
// in the string literal, we can call `core::panicking::panic` to centralize the panic logic.
72+
if tts.len() == 1
73+
&& let Some(TokenTree::Token(token, _)) = tts.get(0)
74+
&& let TokenKind::Literal(lit) = &token.kind
75+
&& let Lit { kind: LitKind::Str | LitKind::StrRaw(_), symbol, .. } = lit
76+
&& let msg = symbol.as_str()
77+
&& !msg.contains(|c| c == '{' || c == '}')
78+
{
79+
let msg = match mac {
80+
InnerCall::Panic2015 | InnerCall::Panic2021 => cx.expr(sp, ExprKind::Lit(*lit)),
81+
InnerCall::Unreachable2015 | InnerCall::Unreachable2021 => {
82+
let msg = if msg.contains('\\') {
83+
let mut buf = String::with_capacity(msg.len());
84+
// Force-inlining here is aggressive but the closure is
85+
// called on every char in the string, so it can be hot in
86+
// programs with many long strings containing escapes.
87+
rustc_literal_escaper::unescape_str(
88+
msg,
89+
#[inline(always)]
90+
|_, res| match res {
91+
Ok(c) => buf.push(c),
92+
Err(err) => {
93+
assert!(!err.is_fatal(), "failed to unescape string literal")
94+
}
95+
},
96+
);
97+
buf
98+
} else {
99+
msg.to_owned()
100+
};
101+
102+
cx.expr_str(
103+
sp,
104+
Symbol::intern(&format!("internal error: entered unreachable code: {msg}")),
105+
)
106+
}
107+
};
108+
109+
return ExpandResult::Ready(MacEager::expr(cx.expr_call(
110+
sp,
111+
cx.expr_path(cx.path_global(
112+
sp,
113+
[sym::core, sym::panicking, sym::panic].map(|sym| Ident::new(sym, sp)).to_vec(),
114+
)),
115+
[msg].into(),
116+
)));
117+
}
118+
47119
ExpandResult::Ready(MacEager::expr(
48120
cx.expr(
49121
sp,
50122
ExprKind::MacCall(Box::new(MacCall {
51123
path: Path {
52124
span: sp,
53125
segments: cx
54-
.std_path(&[sym::panic, mac])
126+
.std_path(&[sym::panic, mac.symbol()])
55127
.into_iter()
56128
.map(|ident| PathSegment::from_ident(ident))
57129
.collect(),

library/core/src/macros/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
#[doc = include_str!("panic.md")]
22
#[macro_export]
33
#[rustc_builtin_macro(core_panic)]
4-
#[allow_internal_unstable(edition_panic)]
4+
#[allow_internal_unstable(edition_panic, panic_internals)]
55
#[stable(feature = "core", since = "1.6.0")]
66
#[rustc_diagnostic_item = "core_panic_macro"]
77
macro_rules! panic {
88
// Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021`
9-
// depending on the edition of the caller.
9+
// depending on the edition of the caller. If the entire message is known at compile time,
10+
// `core::panicking::panic` may be called as an optimization.
1011
($($arg:tt)*) => {
1112
/* compiler built-in */
1213
};
@@ -705,7 +706,7 @@ macro_rules! writeln {
705706
/// ```
706707
#[macro_export]
707708
#[rustc_builtin_macro(unreachable)]
708-
#[allow_internal_unstable(edition_panic)]
709+
#[allow_internal_unstable(edition_panic, panic_internals)]
709710
#[stable(feature = "rust1", since = "1.0.0")]
710711
#[rustc_diagnostic_item = "unreachable_macro"]
711712
macro_rules! unreachable {

library/std/src/macros.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@
99
#[macro_export]
1010
#[rustc_builtin_macro(std_panic)]
1111
#[stable(feature = "rust1", since = "1.0.0")]
12-
#[allow_internal_unstable(edition_panic)]
12+
#[allow_internal_unstable(edition_panic, panic_internals)]
1313
#[cfg_attr(not(test), rustc_diagnostic_item = "std_panic_macro")]
1414
macro_rules! panic {
1515
// Expands to either `$crate::panic::panic_2015` or `$crate::panic::panic_2021`
16-
// depending on the edition of the caller.
16+
// depending on the edition of the caller. If the entire message is known at compile time,
17+
// `core::panicking::panic` may be called as an optimization.
1718
($($arg:tt)*) => {
1819
/* compiler built-in */
1920
};

src/tools/miri/tests/panic/panic1.stderr

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ panicking from libstd
44
stack backtrace:
55
0: std::panicking::panic_handler
66
at RUSTLIB/std/src/panicking.rs:LL:CC
7-
1: std::rt::panic_fmt
7+
1: core::panicking::panic_fmt
88
at RUSTLIB/core/src/panicking.rs:LL:CC
9-
2: main
9+
2: core::panicking::panic
10+
at RUSTLIB/core/src/panicking.rs:LL:CC
11+
3: main
1012
at tests/panic/panic1.rs:LL:CC
11-
3: <fn() as std::ops::FnOnce<()>>::call_once - shim(fn())
13+
4: <fn() as std::ops::FnOnce<()>>::call_once - shim(fn())
1214
at RUSTLIB/core/src/ops/function.rs:LL:CC
1315
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

tests/ui/consts/const-eval/const_panic-normalize-tabs-115498.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
struct Bug([u8; panic!{"\t"}]);
44
//~^ ERROR evaluation panicked
55
//~| NOTE: in this expansion of panic!
6+
//~| NOTE: failed here

tests/ui/panics/short-ice-remove-middle-frames-2.run.stderr

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
thread 'main' ($TID) panicked at $DIR/short-ice-remove-middle-frames-2.rs:62:5:
33
debug!!!
44
stack backtrace:
5-
0: std::panicking::begin_panic
6-
1: short_ice_remove_middle_frames_2::eight
7-
2: short_ice_remove_middle_frames_2::seven::{{closure}}
5+
0: __rustc::rust_begin_unwind
6+
1: core::panicking::panic_fmt
7+
2: core::panicking::panic
8+
3: short_ice_remove_middle_frames_2::eight
9+
4: short_ice_remove_middle_frames_2::seven::{{closure}}
810
[... omitted 3 frames ...]
9-
3: short_ice_remove_middle_frames_2::fifth
10-
4: short_ice_remove_middle_frames_2::fourth::{{closure}}
11+
5: short_ice_remove_middle_frames_2::fifth
12+
6: short_ice_remove_middle_frames_2::fourth::{{closure}}
1113
[... omitted 4 frames ...]
12-
5: short_ice_remove_middle_frames_2::first
13-
6: short_ice_remove_middle_frames_2::main
14-
7: core::ops::function::FnOnce::call_once
14+
7: short_ice_remove_middle_frames_2::first
15+
8: short_ice_remove_middle_frames_2::main
16+
9: core::ops::function::FnOnce::call_once
1517
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

tests/ui/panics/short-ice-remove-middle-frames.run.stderr

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@
22
thread 'main' ($TID) panicked at $DIR/short-ice-remove-middle-frames.rs:58:5:
33
debug!!!
44
stack backtrace:
5-
0: std::panicking::begin_panic
6-
1: short_ice_remove_middle_frames::seven
7-
2: short_ice_remove_middle_frames::sixth
8-
3: short_ice_remove_middle_frames::fifth::{{closure}}
5+
0: __rustc::rust_begin_unwind
6+
1: core::panicking::panic_fmt
7+
2: core::panicking::panic
8+
3: short_ice_remove_middle_frames::seven
9+
4: short_ice_remove_middle_frames::sixth
10+
5: short_ice_remove_middle_frames::fifth::{{closure}}
911
[... omitted 4 frames ...]
10-
4: short_ice_remove_middle_frames::second
11-
5: short_ice_remove_middle_frames::first::{{closure}}
12-
6: short_ice_remove_middle_frames::first
13-
7: short_ice_remove_middle_frames::main
14-
8: core::ops::function::FnOnce::call_once
12+
6: short_ice_remove_middle_frames::second
13+
7: short_ice_remove_middle_frames::first::{{closure}}
14+
8: short_ice_remove_middle_frames::first
15+
9: short_ice_remove_middle_frames::main
16+
10: core::ops::function::FnOnce::call_once
1517
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

0 commit comments

Comments
 (0)