]> git.lizzy.rs Git - rust.git/blob - library/core/src/panicking.rs
Merge commit '35d9c6bf256968e1b40e0d554607928bdf9cebea' into sync_cg_clif-2022-02-23
[rust.git] / library / core / src / panicking.rs
1 //! Panic support for libcore
2 //!
3 //! The core library cannot define panicking, but it does *declare* panicking. This
4 //! means that the functions inside of libcore are allowed to panic, but to be
5 //! useful an upstream crate must define panicking for libcore 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 libcore 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 = "core_panic",
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 /// The underlying implementation of libcore's `panic!` macro when no formatting is used.
33 #[cold]
34 // never inline unless panic_immediate_abort to avoid code
35 // bloat at the call sites as much as possible
36 #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
37 #[cfg_attr(feature = "panic_immediate_abort", inline)]
38 #[track_caller]
39 #[rustc_const_unstable(feature = "core_panic", issue = "none")]
40 #[lang = "panic"] // needed by codegen for panic on overflow and other `Assert` MIR terminators
41 pub const fn panic(expr: &'static str) -> ! {
42     // Use Arguments::new_v1 instead of format_args!("{}", expr) to potentially
43     // reduce size overhead. The format_args! macro uses str's Display trait to
44     // write expr, which calls Formatter::pad, which must accommodate string
45     // truncation and padding (even though none is used here). Using
46     // Arguments::new_v1 may allow the compiler to omit Formatter::pad from the
47     // output binary, saving up to a few kilobytes.
48     panic_fmt(fmt::Arguments::new_v1(&[expr], &[]));
49 }
50
51 #[inline]
52 #[track_caller]
53 #[rustc_diagnostic_item = "panic_str"]
54 #[rustc_const_unstable(feature = "core_panic", issue = "none")]
55 pub const fn panic_str(expr: &str) -> ! {
56     panic_display(&expr);
57 }
58
59 #[cfg(not(bootstrap))]
60 #[inline]
61 #[track_caller]
62 #[rustc_diagnostic_item = "unreachable_display"] // needed for `non-fmt-panics` lint
63 pub fn unreachable_display<T: fmt::Display>(x: &T) -> ! {
64     panic_fmt(format_args!("internal error: entered unreachable code: {}", *x));
65 }
66
67 #[inline]
68 #[track_caller]
69 #[lang = "panic_display"] // needed for const-evaluated panics
70 #[rustc_do_not_const_check] // hooked by const-eval
71 #[rustc_const_unstable(feature = "core_panic", issue = "none")]
72 pub const fn panic_display<T: fmt::Display>(x: &T) -> ! {
73     panic_fmt(format_args!("{}", *x));
74 }
75
76 #[cold]
77 #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
78 #[track_caller]
79 #[lang = "panic_bounds_check"] // needed by codegen for panic on OOB array/slice access
80 fn panic_bounds_check(index: usize, len: usize) -> ! {
81     if cfg!(feature = "panic_immediate_abort") {
82         super::intrinsics::abort()
83     }
84
85     panic!("index out of bounds: the len is {} but the index is {}", len, index)
86 }
87
88 #[cfg(not(bootstrap))]
89 #[cold]
90 #[inline(never)]
91 #[lang = "panic_no_unwind"] // needed by codegen for panic in nounwind function
92 fn panic_no_unwind() -> ! {
93     if cfg!(feature = "panic_immediate_abort") {
94         super::intrinsics::abort()
95     }
96
97     // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
98     // that gets resolved to the `#[panic_handler]` function.
99     extern "Rust" {
100         #[lang = "panic_impl"]
101         fn panic_impl(pi: &PanicInfo<'_>) -> !;
102     }
103
104     // PanicInfo with the `can_unwind` flag set to false forces an abort.
105     let fmt = format_args!("panic in a function that cannot unwind");
106     let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), false);
107
108     // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
109     unsafe { panic_impl(&pi) }
110 }
111
112 /// The entry point for panicking with a formatted message.
113 ///
114 /// This is designed to reduce the amount of code required at the call
115 /// site as much as possible (so that `panic!()` has as low an impact
116 /// on (e.g.) the inlining of other functions as possible), by moving
117 /// the actual formatting into this shared place.
118 #[cold]
119 // If panic_immediate_abort, inline the abort call,
120 // otherwise avoid inlining because of it is cold path.
121 #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
122 #[cfg_attr(feature = "panic_immediate_abort", inline)]
123 #[track_caller]
124 #[lang = "panic_fmt"] // needed for const-evaluated panics
125 #[rustc_do_not_const_check] // hooked by const-eval
126 #[rustc_const_unstable(feature = "core_panic", issue = "none")]
127 pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
128     if cfg!(feature = "panic_immediate_abort") {
129         super::intrinsics::abort()
130     }
131
132     // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call
133     // that gets resolved to the `#[panic_handler]` function.
134     extern "Rust" {
135         #[lang = "panic_impl"]
136         fn panic_impl(pi: &PanicInfo<'_>) -> !;
137     }
138
139     let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), true);
140
141     // SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
142     unsafe { panic_impl(&pi) }
143 }
144
145 /// This function is used instead of panic_fmt in const eval.
146 #[lang = "const_panic_fmt"]
147 #[rustc_const_unstable(feature = "core_panic", issue = "none")]
148 pub const fn const_panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
149     if let Some(msg) = fmt.as_str() {
150         panic_str(msg);
151     } else {
152         // SAFETY: This is only evaluated at compile time, which reliably
153         // handles this UB (in case this branch turns out to be reachable
154         // somehow).
155         unsafe { crate::hint::unreachable_unchecked() };
156     }
157 }
158
159 #[derive(Debug)]
160 #[doc(hidden)]
161 pub enum AssertKind {
162     Eq,
163     Ne,
164     Match,
165 }
166
167 /// Internal function for `assert_eq!` and `assert_ne!` macros
168 #[cold]
169 #[track_caller]
170 #[doc(hidden)]
171 pub fn assert_failed<T, U>(
172     kind: AssertKind,
173     left: &T,
174     right: &U,
175     args: Option<fmt::Arguments<'_>>,
176 ) -> !
177 where
178     T: fmt::Debug + ?Sized,
179     U: fmt::Debug + ?Sized,
180 {
181     assert_failed_inner(kind, &left, &right, args)
182 }
183
184 /// Internal function for `assert_match!`
185 #[cold]
186 #[track_caller]
187 #[doc(hidden)]
188 pub fn assert_matches_failed<T: fmt::Debug + ?Sized>(
189     left: &T,
190     right: &str,
191     args: Option<fmt::Arguments<'_>>,
192 ) -> ! {
193     // Use the Display implementation to display the pattern.
194     struct Pattern<'a>(&'a str);
195     impl fmt::Debug for Pattern<'_> {
196         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197             fmt::Display::fmt(self.0, f)
198         }
199     }
200     assert_failed_inner(AssertKind::Match, &left, &Pattern(right), args);
201 }
202
203 /// Non-generic version of the above functions, to avoid code bloat.
204 #[track_caller]
205 fn assert_failed_inner(
206     kind: AssertKind,
207     left: &dyn fmt::Debug,
208     right: &dyn fmt::Debug,
209     args: Option<fmt::Arguments<'_>>,
210 ) -> ! {
211     let op = match kind {
212         AssertKind::Eq => "==",
213         AssertKind::Ne => "!=",
214         AssertKind::Match => "matches",
215     };
216
217     match args {
218         Some(args) => panic!(
219             r#"assertion failed: `(left {} right)`
220   left: `{:?}`,
221  right: `{:?}`: {}"#,
222             op, left, right, args
223         ),
224         None => panic!(
225             r#"assertion failed: `(left {} right)`
226   left: `{:?}`,
227  right: `{:?}`"#,
228             op, left, right,
229         ),
230     }
231 }