]> git.lizzy.rs Git - rust.git/blob - library/core/src/panic.rs
Auto merge of #76157 - ArekPiekarz:const_caller_location_tracking_issue, r=joshtriplett
[rust.git] / library / core / src / panic.rs
1 //! Panic support in the standard library.
2
3 #![stable(feature = "core_panic_info", since = "1.41.0")]
4
5 use crate::any::Any;
6 use crate::fmt;
7
8 /// A struct providing information about a panic.
9 ///
10 /// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`]
11 /// function.
12 ///
13 /// [`set_hook`]: ../../std/panic/fn.set_hook.html
14 ///
15 /// # Examples
16 ///
17 /// ```should_panic
18 /// use std::panic;
19 ///
20 /// panic::set_hook(Box::new(|panic_info| {
21 ///     if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
22 ///         println!("panic occurred: {:?}", s);
23 ///     } else {
24 ///         println!("panic occurred");
25 ///     }
26 /// }));
27 ///
28 /// panic!("Normal panic");
29 /// ```
30 #[lang = "panic_info"]
31 #[stable(feature = "panic_hooks", since = "1.10.0")]
32 #[derive(Debug)]
33 pub struct PanicInfo<'a> {
34     payload: &'a (dyn Any + Send),
35     message: Option<&'a fmt::Arguments<'a>>,
36     location: &'a Location<'a>,
37 }
38
39 impl<'a> PanicInfo<'a> {
40     #[unstable(
41         feature = "panic_internals",
42         reason = "internal details of the implementation of the `panic!` and related macros",
43         issue = "none"
44     )]
45     #[doc(hidden)]
46     #[inline]
47     pub fn internal_constructor(
48         message: Option<&'a fmt::Arguments<'a>>,
49         location: &'a Location<'a>,
50     ) -> Self {
51         struct NoPayload;
52         PanicInfo { location, message, payload: &NoPayload }
53     }
54
55     #[unstable(
56         feature = "panic_internals",
57         reason = "internal details of the implementation of the `panic!` and related macros",
58         issue = "none"
59     )]
60     #[doc(hidden)]
61     #[inline]
62     pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) {
63         self.payload = info;
64     }
65
66     /// Returns the payload associated with the panic.
67     ///
68     /// This will commonly, but not always, be a `&'static str` or [`String`].
69     ///
70     /// [`String`]: ../../std/string/struct.String.html
71     ///
72     /// # Examples
73     ///
74     /// ```should_panic
75     /// use std::panic;
76     ///
77     /// panic::set_hook(Box::new(|panic_info| {
78     ///     if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
79     ///         println!("panic occurred: {:?}", s);
80     ///     } else {
81     ///         println!("panic occurred");
82     ///     }
83     /// }));
84     ///
85     /// panic!("Normal panic");
86     /// ```
87     #[stable(feature = "panic_hooks", since = "1.10.0")]
88     pub fn payload(&self) -> &(dyn Any + Send) {
89         self.payload
90     }
91
92     /// If the `panic!` macro from the `core` crate (not from `std`)
93     /// was used with a formatting string and some additional arguments,
94     /// returns that message ready to be used for example with [`fmt::write`]
95     #[unstable(feature = "panic_info_message", issue = "66745")]
96     pub fn message(&self) -> Option<&fmt::Arguments<'_>> {
97         self.message
98     }
99
100     /// Returns information about the location from which the panic originated,
101     /// if available.
102     ///
103     /// This method will currently always return [`Some`], but this may change
104     /// in future versions.
105     ///
106     /// # Examples
107     ///
108     /// ```should_panic
109     /// use std::panic;
110     ///
111     /// panic::set_hook(Box::new(|panic_info| {
112     ///     if let Some(location) = panic_info.location() {
113     ///         println!("panic occurred in file '{}' at line {}",
114     ///             location.file(),
115     ///             location.line(),
116     ///         );
117     ///     } else {
118     ///         println!("panic occurred but can't get location information...");
119     ///     }
120     /// }));
121     ///
122     /// panic!("Normal panic");
123     /// ```
124     #[stable(feature = "panic_hooks", since = "1.10.0")]
125     pub fn location(&self) -> Option<&Location<'_>> {
126         // NOTE: If this is changed to sometimes return None,
127         // deal with that case in std::panicking::default_hook and std::panicking::begin_panic_fmt.
128         Some(&self.location)
129     }
130 }
131
132 #[stable(feature = "panic_hook_display", since = "1.26.0")]
133 impl fmt::Display for PanicInfo<'_> {
134     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
135         formatter.write_str("panicked at ")?;
136         if let Some(message) = self.message {
137             write!(formatter, "'{}', ", message)?
138         } else if let Some(payload) = self.payload.downcast_ref::<&'static str>() {
139             write!(formatter, "'{}', ", payload)?
140         }
141         // NOTE: we cannot use downcast_ref::<String>() here
142         // since String is not available in libcore!
143         // The payload is a String when `std::panic!` is called with multiple arguments,
144         // but in that case the message is also available.
145
146         self.location.fmt(formatter)
147     }
148 }
149
150 /// A struct containing information about the location of a panic.
151 ///
152 /// This structure is created by [`PanicInfo::location()`].
153 ///
154 /// # Examples
155 ///
156 /// ```should_panic
157 /// use std::panic;
158 ///
159 /// panic::set_hook(Box::new(|panic_info| {
160 ///     if let Some(location) = panic_info.location() {
161 ///         println!("panic occurred in file '{}' at line {}", location.file(), location.line());
162 ///     } else {
163 ///         println!("panic occurred but can't get location information...");
164 ///     }
165 /// }));
166 ///
167 /// panic!("Normal panic");
168 /// ```
169 ///
170 /// # Comparisons
171 ///
172 /// Comparisons for equality and ordering are made in file, line, then column priority.
173 /// Files are compared as strings, not `Path`, which could be unexpected.
174 /// See [`Location::file`]'s documentation for more discussion.
175 #[lang = "panic_location"]
176 #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
177 #[stable(feature = "panic_hooks", since = "1.10.0")]
178 pub struct Location<'a> {
179     file: &'a str,
180     line: u32,
181     col: u32,
182 }
183
184 impl<'a> Location<'a> {
185     /// Returns the source location of the caller of this function. If that function's caller is
186     /// annotated then its call location will be returned, and so on up the stack to the first call
187     /// within a non-tracked function body.
188     ///
189     /// # Examples
190     ///
191     /// ```
192     /// use core::panic::Location;
193     ///
194     /// /// Returns the [`Location`] at which it is called.
195     /// #[track_caller]
196     /// fn get_caller_location() -> &'static Location<'static> {
197     ///     Location::caller()
198     /// }
199     ///
200     /// /// Returns a [`Location`] from within this function's definition.
201     /// fn get_just_one_location() -> &'static Location<'static> {
202     ///     get_caller_location()
203     /// }
204     ///
205     /// let fixed_location = get_just_one_location();
206     /// assert_eq!(fixed_location.file(), file!());
207     /// assert_eq!(fixed_location.line(), 14);
208     /// assert_eq!(fixed_location.column(), 5);
209     ///
210     /// // running the same untracked function in a different location gives us the same result
211     /// let second_fixed_location = get_just_one_location();
212     /// assert_eq!(fixed_location.file(), second_fixed_location.file());
213     /// assert_eq!(fixed_location.line(), second_fixed_location.line());
214     /// assert_eq!(fixed_location.column(), second_fixed_location.column());
215     ///
216     /// let this_location = get_caller_location();
217     /// assert_eq!(this_location.file(), file!());
218     /// assert_eq!(this_location.line(), 28);
219     /// assert_eq!(this_location.column(), 21);
220     ///
221     /// // running the tracked function in a different location produces a different value
222     /// let another_location = get_caller_location();
223     /// assert_eq!(this_location.file(), another_location.file());
224     /// assert_ne!(this_location.line(), another_location.line());
225     /// assert_ne!(this_location.column(), another_location.column());
226     /// ```
227     #[stable(feature = "track_caller", since = "1.46.0")]
228     #[rustc_const_unstable(feature = "const_caller_location", issue = "76156")]
229     #[track_caller]
230     pub const fn caller() -> &'static Location<'static> {
231         crate::intrinsics::caller_location()
232     }
233 }
234
235 impl<'a> Location<'a> {
236     #![unstable(
237         feature = "panic_internals",
238         reason = "internal details of the implementation of the `panic!` and related macros",
239         issue = "none"
240     )]
241     #[doc(hidden)]
242     pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self {
243         Location { file, line, col }
244     }
245
246     /// Returns the name of the source file from which the panic originated.
247     ///
248     /// # `&str`, not `&Path`
249     ///
250     /// The returned name refers to a source path on the compiling system, but it isn't valid to
251     /// represent this directly as a `&Path`. The compiled code may run on a different system with
252     /// a different `Path` implementation than the system providing the contents and this library
253     /// does not currently have a different "host path" type.
254     ///
255     /// The most surprising behavior occurs when "the same" file is reachable via multiple paths in
256     /// the module system (usually using the `#[path = "..."]` attribute or similar), which can
257     /// cause what appears to be identical code to return differing values from this function.
258     ///
259     /// # Cross-compilation
260     ///
261     /// This value is not suitable for passing to `Path::new` or similar constructors when the host
262     /// platform and target platform differ.
263     ///
264     /// # Examples
265     ///
266     /// ```should_panic
267     /// use std::panic;
268     ///
269     /// panic::set_hook(Box::new(|panic_info| {
270     ///     if let Some(location) = panic_info.location() {
271     ///         println!("panic occurred in file '{}'", location.file());
272     ///     } else {
273     ///         println!("panic occurred but can't get location information...");
274     ///     }
275     /// }));
276     ///
277     /// panic!("Normal panic");
278     /// ```
279     #[stable(feature = "panic_hooks", since = "1.10.0")]
280     pub fn file(&self) -> &str {
281         self.file
282     }
283
284     /// Returns the line number from which the panic originated.
285     ///
286     /// # Examples
287     ///
288     /// ```should_panic
289     /// use std::panic;
290     ///
291     /// panic::set_hook(Box::new(|panic_info| {
292     ///     if let Some(location) = panic_info.location() {
293     ///         println!("panic occurred at line {}", location.line());
294     ///     } else {
295     ///         println!("panic occurred but can't get location information...");
296     ///     }
297     /// }));
298     ///
299     /// panic!("Normal panic");
300     /// ```
301     #[stable(feature = "panic_hooks", since = "1.10.0")]
302     pub fn line(&self) -> u32 {
303         self.line
304     }
305
306     /// Returns the column from which the panic originated.
307     ///
308     /// # Examples
309     ///
310     /// ```should_panic
311     /// use std::panic;
312     ///
313     /// panic::set_hook(Box::new(|panic_info| {
314     ///     if let Some(location) = panic_info.location() {
315     ///         println!("panic occurred at column {}", location.column());
316     ///     } else {
317     ///         println!("panic occurred but can't get location information...");
318     ///     }
319     /// }));
320     ///
321     /// panic!("Normal panic");
322     /// ```
323     #[stable(feature = "panic_col", since = "1.25.0")]
324     pub fn column(&self) -> u32 {
325         self.col
326     }
327 }
328
329 #[stable(feature = "panic_hook_display", since = "1.26.0")]
330 impl fmt::Display for Location<'_> {
331     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
332         write!(formatter, "{}:{}:{}", self.file, self.line, self.col)
333     }
334 }
335
336 /// An internal trait used by libstd to pass data from libstd to `panic_unwind`
337 /// and other panic runtimes. Not intended to be stabilized any time soon, do
338 /// not use.
339 #[unstable(feature = "std_internals", issue = "none")]
340 #[doc(hidden)]
341 pub unsafe trait BoxMeUp {
342     /// Take full ownership of the contents.
343     /// The return type is actually `Box<dyn Any + Send>`, but we cannot use `Box` in libcore.
344     ///
345     /// After this method got called, only some dummy default value is left in `self`.
346     /// Calling this method twice, or calling `get` after calling this method, is an error.
347     ///
348     /// The argument is borrowed because the panic runtime (`__rust_start_panic`) only
349     /// gets a borrowed `dyn BoxMeUp`.
350     fn take_box(&mut self) -> *mut (dyn Any + Send);
351
352     /// Just borrow the contents.
353     fn get(&mut self) -> &(dyn Any + Send);
354 }