1 | //! Panic support for core |
2 | //! |
3 | //! The core library cannot define panicking, but it does *declare* panicking. This |
4 | //! means that the functions inside of core are allowed to panic, but to be |
5 | //! useful an upstream crate must define panicking for core to use. The current |
6 | //! interface for panicking is: |
7 | //! |
8 | //! ``` |
9 | //! fn panic_impl(pi: &core::panic::PanicInfo<'_>) -> ! |
10 | //! # { loop {} } |
11 | //! ``` |
12 | //! |
13 | //! This definition allows for panicking with any general message, but it does not |
14 | //! allow for failing with a `Box<Any>` value. (`PanicInfo` just contains a `&(dyn Any + Send)`, |
15 | //! for which we fill in a dummy value in `PanicInfo::internal_constructor`.) |
16 | //! The reason for this is that core is not allowed to allocate. |
17 | //! |
18 | //! This module contains a few other panicking functions, but these are just the |
19 | //! necessary lang items for the compiler. All panics are funneled through this |
20 | //! one function. The actual symbol is declared through the `#[panic_handler]` attribute. |
21 | |
22 | #![allow (dead_code, missing_docs)] |
23 | #![unstable ( |
24 | feature = "panic_internals" , |
25 | reason = "internal details of the implementation of the `panic!` and related macros" , |
26 | issue = "none" |
27 | )] |
28 | |
29 | use crate::fmt; |
30 | use crate::panic::{Location, PanicInfo}; |
31 | |
32 | #[cfg (feature = "panic_immediate_abort" )] |
33 | const _: () = assert!(cfg!(panic = "abort" ), "panic_immediate_abort requires -C panic=abort" ); |
34 | |
35 | // First we define the two main entry points that all panics go through. |
36 | // In the end both are just convenience wrappers around `panic_impl`. |
37 | |
38 | /// The entry point for panicking with a formatted message. |
39 | /// |
40 | /// This is designed to reduce the amount of code required at the call |
41 | /// site as much as possible (so that `panic!()` has as low an impact |
42 | /// on (e.g.) the inlining of other functions as possible), by moving |
43 | /// the actual formatting into this shared place. |
44 | // If panic_immediate_abort, inline the abort call, |
45 | // otherwise avoid inlining because of it is cold path. |
46 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
47 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
48 | #[track_caller ] |
49 | #[lang = "panic_fmt" ] // needed for const-evaluated panics |
50 | #[rustc_do_not_const_check ] // hooked by const-eval |
51 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
52 | pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! { |
53 | if cfg!(feature = "panic_immediate_abort" ) { |
54 | super::intrinsics::abort() |
55 | } |
56 | |
57 | // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call |
58 | // that gets resolved to the `#[panic_handler]` function. |
59 | extern "Rust" { |
60 | #[lang = "panic_impl" ] |
61 | fn panic_impl(pi: &PanicInfo<'_>) -> !; |
62 | } |
63 | |
64 | let pi: PanicInfo<'_> = PanicInfo::internal_constructor( |
65 | message:Some(&fmt), |
66 | Location::caller(), |
67 | /* can_unwind */ can_unwind:true, |
68 | /* force_no_backtrace */ force_no_backtrace:false, |
69 | ); |
70 | |
71 | // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. |
72 | unsafe { panic_impl(&pi) } |
73 | } |
74 | |
75 | /// Like `panic_fmt`, but for non-unwinding panics. |
76 | /// |
77 | /// Has to be a separate function so that it can carry the `rustc_nounwind` attribute. |
78 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
79 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
80 | #[track_caller ] |
81 | // This attribute has the key side-effect that if the panic handler ignores `can_unwind` |
82 | // and unwinds anyway, we will hit the "unwinding out of nounwind function" guard, |
83 | // which causes a "panic in a function that cannot unwind". |
84 | #[rustc_nounwind ] |
85 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
86 | pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! { |
87 | #[inline ] // this should always be inlined into `panic_nounwind_fmt` |
88 | #[track_caller ] |
89 | fn runtime(fmt: fmt::Arguments<'_>, force_no_backtrace: bool) -> ! { |
90 | if cfg!(feature = "panic_immediate_abort" ) { |
91 | super::intrinsics::abort() |
92 | } |
93 | |
94 | // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call |
95 | // that gets resolved to the `#[panic_handler]` function. |
96 | extern "Rust" { |
97 | #[lang = "panic_impl" ] |
98 | fn panic_impl(pi: &PanicInfo<'_>) -> !; |
99 | } |
100 | |
101 | // PanicInfo with the `can_unwind` flag set to false forces an abort. |
102 | let pi = PanicInfo::internal_constructor( |
103 | Some(&fmt), |
104 | Location::caller(), |
105 | /* can_unwind */ false, |
106 | force_no_backtrace, |
107 | ); |
108 | |
109 | // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call. |
110 | unsafe { panic_impl(&pi) } |
111 | } |
112 | |
113 | #[inline ] |
114 | #[track_caller ] |
115 | const fn comptime(fmt: fmt::Arguments<'_>, _force_no_backtrace: bool) -> ! { |
116 | // We don't unwind anyway at compile-time so we can call the regular `panic_fmt`. |
117 | panic_fmt(fmt); |
118 | } |
119 | |
120 | super::intrinsics::const_eval_select((fmt, force_no_backtrace), comptime, runtime); |
121 | } |
122 | |
123 | // Next we define a bunch of higher-level wrappers that all bottom out in the two core functions |
124 | // above. |
125 | |
126 | /// The underlying implementation of core's `panic!` macro when no formatting is used. |
127 | // Never inline unless panic_immediate_abort to avoid code |
128 | // bloat at the call sites as much as possible. |
129 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
130 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
131 | #[track_caller ] |
132 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
133 | #[lang = "panic" ] // used by lints and miri for panics |
134 | pub const fn panic(expr: &'static str) -> ! { |
135 | // Use Arguments::new_const instead of format_args!("{expr}") to potentially |
136 | // reduce size overhead. The format_args! macro uses str's Display trait to |
137 | // write expr, which calls Formatter::pad, which must accommodate string |
138 | // truncation and padding (even though none is used here). Using |
139 | // Arguments::new_const may allow the compiler to omit Formatter::pad from the |
140 | // output binary, saving up to a few kilobytes. |
141 | // However, this optimization only works for `'static` strings: `new_const` also makes this |
142 | // message return `Some` from `Arguments::as_str`, which means it can become part of the panic |
143 | // payload without any allocation or copying. Shorter-lived strings would become invalid as |
144 | // stack frames get popped during unwinding, and couldn't be directly referenced from the |
145 | // payload. |
146 | panic_fmt(fmt::Arguments::new_const(&[expr])); |
147 | } |
148 | |
149 | // We generate functions for usage by compiler-generated assertions. |
150 | // |
151 | // Placing these functions in libcore means that all Rust programs can generate a jump into this |
152 | // code rather than expanding to panic("...") above, which adds extra bloat to call sites (for the |
153 | // constant string argument's pointer and length). |
154 | // |
155 | // This is especially important when this code is called often (e.g., with -Coverflow-checks) for |
156 | // reducing binary size impact. |
157 | macro_rules! panic_const { |
158 | ($($lang:ident = $message:expr,)+) => { |
159 | #[cfg(not(bootstrap))] |
160 | pub mod panic_const { |
161 | use super::*; |
162 | |
163 | $( |
164 | /// This is a panic called with a message that's a result of a MIR-produced Assert. |
165 | // |
166 | // never inline unless panic_immediate_abort to avoid code |
167 | // bloat at the call sites as much as possible |
168 | #[cfg_attr(not(feature = "panic_immediate_abort" ), inline(never), cold)] |
169 | #[cfg_attr(feature = "panic_immediate_abort" , inline)] |
170 | #[track_caller] |
171 | #[rustc_const_unstable(feature = "panic_internals" , issue = "none" )] |
172 | #[lang = stringify!($lang)] |
173 | pub const fn $lang() -> ! { |
174 | // Use Arguments::new_const instead of format_args!("{expr}") to potentially |
175 | // reduce size overhead. The format_args! macro uses str's Display trait to |
176 | // write expr, which calls Formatter::pad, which must accommodate string |
177 | // truncation and padding (even though none is used here). Using |
178 | // Arguments::new_const may allow the compiler to omit Formatter::pad from the |
179 | // output binary, saving up to a few kilobytes. |
180 | panic_fmt(fmt::Arguments::new_const(&[$message])); |
181 | } |
182 | )+ |
183 | } |
184 | } |
185 | } |
186 | |
187 | // Unfortunately this set of strings is replicated here and in a few places in the compiler in |
188 | // slightly different forms. It's not clear if there's a good way to deduplicate without adding |
189 | // special cases to the compiler (e.g., a const generic function wouldn't have a single definition |
190 | // shared across crates, which is exactly what we want here). |
191 | panic_const! { |
192 | panic_const_add_overflow = "attempt to add with overflow" , |
193 | panic_const_sub_overflow = "attempt to subtract with overflow" , |
194 | panic_const_mul_overflow = "attempt to multiply with overflow" , |
195 | panic_const_div_overflow = "attempt to divide with overflow" , |
196 | panic_const_rem_overflow = "attempt to calculate the remainder with overflow" , |
197 | panic_const_neg_overflow = "attempt to negate with overflow" , |
198 | panic_const_shr_overflow = "attempt to shift right with overflow" , |
199 | panic_const_shl_overflow = "attempt to shift left with overflow" , |
200 | panic_const_div_by_zero = "attempt to divide by zero" , |
201 | panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero" , |
202 | panic_const_coroutine_resumed = "coroutine resumed after completion" , |
203 | panic_const_async_fn_resumed = "`async fn` resumed after completion" , |
204 | panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion" , |
205 | panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion" , |
206 | panic_const_coroutine_resumed_panic = "coroutine resumed after panicking" , |
207 | panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking" , |
208 | panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking" , |
209 | panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking" , |
210 | } |
211 | |
212 | /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller. |
213 | /// If you want `#[track_caller]` for nicer errors, call `panic_nounwind_fmt` directly. |
214 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
215 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
216 | #[lang = "panic_nounwind" ] // needed by codegen for non-unwinding panics |
217 | #[rustc_nounwind ] |
218 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
219 | pub const fn panic_nounwind(expr: &'static str) -> ! { |
220 | panic_nounwind_fmt(fmt:fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ force_no_backtrace:false); |
221 | } |
222 | |
223 | /// Like `panic_nounwind`, but also inhibits showing a backtrace. |
224 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
225 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
226 | #[rustc_nounwind ] |
227 | pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { |
228 | panic_nounwind_fmt(fmt:fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ force_no_backtrace:true); |
229 | } |
230 | |
231 | #[track_caller ] |
232 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
233 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
234 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
235 | pub const fn panic_explicit() -> ! { |
236 | panic_display(&"explicit panic" ); |
237 | } |
238 | |
239 | #[inline ] |
240 | #[track_caller ] |
241 | #[rustc_diagnostic_item = "unreachable_display" ] // needed for `non-fmt-panics` lint |
242 | pub fn unreachable_display<T: fmt::Display>(x: &T) -> ! { |
243 | panic_fmt(format_args!("internal error: entered unreachable code: {}" , *x)); |
244 | } |
245 | |
246 | /// This exists solely for the 2015 edition `panic!` macro to trigger |
247 | /// a lint on `panic!(my_str_variable);`. |
248 | #[inline ] |
249 | #[track_caller ] |
250 | #[rustc_diagnostic_item = "panic_str_2015" ] |
251 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
252 | pub const fn panic_str_2015(expr: &str) -> ! { |
253 | panic_display(&expr); |
254 | } |
255 | |
256 | #[inline ] |
257 | #[track_caller ] |
258 | #[rustc_do_not_const_check ] // hooked by const-eval |
259 | // enforce a &&str argument in const-check and hook this by const-eval |
260 | #[rustc_const_panic_str ] |
261 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
262 | pub const fn panic_display<T: fmt::Display>(x: &T) -> ! { |
263 | panic_fmt(format_args!(" {}" , *x)); |
264 | } |
265 | |
266 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
267 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
268 | #[track_caller ] |
269 | #[lang = "panic_bounds_check" ] // needed by codegen for panic on OOB array/slice access |
270 | fn panic_bounds_check(index: usize, len: usize) -> ! { |
271 | if cfg!(feature = "panic_immediate_abort" ) { |
272 | super::intrinsics::abort() |
273 | } |
274 | |
275 | panic!("index out of bounds: the len is {len} but the index is {index}" ) |
276 | } |
277 | |
278 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
279 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
280 | #[track_caller ] |
281 | #[lang = "panic_misaligned_pointer_dereference" ] // needed by codegen for panic on misaligned pointer deref |
282 | #[rustc_nounwind ] // `CheckAlignment` MIR pass requires this function to never unwind |
283 | fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { |
284 | if cfg!(feature = "panic_immediate_abort" ) { |
285 | super::intrinsics::abort() |
286 | } |
287 | |
288 | panic_nounwind_fmt( |
289 | fmt:format_args!( |
290 | "misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}" |
291 | ), |
292 | /* force_no_backtrace */ force_no_backtrace:false, |
293 | ) |
294 | } |
295 | |
296 | /// Panic because we cannot unwind out of a function. |
297 | /// |
298 | /// This is a separate function to avoid the codesize impact of each crate containing the string to |
299 | /// pass to `panic_nounwind`. |
300 | /// This function is called directly by the codegen backend, and must not have |
301 | /// any extra arguments (including those synthesized by track_caller). |
302 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
303 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
304 | #[lang = "panic_cannot_unwind" ] // needed by codegen for panic in nounwind function |
305 | #[rustc_nounwind ] |
306 | fn panic_cannot_unwind() -> ! { |
307 | // Keep the text in sync with `UnwindTerminateReason::as_str` in `rustc_middle`. |
308 | panic_nounwind(expr:"panic in a function that cannot unwind" ) |
309 | } |
310 | |
311 | /// Panic because we are unwinding out of a destructor during cleanup. |
312 | /// |
313 | /// This is a separate function to avoid the codesize impact of each crate containing the string to |
314 | /// pass to `panic_nounwind`. |
315 | /// This function is called directly by the codegen backend, and must not have |
316 | /// any extra arguments (including those synthesized by track_caller). |
317 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
318 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
319 | #[lang = "panic_in_cleanup" ] // needed by codegen for panic in nounwind function |
320 | #[rustc_nounwind ] |
321 | fn panic_in_cleanup() -> ! { |
322 | // Keep the text in sync with `UnwindTerminateReason::as_str` in `rustc_middle`. |
323 | panic_nounwind_nobacktrace(expr:"panic in a destructor during cleanup" ) |
324 | } |
325 | |
326 | /// This function is used instead of panic_fmt in const eval. |
327 | #[lang = "const_panic_fmt" ] |
328 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
329 | pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { |
330 | if let Some(msg: &str) = fmt.as_str() { |
331 | // The panic_display function is hooked by const eval. |
332 | panic_display(&msg); |
333 | } else { |
334 | // SAFETY: This is only evaluated at compile time, which reliably |
335 | // handles this UB (in case this branch turns out to be reachable |
336 | // somehow). |
337 | unsafe { crate::hint::unreachable_unchecked() }; |
338 | } |
339 | } |
340 | |
341 | #[derive (Debug)] |
342 | #[doc (hidden)] |
343 | pub enum AssertKind { |
344 | Eq, |
345 | Ne, |
346 | Match, |
347 | } |
348 | |
349 | /// Internal function for `assert_eq!` and `assert_ne!` macros |
350 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
351 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
352 | #[track_caller ] |
353 | #[doc (hidden)] |
354 | pub fn assert_failed<T, U>( |
355 | kind: AssertKind, |
356 | left: &T, |
357 | right: &U, |
358 | args: Option<fmt::Arguments<'_>>, |
359 | ) -> ! |
360 | where |
361 | T: fmt::Debug + ?Sized, |
362 | U: fmt::Debug + ?Sized, |
363 | { |
364 | assert_failed_inner(kind, &left, &right, args) |
365 | } |
366 | |
367 | /// Internal function for `assert_match!` |
368 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
369 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
370 | #[track_caller ] |
371 | #[doc (hidden)] |
372 | pub fn assert_matches_failed<T: fmt::Debug + ?Sized>( |
373 | left: &T, |
374 | right: &str, |
375 | args: Option<fmt::Arguments<'_>>, |
376 | ) -> ! { |
377 | // The pattern is a string so it can be displayed directly. |
378 | struct Pattern<'a>(&'a str); |
379 | impl fmt::Debug for Pattern<'_> { |
380 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
381 | f.write_str(self.0) |
382 | } |
383 | } |
384 | assert_failed_inner(kind:AssertKind::Match, &left, &Pattern(right), args); |
385 | } |
386 | |
387 | /// Non-generic version of the above functions, to avoid code bloat. |
388 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
389 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
390 | #[track_caller ] |
391 | fn assert_failed_inner( |
392 | kind: AssertKind, |
393 | left: &dyn fmt::Debug, |
394 | right: &dyn fmt::Debug, |
395 | args: Option<fmt::Arguments<'_>>, |
396 | ) -> ! { |
397 | let op: &str = match kind { |
398 | AssertKind::Eq => "==" , |
399 | AssertKind::Ne => "!=" , |
400 | AssertKind::Match => "matches" , |
401 | }; |
402 | |
403 | match args { |
404 | Some(args: Arguments<'_>) => panic!( |
405 | r#"assertion `left {op} right` failed: {args} |
406 | left: {left:?} |
407 | right: {right:?}"# |
408 | ), |
409 | None => panic!( |
410 | r#"assertion `left {op} right` failed |
411 | left: {left:?} |
412 | right: {right:?}"# |
413 | ), |
414 | } |
415 | } |
416 | |