Skip to content

Commit 801226c

Browse files
committed
Auto merge of #122671 - Mark-Simulacrum:const-panic-msg, r=<try>
Codegen const panic messages as function calls This skips emitting extra arguments at every callsite (of which there can be many). We could handwrite these functions if the approach here proves fairly expensive at compile-time. r? `@Mark-Simulacrum` (for perf, for now)
2 parents 13abc0a + dfac628 commit 801226c

File tree

6 files changed

+148
-36
lines changed

6 files changed

+148
-36
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -651,10 +651,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
651651
(LangItem::PanicMisalignedPointerDereference, vec![required, found, location])
652652
}
653653
_ => {
654-
let msg = bx.const_str(msg.description());
655-
// It's `pub fn panic(expr: &str)`, with the wide reference being passed
656-
// as two arguments, and `#[track_caller]` adds an implicit third argument.
657-
(LangItem::Panic, vec![msg.0, msg.1, location])
654+
// It's `pub fn panic_...()` and `#[track_caller]` adds an implicit argument.
655+
(msg.description(), vec![location])
658656
}
659657
};
660658

compiler/rustc_hir/src/lang_items.rs

+19
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,25 @@ language_item_table! {
246246
PanicImpl, sym::panic_impl, panic_impl, Target::Fn, GenericRequirement::None;
247247
PanicCannotUnwind, sym::panic_cannot_unwind, panic_cannot_unwind, Target::Fn, GenericRequirement::Exact(0);
248248
PanicInCleanup, sym::panic_in_cleanup, panic_in_cleanup, Target::Fn, GenericRequirement::Exact(0);
249+
/// Constant panic messages, used for codegen of MIR asserts.
250+
PanicAddOverflow, sym::panic_const_add_overflow, panic_const_add_overflow, Target::Fn, GenericRequirement::None;
251+
PanicSubOverflow, sym::panic_const_sub_overflow, panic_const_sub_overflow, Target::Fn, GenericRequirement::None;
252+
PanicMulOverflow, sym::panic_const_mul_overflow, panic_const_mul_overflow, Target::Fn, GenericRequirement::None;
253+
PanicDivOverflow, sym::panic_const_div_overflow, panic_const_div_overflow, Target::Fn, GenericRequirement::None;
254+
PanicRemOverflow, sym::panic_const_rem_overflow, panic_const_rem_overflow, Target::Fn, GenericRequirement::None;
255+
PanicNegOverflow, sym::panic_const_neg_overflow, panic_const_neg_overflow, Target::Fn, GenericRequirement::None;
256+
PanicShrOverflow, sym::panic_const_shr_overflow, panic_const_shr_overflow, Target::Fn, GenericRequirement::None;
257+
PanicShlOverflow, sym::panic_const_shl_overflow, panic_const_shl_overflow, Target::Fn, GenericRequirement::None;
258+
PanicDivZero, sym::panic_const_div_by_zero, panic_const_div_by_zero, Target::Fn, GenericRequirement::None;
259+
PanicRemZero, sym::panic_const_rem_by_zero, panic_const_rem_by_zero, Target::Fn, GenericRequirement::None;
260+
PanicCoroutineResumed, sym::panic_const_coroutine_resumed, panic_const_coroutine_resumed, Target::Fn, GenericRequirement::None;
261+
PanicAsyncFnResumed, sym::panic_const_async_fn_resumed, panic_const_async_fn_resumed, Target::Fn, GenericRequirement::None;
262+
PanicAsyncGenFnResumed, sym::panic_const_async_gen_fn_resumed, panic_const_async_gen_fn_resumed, Target::Fn, GenericRequirement::None;
263+
PanicGenFnNone, sym::panic_const_gen_fn_none, panic_const_gen_fn_none, Target::Fn, GenericRequirement::None;
264+
PanicCoroutineResumedPanic, sym::panic_const_coroutine_resumed_panic, panic_const_coroutine_resumed_panic, Target::Fn, GenericRequirement::None;
265+
PanicAsyncFnResumedPanic, sym::panic_const_async_fn_resumed_panic, panic_const_async_fn_resumed_panic, Target::Fn, GenericRequirement::None;
266+
PanicAsyncGenFnResumedPanic, sym::panic_const_async_gen_fn_resumed_panic, panic_const_async_gen_fn_resumed_panic, Target::Fn, GenericRequirement::None;
267+
PanicGenFnNonePanic, sym::panic_const_gen_fn_none_panic, panic_const_gen_fn_none_panic, Target::Fn, GenericRequirement::None;
249268
/// libstd panic entry point. Necessary for const eval to be able to catch it
250269
BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None;
251270

compiler/rustc_middle/src/mir/terminator.rs

+47-22
Original file line numberDiff line numberDiff line change
@@ -149,44 +149,45 @@ impl<O> AssertKind<O> {
149149
matches!(self, OverflowNeg(..) | Overflow(Add | Sub | Mul | Shl | Shr, ..))
150150
}
151151

152-
/// Get the message that is printed at runtime when this assertion fails.
152+
/// Get the lang item that is invoked to print a static message when this assert fires.
153153
///
154154
/// The caller is expected to handle `BoundsCheck` and `MisalignedPointerDereference` by
155155
/// invoking the appropriate lang item (panic_bounds_check/panic_misaligned_pointer_dereference)
156-
/// instead of printing a static message.
157-
pub fn description(&self) -> &'static str {
156+
/// instead of printing a static message. Those have dynamic arguments that aren't present for
157+
/// the rest of the messages here.
158+
pub fn description(&self) -> LangItem {
158159
use AssertKind::*;
159160
match self {
160-
Overflow(BinOp::Add, _, _) => "attempt to add with overflow",
161-
Overflow(BinOp::Sub, _, _) => "attempt to subtract with overflow",
162-
Overflow(BinOp::Mul, _, _) => "attempt to multiply with overflow",
163-
Overflow(BinOp::Div, _, _) => "attempt to divide with overflow",
164-
Overflow(BinOp::Rem, _, _) => "attempt to calculate the remainder with overflow",
165-
OverflowNeg(_) => "attempt to negate with overflow",
166-
Overflow(BinOp::Shr, _, _) => "attempt to shift right with overflow",
167-
Overflow(BinOp::Shl, _, _) => "attempt to shift left with overflow",
161+
Overflow(BinOp::Add, _, _) => LangItem::PanicAddOverflow,
162+
Overflow(BinOp::Sub, _, _) => LangItem::PanicSubOverflow,
163+
Overflow(BinOp::Mul, _, _) => LangItem::PanicMulOverflow,
164+
Overflow(BinOp::Div, _, _) => LangItem::PanicDivOverflow,
165+
Overflow(BinOp::Rem, _, _) => LangItem::PanicRemOverflow,
166+
OverflowNeg(_) => LangItem::PanicNegOverflow,
167+
Overflow(BinOp::Shr, _, _) => LangItem::PanicShrOverflow,
168+
Overflow(BinOp::Shl, _, _) => LangItem::PanicShlOverflow,
168169
Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
169-
DivisionByZero(_) => "attempt to divide by zero",
170-
RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
171-
ResumedAfterReturn(CoroutineKind::Coroutine(_)) => "coroutine resumed after completion",
170+
DivisionByZero(_) => LangItem::PanicDivZero,
171+
RemainderByZero(_) => LangItem::PanicRemZero,
172+
ResumedAfterReturn(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumed,
172173
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
173-
"`async fn` resumed after completion"
174+
LangItem::PanicAsyncFnResumed
174175
}
175176
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
176-
"`async gen fn` resumed after completion"
177+
LangItem::PanicAsyncGenFnResumed
177178
}
178179
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
179-
"`gen fn` should just keep returning `None` after completion"
180+
LangItem::PanicGenFnNone
180181
}
181-
ResumedAfterPanic(CoroutineKind::Coroutine(_)) => "coroutine resumed after panicking",
182+
ResumedAfterPanic(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedPanic,
182183
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
183-
"`async fn` resumed after panicking"
184+
LangItem::PanicAsyncFnResumedPanic
184185
}
185186
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
186-
"`async gen fn` resumed after panicking"
187+
LangItem::PanicAsyncGenFnResumedPanic
187188
}
188189
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
189-
"`gen fn` should just keep returning `None` after panicking"
190+
LangItem::PanicGenFnNonePanic
190191
}
191192

192193
BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
@@ -246,13 +247,37 @@ impl<O> AssertKind<O> {
246247
Overflow(BinOp::Shl, _, r) => {
247248
write!(f, "\"attempt to shift left by `{{}}`, which would overflow\", {r:?}")
248249
}
250+
Overflow(op, _, _) => bug!("{:?} cannot overflow", op),
249251
MisalignedPointerDereference { required, found } => {
250252
write!(
251253
f,
252254
"\"misaligned pointer dereference: address must be a multiple of {{}} but is {{}}\", {required:?}, {found:?}"
253255
)
254256
}
255-
_ => write!(f, "\"{}\"", self.description()),
257+
ResumedAfterReturn(CoroutineKind::Coroutine(_)) => {
258+
write!(f, "\"coroutine resumed after completion\"")
259+
}
260+
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
261+
write!(f, "\"`async fn` resumed after completion\"")
262+
}
263+
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
264+
write!(f, "\"`async gen fn` resumed after completion\"")
265+
}
266+
ResumedAfterReturn(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
267+
write!(f, "\"`gen fn` should just keep returning `None` after completion\"")
268+
}
269+
ResumedAfterPanic(CoroutineKind::Coroutine(_)) => {
270+
write!(f, "\"coroutine resumed after panicking\"")
271+
}
272+
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => {
273+
write!(f, "\"`async fn` resumed after panicking\"")
274+
}
275+
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => {
276+
write!(f, "\"`async gen fn` resumed after panicking\"")
277+
}
278+
ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => {
279+
write!(f, "\"`gen fn` should just keep returning `None` after panicking\"")
280+
}
256281
}
257282
}
258283

compiler/rustc_monomorphize/src/collector.rs

+11-10
Original file line numberDiff line numberDiff line change
@@ -886,16 +886,17 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
886886
}
887887
}
888888
}
889-
mir::TerminatorKind::Assert { ref msg, .. } => {
890-
let lang_item = match &**msg {
891-
mir::AssertKind::BoundsCheck { .. } => LangItem::PanicBoundsCheck,
892-
mir::AssertKind::MisalignedPointerDereference { .. } => {
893-
LangItem::PanicMisalignedPointerDereference
894-
}
895-
_ => LangItem::Panic,
896-
};
897-
push_mono_lang_item(self, lang_item);
898-
}
889+
mir::TerminatorKind::Assert { ref msg, .. } => match &**msg {
890+
mir::AssertKind::BoundsCheck { .. } => {
891+
push_mono_lang_item(self, LangItem::PanicBoundsCheck);
892+
}
893+
mir::AssertKind::MisalignedPointerDereference { .. } => {
894+
push_mono_lang_item(self, LangItem::PanicMisalignedPointerDereference);
895+
}
896+
_ => {
897+
push_mono_lang_item(self, msg.description());
898+
}
899+
},
899900
mir::TerminatorKind::UnwindTerminate(reason) => {
900901
push_mono_lang_item(self, reason.lang_item());
901902
}

compiler/rustc_span/src/symbol.rs

+18
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,24 @@ symbols! {
12971297
panic_abort,
12981298
panic_bounds_check,
12991299
panic_cannot_unwind,
1300+
panic_const_add_overflow,
1301+
panic_const_async_fn_resumed,
1302+
panic_const_async_fn_resumed_panic,
1303+
panic_const_async_gen_fn_resumed,
1304+
panic_const_async_gen_fn_resumed_panic,
1305+
panic_const_coroutine_resumed,
1306+
panic_const_coroutine_resumed_panic,
1307+
panic_const_div_by_zero,
1308+
panic_const_div_overflow,
1309+
panic_const_gen_fn_none,
1310+
panic_const_gen_fn_none_panic,
1311+
panic_const_mul_overflow,
1312+
panic_const_neg_overflow,
1313+
panic_const_rem_by_zero,
1314+
panic_const_rem_overflow,
1315+
panic_const_shl_overflow,
1316+
panic_const_shr_overflow,
1317+
panic_const_sub_overflow,
13001318
panic_fmt,
13011319
panic_handler,
13021320
panic_impl,

library/core/src/panicking.rs

+51
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,57 @@ pub const fn panic(expr: &'static str) -> ! {
145145
panic_fmt(fmt::Arguments::new_const(&[expr]));
146146
}
147147

148+
macro_rules! panic_const {
149+
($($lang:ident = $message:expr,)+) => {
150+
#[cfg(not(bootstrap))]
151+
pub mod panic_const {
152+
use super::*;
153+
154+
$(
155+
/// This is a panic called with a message that's a result of a MIR-produced Assert.
156+
//
157+
// never inline unless panic_immediate_abort to avoid code
158+
// bloat at the call sites as much as possible
159+
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
160+
#[cfg_attr(feature = "panic_immediate_abort", inline)]
161+
#[track_caller]
162+
#[rustc_const_unstable(feature = "panic_internals", issue = "none")]
163+
#[lang = stringify!($lang)] // needed by codegen for panic on overflow and other `Assert` MIR terminators
164+
pub const fn $lang() -> ! {
165+
// Use Arguments::new_v1 instead of format_args!("{expr}") to potentially
166+
// reduce size overhead. The format_args! macro uses str's Display trait to
167+
// write expr, which calls Formatter::pad, which must accommodate string
168+
// truncation and padding (even though none is used here). Using
169+
// Arguments::new_v1 may allow the compiler to omit Formatter::pad from the
170+
// output binary, saving up to a few kilobytes.
171+
panic_fmt(fmt::Arguments::new_const(&[$message]));
172+
}
173+
)+
174+
}
175+
}
176+
}
177+
178+
panic_const! {
179+
panic_const_add_overflow = "attempt to add with overflow",
180+
panic_const_sub_overflow = "attempt to subtract with overflow",
181+
panic_const_mul_overflow = "attempt to multiply with overflow",
182+
panic_const_div_overflow = "attempt to divide with overflow",
183+
panic_const_rem_overflow = "attempt to calculate the remainder with overflow",
184+
panic_const_neg_overflow = "attempt to negate with overflow",
185+
panic_const_shr_overflow = "attempt to shift right with overflow",
186+
panic_const_shl_overflow = "attempt to shift left with overflow",
187+
panic_const_div_by_zero = "attempt to divide by zero",
188+
panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero",
189+
panic_const_coroutine_resumed = "coroutine resumed after completion",
190+
panic_const_async_fn_resumed = "`async fn` resumed after completion",
191+
panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion",
192+
panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion",
193+
panic_const_coroutine_resumed_panic = "coroutine resumed after panicking",
194+
panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking",
195+
panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking",
196+
panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking",
197+
}
198+
148199
/// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller.
149200
/// If you want `#[track_caller]` for nicer errors, call `panic_nounwind_fmt` directly.
150201
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]

0 commit comments

Comments
 (0)