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 } ;
33use rustc_ast:: * ;
44use rustc_expand:: base:: * ;
55use 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.
3052pub ( 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
3962fn 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 ( ) ,
0 commit comments