Skip to content

Commit d1479e2

Browse files
Auto merge of #148375 - jhpratt:panic-str-lit, r=<try>
Optimize `panic!(<str lit>)` into method call
2 parents bd3ac03 + 16cafa7 commit d1479e2

File tree

10 files changed

+126
-38
lines changed

10 files changed

+126
-38
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: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,29 @@
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
@@ -19,7 +39,7 @@ pub(crate) fn expand_panic<'cx>(
1939
sp: Span,
2040
tts: TokenStream,
2141
) -> MacroExpanderResult<'cx> {
22-
let mac = if use_panic_2021(sp) { sym::panic_2021 } else { sym::panic_2015 };
42+
let mac = if use_panic_2021(sp) { InnerCall::Panic2021 } else { InnerCall::Panic2015 };
2343
expand(mac, cx, sp, tts)
2444
}
2545

@@ -32,26 +52,77 @@ pub(crate) fn expand_unreachable<'cx>(
3252
sp: Span,
3353
tts: TokenStream,
3454
) -> MacroExpanderResult<'cx> {
35-
let mac = if use_panic_2021(sp) { sym::unreachable_2021 } else { sym::unreachable_2015 };
55+
let mac =
56+
if use_panic_2021(sp) { InnerCall::Unreachable2021 } else { InnerCall::Unreachable2015 };
3657
expand(mac, cx, sp, tts)
3758
}
3859

3960
fn expand<'cx>(
40-
mac: rustc_span::Symbol,
61+
mac: InnerCall,
4162
cx: &'cx ExtCtxt<'_>,
4263
sp: Span,
4364
tts: TokenStream,
4465
) -> MacroExpanderResult<'cx> {
4566
let sp = cx.with_call_site_ctxt(sp);
4667

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

library/core/src/macros/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
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 {
@@ -705,7 +705,7 @@ macro_rules! writeln {
705705
/// ```
706706
#[macro_export]
707707
#[rustc_builtin_macro(unreachable)]
708-
#[allow_internal_unstable(edition_panic)]
708+
#[allow_internal_unstable(edition_panic, panic_internals)]
709709
#[stable(feature = "rust1", since = "1.0.0")]
710710
#[rustc_diagnostic_item = "unreachable_macro"]
711711
macro_rules! unreachable {

library/std/src/macros.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
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`

src/tools/clippy/clippy_lints/src/incompatible_msrv.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ pub struct IncompatibleMsrv {
8181
msrv: Msrv,
8282
availability_cache: FxHashMap<(DefId, bool), Availability>,
8383
check_in_tests: bool,
84-
core_crate: Option<CrateNum>,
84+
stdlib_crates: Vec<CrateNum>,
8585

8686
// The most recently called path. Used to skip checking the path after it's
8787
// been checked when visiting the call expression.
@@ -96,11 +96,15 @@ impl IncompatibleMsrv {
9696
msrv: conf.msrv,
9797
availability_cache: FxHashMap::default(),
9898
check_in_tests: conf.check_incompatible_msrv_in_tests,
99-
core_crate: tcx
99+
stdlib_crates: tcx
100100
.crates(())
101101
.iter()
102-
.find(|krate| tcx.crate_name(**krate) == sym::core)
103-
.copied(),
102+
.filter(|krate| {
103+
let name = tcx.crate_name(**krate);
104+
name == sym::core || name == sym::alloc || name == sym::std
105+
})
106+
.copied()
107+
.collect(),
104108
called_path: None,
105109
}
106110
}
@@ -162,10 +166,14 @@ impl IncompatibleMsrv {
162166
// Intentionally not using `.from_expansion()`, since we do still care about macro expansions
163167
return;
164168
}
165-
// Functions coming from `core` while expanding a macro such as `assert*!()` get to cheat too: the
166-
// macros may have existed prior to the checked MSRV, but their expansion with a recent compiler
167-
// might use recent functions or methods. Compiling with an older compiler would not use those.
168-
if Some(def_id.krate) == self.core_crate && expn_data.macro_def_id.map(|did| did.krate) == self.core_crate {
169+
// Functions coming from standard library crates while expanding a macro such as
170+
// `assert*!()` get to cheat too: the macros may have existed prior to the checked MSRV, but
171+
// their expansion with a recent compiler might use recent functions or methods. Compiling
172+
// with an older compiler would not use those.
173+
if self.stdlib_crates.contains(&def_id.krate)
174+
&& let Some(did) = expn_data.macro_def_id
175+
&& self.stdlib_crates.contains(&did.krate)
176+
{
169177
return;
170178
}
171179

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)