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