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 | pub mod panic_const { |
159 | use super::*; |
160 | |
161 | $( |
162 | /// This is a panic called with a message that's a result of a MIR-produced Assert. |
163 | // |
164 | // never inline unless panic_immediate_abort to avoid code |
165 | // bloat at the call sites as much as possible |
166 | #[cfg_attr(not(feature = "panic_immediate_abort" ), inline(never), cold)] |
167 | #[cfg_attr(feature = "panic_immediate_abort" , inline)] |
168 | #[track_caller] |
169 | #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable |
170 | #[lang = stringify!($lang)] |
171 | pub const fn $lang() -> ! { |
172 | // Use Arguments::new_const instead of format_args!("{expr}") to potentially |
173 | // reduce size overhead. The format_args! macro uses str's Display trait to |
174 | // write expr, which calls Formatter::pad, which must accommodate string |
175 | // truncation and padding (even though none is used here). Using |
176 | // Arguments::new_const may allow the compiler to omit Formatter::pad from the |
177 | // output binary, saving up to a few kilobytes. |
178 | panic_fmt(fmt::Arguments::new_const(&[$message])); |
179 | } |
180 | )+ |
181 | } |
182 | } |
183 | } |
184 | |
185 | // Unfortunately this set of strings is replicated here and in a few places in the compiler in |
186 | // slightly different forms. It's not clear if there's a good way to deduplicate without adding |
187 | // special cases to the compiler (e.g., a const generic function wouldn't have a single definition |
188 | // shared across crates, which is exactly what we want here). |
189 | panic_const! { |
190 | panic_const_add_overflow = "attempt to add with overflow" , |
191 | panic_const_sub_overflow = "attempt to subtract with overflow" , |
192 | panic_const_mul_overflow = "attempt to multiply with overflow" , |
193 | panic_const_div_overflow = "attempt to divide with overflow" , |
194 | panic_const_rem_overflow = "attempt to calculate the remainder with overflow" , |
195 | panic_const_neg_overflow = "attempt to negate with overflow" , |
196 | panic_const_shr_overflow = "attempt to shift right with overflow" , |
197 | panic_const_shl_overflow = "attempt to shift left with overflow" , |
198 | panic_const_div_by_zero = "attempt to divide by zero" , |
199 | panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero" , |
200 | panic_const_coroutine_resumed = "coroutine resumed after completion" , |
201 | panic_const_async_fn_resumed = "`async fn` resumed after completion" , |
202 | panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion" , |
203 | panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion" , |
204 | panic_const_coroutine_resumed_panic = "coroutine resumed after panicking" , |
205 | panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking" , |
206 | panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking" , |
207 | panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking" , |
208 | } |
209 | |
210 | /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller. |
211 | /// If you want `#[track_caller]` for nicer errors, call `panic_nounwind_fmt` directly. |
212 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
213 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
214 | #[lang = "panic_nounwind" ] // needed by codegen for non-unwinding panics |
215 | #[rustc_nounwind ] |
216 | #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable |
217 | pub const fn panic_nounwind(expr: &'static str) -> ! { |
218 | panic_nounwind_fmt(fmt:fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ force_no_backtrace:false); |
219 | } |
220 | |
221 | /// Like `panic_nounwind`, but also inhibits showing a backtrace. |
222 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
223 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
224 | #[rustc_nounwind ] |
225 | pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { |
226 | panic_nounwind_fmt(fmt:fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ force_no_backtrace:true); |
227 | } |
228 | |
229 | #[track_caller ] |
230 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
231 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
232 | #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable |
233 | pub const fn panic_explicit() -> ! { |
234 | panic_display(&"explicit panic" ); |
235 | } |
236 | |
237 | #[inline ] |
238 | #[track_caller ] |
239 | #[rustc_diagnostic_item = "unreachable_display" ] // needed for `non-fmt-panics` lint |
240 | pub fn unreachable_display<T: fmt::Display>(x: &T) -> ! { |
241 | panic_fmt(format_args!("internal error: entered unreachable code: {}" , *x)); |
242 | } |
243 | |
244 | /// This exists solely for the 2015 edition `panic!` macro to trigger |
245 | /// a lint on `panic!(my_str_variable);`. |
246 | #[inline ] |
247 | #[track_caller ] |
248 | #[rustc_diagnostic_item = "panic_str_2015" ] |
249 | #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable |
250 | pub const fn panic_str_2015(expr: &str) -> ! { |
251 | panic_display(&expr); |
252 | } |
253 | |
254 | #[inline ] |
255 | #[track_caller ] |
256 | #[rustc_do_not_const_check ] // hooked by const-eval |
257 | // enforce a &&str argument in const-check and hook this by const-eval |
258 | #[rustc_const_panic_str ] |
259 | #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable |
260 | pub const fn panic_display<T: fmt::Display>(x: &T) -> ! { |
261 | panic_fmt(format_args!(" {}" , *x)); |
262 | } |
263 | |
264 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold, optimize(size))] |
265 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
266 | #[track_caller ] |
267 | #[lang = "panic_bounds_check" ] // needed by codegen for panic on OOB array/slice access |
268 | fn panic_bounds_check(index: usize, len: usize) -> ! { |
269 | if cfg!(feature = "panic_immediate_abort" ) { |
270 | super::intrinsics::abort() |
271 | } |
272 | |
273 | panic!("index out of bounds: the len is {len} but the index is {index}" ) |
274 | } |
275 | |
276 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold, optimize(size))] |
277 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
278 | #[track_caller ] |
279 | #[lang = "panic_misaligned_pointer_dereference" ] // needed by codegen for panic on misaligned pointer deref |
280 | #[rustc_nounwind ] // `CheckAlignment` MIR pass requires this function to never unwind |
281 | fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { |
282 | if cfg!(feature = "panic_immediate_abort" ) { |
283 | super::intrinsics::abort() |
284 | } |
285 | |
286 | panic_nounwind_fmt( |
287 | fmt:format_args!( |
288 | "misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}" |
289 | ), |
290 | /* force_no_backtrace */ force_no_backtrace:false, |
291 | ) |
292 | } |
293 | |
294 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold, optimize(size))] |
295 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
296 | #[track_caller ] |
297 | #[lang = "panic_null_pointer_dereference" ] // needed by codegen for panic on null pointer deref |
298 | #[rustc_nounwind ] // `CheckNull` MIR pass requires this function to never unwind |
299 | fn panic_null_pointer_dereference() -> ! { |
300 | if cfg!(feature = "panic_immediate_abort" ) { |
301 | super::intrinsics::abort() |
302 | } |
303 | |
304 | panic_nounwind_fmt( |
305 | fmt:format_args!("null pointer dereference occurred" ), |
306 | /* force_no_backtrace */ force_no_backtrace:false, |
307 | ) |
308 | } |
309 | |
310 | /// Panics because we cannot unwind out of a function. |
311 | /// |
312 | /// This is a separate function to avoid the codesize impact of each crate containing the string to |
313 | /// pass to `panic_nounwind`. |
314 | /// |
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, optimize(size))] |
318 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
319 | #[lang = "panic_cannot_unwind" ] // needed by codegen for panic in nounwind function |
320 | #[rustc_nounwind ] |
321 | fn panic_cannot_unwind() -> ! { |
322 | // Keep the text in sync with `UnwindTerminateReason::as_str` in `rustc_middle`. |
323 | panic_nounwind(expr:"panic in a function that cannot unwind" ) |
324 | } |
325 | |
326 | /// Panics because we are unwinding out of a destructor during cleanup. |
327 | /// |
328 | /// This is a separate function to avoid the codesize impact of each crate containing the string to |
329 | /// pass to `panic_nounwind`. |
330 | /// |
331 | /// This function is called directly by the codegen backend, and must not have |
332 | /// any extra arguments (including those synthesized by track_caller). |
333 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold, optimize(size))] |
334 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
335 | #[lang = "panic_in_cleanup" ] // needed by codegen for panic in nounwind function |
336 | #[rustc_nounwind ] |
337 | fn panic_in_cleanup() -> ! { |
338 | // Keep the text in sync with `UnwindTerminateReason::as_str` in `rustc_middle`. |
339 | panic_nounwind_nobacktrace(expr:"panic in a destructor during cleanup" ) |
340 | } |
341 | |
342 | /// This function is used instead of panic_fmt in const eval. |
343 | #[lang = "const_panic_fmt" ] // needed by const-eval machine to replace calls to `panic_fmt` lang item |
344 | #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable |
345 | pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { |
346 | if let Some(msg: &'static str) = fmt.as_str() { |
347 | // The panic_display function is hooked by const eval. |
348 | panic_display(&msg); |
349 | } else { |
350 | // SAFETY: This is only evaluated at compile time, which reliably |
351 | // handles this UB (in case this branch turns out to be reachable |
352 | // somehow). |
353 | unsafe { crate::hint::unreachable_unchecked() }; |
354 | } |
355 | } |
356 | |
357 | #[derive (Debug)] |
358 | #[doc (hidden)] |
359 | pub enum AssertKind { |
360 | Eq, |
361 | Ne, |
362 | Match, |
363 | } |
364 | |
365 | /// Internal function for `assert_eq!` and `assert_ne!` macros |
366 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold, optimize(size))] |
367 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
368 | #[track_caller ] |
369 | #[doc (hidden)] |
370 | pub fn assert_failed<T, U>( |
371 | kind: AssertKind, |
372 | left: &T, |
373 | right: &U, |
374 | args: Option<fmt::Arguments<'_>>, |
375 | ) -> ! |
376 | where |
377 | T: fmt::Debug + ?Sized, |
378 | U: fmt::Debug + ?Sized, |
379 | { |
380 | assert_failed_inner(kind, &left, &right, args) |
381 | } |
382 | |
383 | /// Internal function for `assert_match!` |
384 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold, optimize(size))] |
385 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
386 | #[track_caller ] |
387 | #[doc (hidden)] |
388 | pub fn assert_matches_failed<T: fmt::Debug + ?Sized>( |
389 | left: &T, |
390 | right: &str, |
391 | args: Option<fmt::Arguments<'_>>, |
392 | ) -> ! { |
393 | // The pattern is a string so it can be displayed directly. |
394 | struct Pattern<'a>(&'a str); |
395 | impl fmt::Debug for Pattern<'_> { |
396 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
397 | f.write_str(self.0) |
398 | } |
399 | } |
400 | assert_failed_inner(kind:AssertKind::Match, &left, &Pattern(right), args); |
401 | } |
402 | |
403 | /// Non-generic version of the above functions, to avoid code bloat. |
404 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold, optimize(size))] |
405 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
406 | #[track_caller ] |
407 | fn assert_failed_inner( |
408 | kind: AssertKind, |
409 | left: &dyn fmt::Debug, |
410 | right: &dyn fmt::Debug, |
411 | args: Option<fmt::Arguments<'_>>, |
412 | ) -> ! { |
413 | let op: &'static str = match kind { |
414 | AssertKind::Eq => "==" , |
415 | AssertKind::Ne => "!=" , |
416 | AssertKind::Match => "matches" , |
417 | }; |
418 | |
419 | match args { |
420 | Some(args: Arguments<'_>) => panic!( |
421 | r#"assertion `left {op} right` failed: {args} |
422 | left: {left:?} |
423 | right: {right:?}"# |
424 | ), |
425 | None => panic!( |
426 | r#"assertion `left {op} right` failed |
427 | left: {left:?} |
428 | right: {right:?}"# |
429 | ), |
430 | } |
431 | } |
432 | |