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 | // SAFETY: const panic does not care about unwinding |
121 | unsafe { |
122 | super::intrinsics::const_eval_select((fmt, force_no_backtrace), comptime, runtime); |
123 | } |
124 | } |
125 | |
126 | // Next we define a bunch of higher-level wrappers that all bottom out in the two core functions |
127 | // above. |
128 | |
129 | /// The underlying implementation of core's `panic!` macro when no formatting is used. |
130 | // never inline unless panic_immediate_abort to avoid code |
131 | // bloat at the call sites as much as possible |
132 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
133 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
134 | #[track_caller ] |
135 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
136 | #[lang = "panic" ] // needed by codegen for panic on overflow and other `Assert` MIR terminators |
137 | pub const fn panic(expr: &'static str) -> ! { |
138 | // Use Arguments::new_v1 instead of format_args!("{expr}") to potentially |
139 | // reduce size overhead. The format_args! macro uses str's Display trait to |
140 | // write expr, which calls Formatter::pad, which must accommodate string |
141 | // truncation and padding (even though none is used here). Using |
142 | // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the |
143 | // output binary, saving up to a few kilobytes. |
144 | panic_fmt(fmt::Arguments::new_const(&[expr])); |
145 | } |
146 | |
147 | /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller. |
148 | /// If you want `#[track_caller]` for nicer errors, call `panic_nounwind_fmt` directly. |
149 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
150 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
151 | #[lang = "panic_nounwind" ] // needed by codegen for non-unwinding panics |
152 | #[rustc_nounwind ] |
153 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
154 | pub const fn panic_nounwind(expr: &'static str) -> ! { |
155 | panic_nounwind_fmt(fmt:fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ force_no_backtrace:false); |
156 | } |
157 | |
158 | /// Like `panic_nounwind`, but also inhibits showing a backtrace. |
159 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
160 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
161 | #[rustc_nounwind ] |
162 | pub fn panic_nounwind_nobacktrace(expr: &'static str) -> ! { |
163 | panic_nounwind_fmt(fmt:fmt::Arguments::new_const(&[expr]), /* force_no_backtrace */ force_no_backtrace:true); |
164 | } |
165 | |
166 | #[inline ] |
167 | #[track_caller ] |
168 | #[rustc_diagnostic_item = "panic_str" ] |
169 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
170 | pub const fn panic_str(expr: &str) -> ! { |
171 | panic_display(&expr); |
172 | } |
173 | |
174 | #[track_caller ] |
175 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
176 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
177 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
178 | pub const fn panic_explicit() -> ! { |
179 | panic_display(&"explicit panic" ); |
180 | } |
181 | |
182 | #[inline ] |
183 | #[track_caller ] |
184 | #[rustc_diagnostic_item = "unreachable_display" ] // needed for `non-fmt-panics` lint |
185 | pub fn unreachable_display<T: fmt::Display>(x: &T) -> ! { |
186 | panic_fmt(format_args!("internal error: entered unreachable code: {}" , *x)); |
187 | } |
188 | |
189 | #[inline ] |
190 | #[track_caller ] |
191 | #[rustc_do_not_const_check ] // hooked by const-eval |
192 | // enforce a &&str argument in const-check and hook this by const-eval |
193 | #[rustc_const_panic_str ] |
194 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
195 | pub const fn panic_display<T: fmt::Display>(x: &T) -> ! { |
196 | panic_fmt(format_args!(" {}" , *x)); |
197 | } |
198 | |
199 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
200 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
201 | #[track_caller ] |
202 | #[lang = "panic_bounds_check" ] // needed by codegen for panic on OOB array/slice access |
203 | fn panic_bounds_check(index: usize, len: usize) -> ! { |
204 | if cfg!(feature = "panic_immediate_abort" ) { |
205 | super::intrinsics::abort() |
206 | } |
207 | |
208 | panic!("index out of bounds: the len is {len} but the index is {index}" ) |
209 | } |
210 | |
211 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
212 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
213 | #[track_caller ] |
214 | #[lang = "panic_misaligned_pointer_dereference" ] // needed by codegen for panic on misaligned pointer deref |
215 | #[rustc_nounwind ] // `CheckAlignment` MIR pass requires this function to never unwind |
216 | fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! { |
217 | if cfg!(feature = "panic_immediate_abort" ) { |
218 | super::intrinsics::abort() |
219 | } |
220 | |
221 | panic_nounwind_fmt( |
222 | fmt:format_args!( |
223 | "misaligned pointer dereference: address must be a multiple of {required:#x} but is {found:#x}" |
224 | ), |
225 | /* force_no_backtrace */ force_no_backtrace:false, |
226 | ) |
227 | } |
228 | |
229 | /// Panic because we cannot unwind out of a function. |
230 | /// |
231 | /// This is a separate function to avoid the codesize impact of each crate containing the string to |
232 | /// pass to `panic_nounwind`. |
233 | /// This function is called directly by the codegen backend, and must not have |
234 | /// any extra arguments (including those synthesized by track_caller). |
235 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
236 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
237 | #[lang = "panic_cannot_unwind" ] // needed by codegen for panic in nounwind function |
238 | #[rustc_nounwind ] |
239 | fn panic_cannot_unwind() -> ! { |
240 | // Keep the text in sync with `UnwindTerminateReason::as_str` in `rustc_middle`. |
241 | panic_nounwind(expr:"panic in a function that cannot unwind" ) |
242 | } |
243 | |
244 | /// Panic because we are unwinding out of a destructor during cleanup. |
245 | /// |
246 | /// This is a separate function to avoid the codesize impact of each crate containing the string to |
247 | /// pass to `panic_nounwind`. |
248 | /// This function is called directly by the codegen backend, and must not have |
249 | /// any extra arguments (including those synthesized by track_caller). |
250 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
251 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
252 | #[lang = "panic_in_cleanup" ] // needed by codegen for panic in nounwind function |
253 | #[rustc_nounwind ] |
254 | fn panic_in_cleanup() -> ! { |
255 | // Keep the text in sync with `UnwindTerminateReason::as_str` in `rustc_middle`. |
256 | panic_nounwind_nobacktrace(expr:"panic in a destructor during cleanup" ) |
257 | } |
258 | |
259 | /// This function is used instead of panic_fmt in const eval. |
260 | #[lang = "const_panic_fmt" ] |
261 | #[rustc_const_unstable (feature = "panic_internals" , issue = "none" )] |
262 | pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! { |
263 | if let Some(msg: &str) = fmt.as_str() { |
264 | // The panic_display function is hooked by const eval. |
265 | panic_display(&msg); |
266 | } else { |
267 | // SAFETY: This is only evaluated at compile time, which reliably |
268 | // handles this UB (in case this branch turns out to be reachable |
269 | // somehow). |
270 | unsafe { crate::hint::unreachable_unchecked() }; |
271 | } |
272 | } |
273 | |
274 | #[derive (Debug)] |
275 | #[doc (hidden)] |
276 | pub enum AssertKind { |
277 | Eq, |
278 | Ne, |
279 | Match, |
280 | } |
281 | |
282 | /// Internal function for `assert_eq!` and `assert_ne!` macros |
283 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
284 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
285 | #[track_caller ] |
286 | #[doc (hidden)] |
287 | pub fn assert_failed<T, U>( |
288 | kind: AssertKind, |
289 | left: &T, |
290 | right: &U, |
291 | args: Option<fmt::Arguments<'_>>, |
292 | ) -> ! |
293 | where |
294 | T: fmt::Debug + ?Sized, |
295 | U: fmt::Debug + ?Sized, |
296 | { |
297 | assert_failed_inner(kind, &left, &right, args) |
298 | } |
299 | |
300 | /// Internal function for `assert_match!` |
301 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
302 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
303 | #[track_caller ] |
304 | #[doc (hidden)] |
305 | pub fn assert_matches_failed<T: fmt::Debug + ?Sized>( |
306 | left: &T, |
307 | right: &str, |
308 | args: Option<fmt::Arguments<'_>>, |
309 | ) -> ! { |
310 | // The pattern is a string so it can be displayed directly. |
311 | struct Pattern<'a>(&'a str); |
312 | impl fmt::Debug for Pattern<'_> { |
313 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
314 | f.write_str(self.0) |
315 | } |
316 | } |
317 | assert_failed_inner(kind:AssertKind::Match, &left, &Pattern(right), args); |
318 | } |
319 | |
320 | /// Non-generic version of the above functions, to avoid code bloat. |
321 | #[cfg_attr (not(feature = "panic_immediate_abort" ), inline(never), cold)] |
322 | #[cfg_attr (feature = "panic_immediate_abort" , inline)] |
323 | #[track_caller ] |
324 | fn assert_failed_inner( |
325 | kind: AssertKind, |
326 | left: &dyn fmt::Debug, |
327 | right: &dyn fmt::Debug, |
328 | args: Option<fmt::Arguments<'_>>, |
329 | ) -> ! { |
330 | let op: &str = match kind { |
331 | AssertKind::Eq => "==" , |
332 | AssertKind::Ne => "!=" , |
333 | AssertKind::Match => "matches" , |
334 | }; |
335 | |
336 | match args { |
337 | Some(args: Arguments<'_>) => panic!( |
338 | r#"assertion `left {op} right` failed: {args} |
339 | left: {left:?} |
340 | right: {right:?}"# |
341 | ), |
342 | None => panic!( |
343 | r#"assertion `left {op} right` failed |
344 | left: {left:?} |
345 | right: {right:?}"# |
346 | ), |
347 | } |
348 | } |
349 | |