Skip to content

Commit f381e77

Browse files
committedAug 1, 2021
Auto merge of #84662 - dtolnay:unwindsafe, r=Amanieu
Move UnwindSafe, RefUnwindSafe, AssertUnwindSafe to core They were previously only available in std::panic, not core::panic. - https://doc.rust-lang.org/1.51.0/std/panic/trait.UnwindSafe.html - https://doc.rust-lang.org/1.51.0/std/panic/trait.RefUnwindSafe.html - https://doc.rust-lang.org/1.51.0/std/panic/struct.AssertUnwindSafe.html Where this is relevant: trait objects! Inside a `#![no_std]` library it's otherwise impossible to have a struct holding a trait object, and at the same time can be used from downstream std crates in a way that doesn't interfere with catch_unwind. ```rust // common library #![no_std] pub struct Thing { pub(crate) x: &'static (dyn SomeTrait + Send + Sync), } pub(crate) trait SomeTrait {...} ``` ```rust // downstream application fn main() { let thing: library::Thing = ...; let _ = std::panic::catch_unwind(|| { let _ = thing; }); // does not work :( } ``` See https://github.com/dtolnay/colorous/blob/a4131708e2f05d2377964981896ff62dbc9b027b/src/gradient.rs#L7-L15 for a real life example of needing to work around this problem. In particular that workaround would not even be viable if implementors of the trait were provided externally by a caller, as the `feature = "std"` would become non-additive in that case. What happens without the UnwindSafe constraints: ```rust fn main() { let gradient = colorous::VIRIDIS; let _ = std::panic::catch_unwind(|| { let _ = gradient; }); } ``` ```console error[E0277]: the type `(dyn colorous::gradient::EvalGradient + Send + Sync + 'static)` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> src/main.rs:3:13 | 3 | let _ = std::panic::catch_unwind(|| { let _ = gradient; }); | ^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn colorous::gradient::EvalGradient + Send + Sync + 'static)` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | ::: .rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:430:40 | 430 | pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> { | ---------- required by this bound in `catch_unwind` | = help: within `Gradient`, the trait `RefUnwindSafe` is not implemented for `(dyn colorous::gradient::EvalGradient + Send + Sync + 'static)` = note: required because it appears within the type `&'static (dyn colorous::gradient::EvalGradient + Send + Sync + 'static)` = note: required because it appears within the type `Gradient` = note: required because of the requirements on the impl of `UnwindSafe` for `&Gradient` = note: required because it appears within the type `[closure@src/main.rs:3:38: 3:62]` ```
2 parents a0a6bab + d1586fc commit f381e77

File tree

7 files changed

+662
-638
lines changed

7 files changed

+662
-638
lines changed
 

‎library/alloc/src/rc.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ use core::marker::{self, PhantomData, Unpin, Unsize};
262262
use core::mem::size_of_val;
263263
use core::mem::{self, align_of_val_raw, forget};
264264
use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
265+
use core::panic::{RefUnwindSafe, UnwindSafe};
265266
#[cfg(not(no_global_oom_handling))]
266267
use core::pin::Pin;
267268
use core::ptr::{self, NonNull};
@@ -314,6 +315,9 @@ impl<T: ?Sized> !marker::Send for Rc<T> {}
314315
#[stable(feature = "rust1", since = "1.0.0")]
315316
impl<T: ?Sized> !marker::Sync for Rc<T> {}
316317

318+
#[stable(feature = "catch_unwind", since = "1.9.0")]
319+
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Rc<T> {}
320+
317321
#[unstable(feature = "coerce_unsized", issue = "27732")]
318322
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Rc<U>> for Rc<T> {}
319323

‎library/alloc/src/sync.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use core::marker::{PhantomData, Unpin, Unsize};
1919
use core::mem::size_of_val;
2020
use core::mem::{self, align_of_val_raw};
2121
use core::ops::{CoerceUnsized, Deref, DispatchFromDyn, Receiver};
22+
use core::panic::{RefUnwindSafe, UnwindSafe};
2223
use core::pin::Pin;
2324
use core::ptr::{self, NonNull};
2425
#[cfg(not(no_global_oom_handling))]
@@ -240,6 +241,9 @@ unsafe impl<T: ?Sized + Sync + Send> Send for Arc<T> {}
240241
#[stable(feature = "rust1", since = "1.0.0")]
241242
unsafe impl<T: ?Sized + Sync + Send> Sync for Arc<T> {}
242243

244+
#[stable(feature = "catch_unwind", since = "1.9.0")]
245+
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Arc<T> {}
246+
243247
#[unstable(feature = "coerce_unsized", issue = "27732")]
244248
impl<T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<Arc<U>> for Arc<T> {}
245249

‎library/core/src/panic.rs

Lines changed: 11 additions & 329 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,18 @@
22
33
#![stable(feature = "core_panic_info", since = "1.41.0")]
44

5+
mod location;
6+
mod panic_info;
7+
mod unwind_safe;
8+
59
use crate::any::Any;
6-
use crate::fmt;
10+
11+
#[stable(feature = "panic_hooks", since = "1.10.0")]
12+
pub use self::location::Location;
13+
#[stable(feature = "panic_hooks", since = "1.10.0")]
14+
pub use self::panic_info::PanicInfo;
15+
#[stable(feature = "catch_unwind", since = "1.9.0")]
16+
pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};
717

818
#[doc(hidden)]
919
#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]
@@ -39,334 +49,6 @@ pub macro panic_2021 {
3949
),
4050
}
4151

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

‎library/core/src/panic/location.rs

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
use crate::fmt;
2+
3+
/// A struct containing information about the location of a panic.
4+
///
5+
/// This structure is created by [`PanicInfo::location()`].
6+
///
7+
/// [`PanicInfo::location()`]: crate::panic::PanicInfo::location
8+
///
9+
/// # Examples
10+
///
11+
/// ```should_panic
12+
/// use std::panic;
13+
///
14+
/// panic::set_hook(Box::new(|panic_info| {
15+
/// if let Some(location) = panic_info.location() {
16+
/// println!("panic occurred in file '{}' at line {}", location.file(), location.line());
17+
/// } else {
18+
/// println!("panic occurred but can't get location information...");
19+
/// }
20+
/// }));
21+
///
22+
/// panic!("Normal panic");
23+
/// ```
24+
///
25+
/// # Comparisons
26+
///
27+
/// Comparisons for equality and ordering are made in file, line, then column priority.
28+
/// Files are compared as strings, not `Path`, which could be unexpected.
29+
/// See [`Location::file`]'s documentation for more discussion.
30+
#[lang = "panic_location"]
31+
#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
32+
#[stable(feature = "panic_hooks", since = "1.10.0")]
33+
pub struct Location<'a> {
34+
file: &'a str,
35+
line: u32,
36+
col: u32,
37+
}
38+
39+
impl<'a> Location<'a> {
40+
/// Returns the source location of the caller of this function. If that function's caller is
41+
/// annotated then its call location will be returned, and so on up the stack to the first call
42+
/// within a non-tracked function body.
43+
///
44+
/// # Examples
45+
///
46+
/// ```
47+
/// use std::panic::Location;
48+
///
49+
/// /// Returns the [`Location`] at which it is called.
50+
/// #[track_caller]
51+
/// fn get_caller_location() -> &'static Location<'static> {
52+
/// Location::caller()
53+
/// }
54+
///
55+
/// /// Returns a [`Location`] from within this function's definition.
56+
/// fn get_just_one_location() -> &'static Location<'static> {
57+
/// get_caller_location()
58+
/// }
59+
///
60+
/// let fixed_location = get_just_one_location();
61+
/// assert_eq!(fixed_location.file(), file!());
62+
/// assert_eq!(fixed_location.line(), 14);
63+
/// assert_eq!(fixed_location.column(), 5);
64+
///
65+
/// // running the same untracked function in a different location gives us the same result
66+
/// let second_fixed_location = get_just_one_location();
67+
/// assert_eq!(fixed_location.file(), second_fixed_location.file());
68+
/// assert_eq!(fixed_location.line(), second_fixed_location.line());
69+
/// assert_eq!(fixed_location.column(), second_fixed_location.column());
70+
///
71+
/// let this_location = get_caller_location();
72+
/// assert_eq!(this_location.file(), file!());
73+
/// assert_eq!(this_location.line(), 28);
74+
/// assert_eq!(this_location.column(), 21);
75+
///
76+
/// // running the tracked function in a different location produces a different value
77+
/// let another_location = get_caller_location();
78+
/// assert_eq!(this_location.file(), another_location.file());
79+
/// assert_ne!(this_location.line(), another_location.line());
80+
/// assert_ne!(this_location.column(), another_location.column());
81+
/// ```
82+
#[stable(feature = "track_caller", since = "1.46.0")]
83+
#[rustc_const_unstable(feature = "const_caller_location", issue = "76156")]
84+
#[track_caller]
85+
pub const fn caller() -> &'static Location<'static> {
86+
crate::intrinsics::caller_location()
87+
}
88+
89+
/// Returns the name of the source file from which the panic originated.
90+
///
91+
/// # `&str`, not `&Path`
92+
///
93+
/// The returned name refers to a source path on the compiling system, but it isn't valid to
94+
/// represent this directly as a `&Path`. The compiled code may run on a different system with
95+
/// a different `Path` implementation than the system providing the contents and this library
96+
/// does not currently have a different "host path" type.
97+
///
98+
/// The most surprising behavior occurs when "the same" file is reachable via multiple paths in
99+
/// the module system (usually using the `#[path = "..."]` attribute or similar), which can
100+
/// cause what appears to be identical code to return differing values from this function.
101+
///
102+
/// # Cross-compilation
103+
///
104+
/// This value is not suitable for passing to `Path::new` or similar constructors when the host
105+
/// platform and target platform differ.
106+
///
107+
/// # Examples
108+
///
109+
/// ```should_panic
110+
/// use std::panic;
111+
///
112+
/// panic::set_hook(Box::new(|panic_info| {
113+
/// if let Some(location) = panic_info.location() {
114+
/// println!("panic occurred in file '{}'", location.file());
115+
/// } else {
116+
/// println!("panic occurred but can't get location information...");
117+
/// }
118+
/// }));
119+
///
120+
/// panic!("Normal panic");
121+
/// ```
122+
#[stable(feature = "panic_hooks", since = "1.10.0")]
123+
pub fn file(&self) -> &str {
124+
self.file
125+
}
126+
127+
/// Returns the line number from which the panic originated.
128+
///
129+
/// # Examples
130+
///
131+
/// ```should_panic
132+
/// use std::panic;
133+
///
134+
/// panic::set_hook(Box::new(|panic_info| {
135+
/// if let Some(location) = panic_info.location() {
136+
/// println!("panic occurred at line {}", location.line());
137+
/// } else {
138+
/// println!("panic occurred but can't get location information...");
139+
/// }
140+
/// }));
141+
///
142+
/// panic!("Normal panic");
143+
/// ```
144+
#[stable(feature = "panic_hooks", since = "1.10.0")]
145+
pub fn line(&self) -> u32 {
146+
self.line
147+
}
148+
149+
/// Returns the column from which the panic originated.
150+
///
151+
/// # Examples
152+
///
153+
/// ```should_panic
154+
/// use std::panic;
155+
///
156+
/// panic::set_hook(Box::new(|panic_info| {
157+
/// if let Some(location) = panic_info.location() {
158+
/// println!("panic occurred at column {}", location.column());
159+
/// } else {
160+
/// println!("panic occurred but can't get location information...");
161+
/// }
162+
/// }));
163+
///
164+
/// panic!("Normal panic");
165+
/// ```
166+
#[stable(feature = "panic_col", since = "1.25.0")]
167+
pub fn column(&self) -> u32 {
168+
self.col
169+
}
170+
}
171+
172+
#[unstable(
173+
feature = "panic_internals",
174+
reason = "internal details of the implementation of the `panic!` and related macros",
175+
issue = "none"
176+
)]
177+
impl<'a> Location<'a> {
178+
#[doc(hidden)]
179+
pub const fn internal_constructor(file: &'a str, line: u32, col: u32) -> Self {
180+
Location { file, line, col }
181+
}
182+
}
183+
184+
#[stable(feature = "panic_hook_display", since = "1.26.0")]
185+
impl fmt::Display for Location<'_> {
186+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
187+
write!(formatter, "{}:{}:{}", self.file, self.line, self.col)
188+
}
189+
}

‎library/core/src/panic/panic_info.rs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
use crate::any::Any;
2+
use crate::fmt;
3+
use crate::panic::Location;
4+
5+
/// A struct providing information about a panic.
6+
///
7+
/// `PanicInfo` structure is passed to a panic hook set by the [`set_hook`]
8+
/// function.
9+
///
10+
/// [`set_hook`]: ../../std/panic/fn.set_hook.html
11+
///
12+
/// # Examples
13+
///
14+
/// ```should_panic
15+
/// use std::panic;
16+
///
17+
/// panic::set_hook(Box::new(|panic_info| {
18+
/// if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
19+
/// println!("panic occurred: {:?}", s);
20+
/// } else {
21+
/// println!("panic occurred");
22+
/// }
23+
/// }));
24+
///
25+
/// panic!("Normal panic");
26+
/// ```
27+
#[lang = "panic_info"]
28+
#[stable(feature = "panic_hooks", since = "1.10.0")]
29+
#[derive(Debug)]
30+
pub struct PanicInfo<'a> {
31+
payload: &'a (dyn Any + Send),
32+
message: Option<&'a fmt::Arguments<'a>>,
33+
location: &'a Location<'a>,
34+
}
35+
36+
impl<'a> PanicInfo<'a> {
37+
#[unstable(
38+
feature = "panic_internals",
39+
reason = "internal details of the implementation of the `panic!` and related macros",
40+
issue = "none"
41+
)]
42+
#[doc(hidden)]
43+
#[inline]
44+
pub fn internal_constructor(
45+
message: Option<&'a fmt::Arguments<'a>>,
46+
location: &'a Location<'a>,
47+
) -> Self {
48+
struct NoPayload;
49+
PanicInfo { location, message, payload: &NoPayload }
50+
}
51+
52+
#[unstable(
53+
feature = "panic_internals",
54+
reason = "internal details of the implementation of the `panic!` and related macros",
55+
issue = "none"
56+
)]
57+
#[doc(hidden)]
58+
#[inline]
59+
pub fn set_payload(&mut self, info: &'a (dyn Any + Send)) {
60+
self.payload = info;
61+
}
62+
63+
/// Returns the payload associated with the panic.
64+
///
65+
/// This will commonly, but not always, be a `&'static str` or [`String`].
66+
///
67+
/// [`String`]: ../../std/string/struct.String.html
68+
///
69+
/// # Examples
70+
///
71+
/// ```should_panic
72+
/// use std::panic;
73+
///
74+
/// panic::set_hook(Box::new(|panic_info| {
75+
/// if let Some(s) = panic_info.payload().downcast_ref::<&str>() {
76+
/// println!("panic occurred: {:?}", s);
77+
/// } else {
78+
/// println!("panic occurred");
79+
/// }
80+
/// }));
81+
///
82+
/// panic!("Normal panic");
83+
/// ```
84+
#[stable(feature = "panic_hooks", since = "1.10.0")]
85+
pub fn payload(&self) -> &(dyn Any + Send) {
86+
self.payload
87+
}
88+
89+
/// If the `panic!` macro from the `core` crate (not from `std`)
90+
/// was used with a formatting string and some additional arguments,
91+
/// returns that message ready to be used for example with [`fmt::write`]
92+
#[unstable(feature = "panic_info_message", issue = "66745")]
93+
pub fn message(&self) -> Option<&fmt::Arguments<'_>> {
94+
self.message
95+
}
96+
97+
/// Returns information about the location from which the panic originated,
98+
/// if available.
99+
///
100+
/// This method will currently always return [`Some`], but this may change
101+
/// in future versions.
102+
///
103+
/// # Examples
104+
///
105+
/// ```should_panic
106+
/// use std::panic;
107+
///
108+
/// panic::set_hook(Box::new(|panic_info| {
109+
/// if let Some(location) = panic_info.location() {
110+
/// println!("panic occurred in file '{}' at line {}",
111+
/// location.file(),
112+
/// location.line(),
113+
/// );
114+
/// } else {
115+
/// println!("panic occurred but can't get location information...");
116+
/// }
117+
/// }));
118+
///
119+
/// panic!("Normal panic");
120+
/// ```
121+
#[stable(feature = "panic_hooks", since = "1.10.0")]
122+
pub fn location(&self) -> Option<&Location<'_>> {
123+
// NOTE: If this is changed to sometimes return None,
124+
// deal with that case in std::panicking::default_hook and std::panicking::begin_panic_fmt.
125+
Some(&self.location)
126+
}
127+
}
128+
129+
#[stable(feature = "panic_hook_display", since = "1.26.0")]
130+
impl fmt::Display for PanicInfo<'_> {
131+
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
132+
formatter.write_str("panicked at ")?;
133+
if let Some(message) = self.message {
134+
write!(formatter, "'{}', ", message)?
135+
} else if let Some(payload) = self.payload.downcast_ref::<&'static str>() {
136+
write!(formatter, "'{}', ", payload)?
137+
}
138+
// NOTE: we cannot use downcast_ref::<String>() here
139+
// since String is not available in libcore!
140+
// The payload is a String when `std::panic!` is called with multiple arguments,
141+
// but in that case the message is also available.
142+
143+
self.location.fmt(formatter)
144+
}
145+
}

‎library/core/src/panic/unwind_safe.rs

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
use crate::cell::UnsafeCell;
2+
use crate::fmt;
3+
use crate::future::Future;
4+
use crate::ops::{Deref, DerefMut};
5+
use crate::pin::Pin;
6+
use crate::ptr::{NonNull, Unique};
7+
use crate::stream::Stream;
8+
use crate::task::{Context, Poll};
9+
10+
/// A marker trait which represents "panic safe" types in Rust.
11+
///
12+
/// This trait is implemented by default for many types and behaves similarly in
13+
/// terms of inference of implementation to the [`Send`] and [`Sync`] traits. The
14+
/// purpose of this trait is to encode what types are safe to cross a [`catch_unwind`]
15+
/// boundary with no fear of unwind safety.
16+
///
17+
/// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html
18+
///
19+
/// ## What is unwind safety?
20+
///
21+
/// In Rust a function can "return" early if it either panics or calls a
22+
/// function which transitively panics. This sort of control flow is not always
23+
/// anticipated, and has the possibility of causing subtle bugs through a
24+
/// combination of two critical components:
25+
///
26+
/// 1. A data structure is in a temporarily invalid state when the thread
27+
/// panics.
28+
/// 2. This broken invariant is then later observed.
29+
///
30+
/// Typically in Rust, it is difficult to perform step (2) because catching a
31+
/// panic involves either spawning a thread (which in turns makes it difficult
32+
/// to later witness broken invariants) or using the `catch_unwind` function in this
33+
/// module. Additionally, even if an invariant is witnessed, it typically isn't a
34+
/// problem in Rust because there are no uninitialized values (like in C or C++).
35+
///
36+
/// It is possible, however, for **logical** invariants to be broken in Rust,
37+
/// which can end up causing behavioral bugs. Another key aspect of unwind safety
38+
/// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to
39+
/// memory unsafety.
40+
///
41+
/// That was a bit of a whirlwind tour of unwind safety, but for more information
42+
/// about unwind safety and how it applies to Rust, see an [associated RFC][rfc].
43+
///
44+
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
45+
///
46+
/// ## What is `UnwindSafe`?
47+
///
48+
/// Now that we've got an idea of what unwind safety is in Rust, it's also
49+
/// important to understand what this trait represents. As mentioned above, one
50+
/// way to witness broken invariants is through the `catch_unwind` function in this
51+
/// module as it allows catching a panic and then re-using the environment of
52+
/// the closure.
53+
///
54+
/// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow
55+
/// witnessing a broken invariant through the use of `catch_unwind` (catching a
56+
/// panic). This trait is an auto trait, so it is automatically implemented for
57+
/// many types, and it is also structurally composed (e.g., a struct is unwind
58+
/// safe if all of its components are unwind safe).
59+
///
60+
/// Note, however, that this is not an unsafe trait, so there is not a succinct
61+
/// contract that this trait is providing. Instead it is intended as more of a
62+
/// "speed bump" to alert users of `catch_unwind` that broken invariants may be
63+
/// witnessed and may need to be accounted for.
64+
///
65+
/// ## Who implements `UnwindSafe`?
66+
///
67+
/// Types such as `&mut T` and `&RefCell<T>` are examples which are **not**
68+
/// unwind safe. The general idea is that any mutable state which can be shared
69+
/// across `catch_unwind` is not unwind safe by default. This is because it is very
70+
/// easy to witness a broken invariant outside of `catch_unwind` as the data is
71+
/// simply accessed as usual.
72+
///
73+
/// Types like `&Mutex<T>`, however, are unwind safe because they implement
74+
/// poisoning by default. They still allow witnessing a broken invariant, but
75+
/// they already provide their own "speed bumps" to do so.
76+
///
77+
/// ## When should `UnwindSafe` be used?
78+
///
79+
/// It is not intended that most types or functions need to worry about this trait.
80+
/// It is only used as a bound on the `catch_unwind` function and as mentioned
81+
/// above, the lack of `unsafe` means it is mostly an advisory. The
82+
/// [`AssertUnwindSafe`] wrapper struct can be used to force this trait to be
83+
/// implemented for any closed over variables passed to `catch_unwind`.
84+
#[stable(feature = "catch_unwind", since = "1.9.0")]
85+
#[cfg_attr(not(test), rustc_diagnostic_item = "unwind_safe_trait")]
86+
#[rustc_on_unimplemented(
87+
message = "the type `{Self}` may not be safely transferred across an unwind boundary",
88+
label = "`{Self}` may not be safely transferred across an unwind boundary"
89+
)]
90+
pub auto trait UnwindSafe {}
91+
92+
/// A marker trait representing types where a shared reference is considered
93+
/// unwind safe.
94+
///
95+
/// This trait is namely not implemented by [`UnsafeCell`], the root of all
96+
/// interior mutability.
97+
///
98+
/// This is a "helper marker trait" used to provide impl blocks for the
99+
/// [`UnwindSafe`] trait, for more information see that documentation.
100+
#[stable(feature = "catch_unwind", since = "1.9.0")]
101+
#[cfg_attr(not(test), rustc_diagnostic_item = "ref_unwind_safe_trait")]
102+
#[rustc_on_unimplemented(
103+
message = "the type `{Self}` may contain interior mutability and a reference may not be safely \
104+
transferrable across a catch_unwind boundary",
105+
label = "`{Self}` may contain interior mutability and a reference may not be safely \
106+
transferrable across a catch_unwind boundary"
107+
)]
108+
pub auto trait RefUnwindSafe {}
109+
110+
/// A simple wrapper around a type to assert that it is unwind safe.
111+
///
112+
/// When using [`catch_unwind`] it may be the case that some of the closed over
113+
/// variables are not unwind safe. For example if `&mut T` is captured the
114+
/// compiler will generate a warning indicating that it is not unwind safe. It
115+
/// might not be the case, however, that this is actually a problem due to the
116+
/// specific usage of [`catch_unwind`] if unwind safety is specifically taken into
117+
/// account. This wrapper struct is useful for a quick and lightweight
118+
/// annotation that a variable is indeed unwind safe.
119+
///
120+
/// [`catch_unwind`]: ../../std/panic/fn.catch_unwind.html
121+
///
122+
/// # Examples
123+
///
124+
/// One way to use `AssertUnwindSafe` is to assert that the entire closure
125+
/// itself is unwind safe, bypassing all checks for all variables:
126+
///
127+
/// ```
128+
/// use std::panic::{self, AssertUnwindSafe};
129+
///
130+
/// let mut variable = 4;
131+
///
132+
/// // This code will not compile because the closure captures `&mut variable`
133+
/// // which is not considered unwind safe by default.
134+
///
135+
/// // panic::catch_unwind(|| {
136+
/// // variable += 3;
137+
/// // });
138+
///
139+
/// // This, however, will compile due to the `AssertUnwindSafe` wrapper
140+
/// let result = panic::catch_unwind(AssertUnwindSafe(|| {
141+
/// variable += 3;
142+
/// }));
143+
/// // ...
144+
/// ```
145+
///
146+
/// Wrapping the entire closure amounts to a blanket assertion that all captured
147+
/// variables are unwind safe. This has the downside that if new captures are
148+
/// added in the future, they will also be considered unwind safe. Therefore,
149+
/// you may prefer to just wrap individual captures, as shown below. This is
150+
/// more annotation, but it ensures that if a new capture is added which is not
151+
/// unwind safe, you will get a compilation error at that time, which will
152+
/// allow you to consider whether that new capture in fact represent a bug or
153+
/// not.
154+
///
155+
/// ```
156+
/// use std::panic::{self, AssertUnwindSafe};
157+
///
158+
/// let mut variable = 4;
159+
/// let other_capture = 3;
160+
///
161+
/// let result = {
162+
/// let mut wrapper = AssertUnwindSafe(&mut variable);
163+
/// panic::catch_unwind(move || {
164+
/// **wrapper += other_capture;
165+
/// })
166+
/// };
167+
/// // ...
168+
/// ```
169+
#[stable(feature = "catch_unwind", since = "1.9.0")]
170+
pub struct AssertUnwindSafe<T>(#[stable(feature = "catch_unwind", since = "1.9.0")] pub T);
171+
172+
// Implementations of the `UnwindSafe` trait:
173+
//
174+
// * By default everything is unwind safe
175+
// * pointers T contains mutability of some form are not unwind safe
176+
// * Unique, an owning pointer, lifts an implementation
177+
// * Types like Mutex/RwLock which are explicitly poisoned are unwind safe
178+
// * Our custom AssertUnwindSafe wrapper is indeed unwind safe
179+
180+
#[stable(feature = "catch_unwind", since = "1.9.0")]
181+
impl<T: ?Sized> !UnwindSafe for &mut T {}
182+
#[stable(feature = "catch_unwind", since = "1.9.0")]
183+
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for &T {}
184+
#[stable(feature = "catch_unwind", since = "1.9.0")]
185+
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for *const T {}
186+
#[stable(feature = "catch_unwind", since = "1.9.0")]
187+
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for *mut T {}
188+
#[unstable(feature = "ptr_internals", issue = "none")]
189+
impl<T: UnwindSafe + ?Sized> UnwindSafe for Unique<T> {}
190+
#[stable(feature = "nonnull", since = "1.25.0")]
191+
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for NonNull<T> {}
192+
#[stable(feature = "catch_unwind", since = "1.9.0")]
193+
impl<T> UnwindSafe for AssertUnwindSafe<T> {}
194+
195+
// Pretty simple implementations for the `RefUnwindSafe` marker trait,
196+
// basically just saying that `UnsafeCell` is the
197+
// only thing which doesn't implement it (which then transitively applies to
198+
// everything else).
199+
#[stable(feature = "catch_unwind", since = "1.9.0")]
200+
impl<T: ?Sized> !RefUnwindSafe for UnsafeCell<T> {}
201+
#[stable(feature = "catch_unwind", since = "1.9.0")]
202+
impl<T> RefUnwindSafe for AssertUnwindSafe<T> {}
203+
204+
#[cfg(target_has_atomic_load_store = "ptr")]
205+
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
206+
impl RefUnwindSafe for crate::sync::atomic::AtomicIsize {}
207+
#[cfg(target_has_atomic_load_store = "8")]
208+
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
209+
impl RefUnwindSafe for crate::sync::atomic::AtomicI8 {}
210+
#[cfg(target_has_atomic_load_store = "16")]
211+
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
212+
impl RefUnwindSafe for crate::sync::atomic::AtomicI16 {}
213+
#[cfg(target_has_atomic_load_store = "32")]
214+
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
215+
impl RefUnwindSafe for crate::sync::atomic::AtomicI32 {}
216+
#[cfg(target_has_atomic_load_store = "64")]
217+
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
218+
impl RefUnwindSafe for crate::sync::atomic::AtomicI64 {}
219+
#[cfg(target_has_atomic_load_store = "128")]
220+
#[unstable(feature = "integer_atomics", issue = "32976")]
221+
impl RefUnwindSafe for crate::sync::atomic::AtomicI128 {}
222+
223+
#[cfg(target_has_atomic_load_store = "ptr")]
224+
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
225+
impl RefUnwindSafe for crate::sync::atomic::AtomicUsize {}
226+
#[cfg(target_has_atomic_load_store = "8")]
227+
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
228+
impl RefUnwindSafe for crate::sync::atomic::AtomicU8 {}
229+
#[cfg(target_has_atomic_load_store = "16")]
230+
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
231+
impl RefUnwindSafe for crate::sync::atomic::AtomicU16 {}
232+
#[cfg(target_has_atomic_load_store = "32")]
233+
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
234+
impl RefUnwindSafe for crate::sync::atomic::AtomicU32 {}
235+
#[cfg(target_has_atomic_load_store = "64")]
236+
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
237+
impl RefUnwindSafe for crate::sync::atomic::AtomicU64 {}
238+
#[cfg(target_has_atomic_load_store = "128")]
239+
#[unstable(feature = "integer_atomics", issue = "32976")]
240+
impl RefUnwindSafe for crate::sync::atomic::AtomicU128 {}
241+
242+
#[cfg(target_has_atomic_load_store = "8")]
243+
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
244+
impl RefUnwindSafe for crate::sync::atomic::AtomicBool {}
245+
246+
#[cfg(target_has_atomic_load_store = "ptr")]
247+
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
248+
impl<T> RefUnwindSafe for crate::sync::atomic::AtomicPtr<T> {}
249+
250+
#[stable(feature = "catch_unwind", since = "1.9.0")]
251+
impl<T> Deref for AssertUnwindSafe<T> {
252+
type Target = T;
253+
254+
fn deref(&self) -> &T {
255+
&self.0
256+
}
257+
}
258+
259+
#[stable(feature = "catch_unwind", since = "1.9.0")]
260+
impl<T> DerefMut for AssertUnwindSafe<T> {
261+
fn deref_mut(&mut self) -> &mut T {
262+
&mut self.0
263+
}
264+
}
265+
266+
#[stable(feature = "catch_unwind", since = "1.9.0")]
267+
impl<R, F: FnOnce() -> R> FnOnce<()> for AssertUnwindSafe<F> {
268+
type Output = R;
269+
270+
extern "rust-call" fn call_once(self, _args: ()) -> R {
271+
(self.0)()
272+
}
273+
}
274+
275+
#[stable(feature = "std_debug", since = "1.16.0")]
276+
impl<T: fmt::Debug> fmt::Debug for AssertUnwindSafe<T> {
277+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278+
f.debug_tuple("AssertUnwindSafe").field(&self.0).finish()
279+
}
280+
}
281+
282+
#[stable(feature = "futures_api", since = "1.36.0")]
283+
impl<F: Future> Future for AssertUnwindSafe<F> {
284+
type Output = F::Output;
285+
286+
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
287+
// SAFETY: pin projection. AssertUnwindSafe follows structural pinning.
288+
let pinned_field = unsafe { Pin::map_unchecked_mut(self, |x| &mut x.0) };
289+
F::poll(pinned_field, cx)
290+
}
291+
}
292+
293+
#[unstable(feature = "async_stream", issue = "79024")]
294+
impl<S: Stream> Stream for AssertUnwindSafe<S> {
295+
type Item = S::Item;
296+
297+
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
298+
// SAFETY: pin projection. AssertUnwindSafe follows structural pinning.
299+
unsafe { self.map_unchecked_mut(|x| &mut x.0) }.poll_next(cx)
300+
}
301+
302+
fn size_hint(&self) -> (usize, Option<usize>) {
303+
self.0.size_hint()
304+
}
305+
}

‎library/std/src/panic.rs

Lines changed: 4 additions & 309 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,9 @@
33
#![stable(feature = "std_panic", since = "1.9.0")]
44

55
use crate::any::Any;
6-
use crate::cell::UnsafeCell;
76
use crate::collections;
8-
use crate::fmt;
9-
use crate::future::Future;
10-
use crate::ops::{Deref, DerefMut};
117
use crate::panicking;
12-
use crate::pin::Pin;
13-
use crate::ptr::{NonNull, Unique};
14-
use crate::rc::Rc;
15-
use crate::stream::Stream;
16-
use crate::sync::atomic;
17-
use crate::sync::{Arc, Mutex, RwLock};
18-
use crate::task::{Context, Poll};
8+
use crate::sync::{Mutex, RwLock};
199
use crate::thread::Result;
2010

2111
#[doc(hidden)]
@@ -45,6 +35,9 @@ pub use crate::panicking::{set_hook, take_hook};
4535
#[stable(feature = "panic_hooks", since = "1.10.0")]
4636
pub use core::panic::{Location, PanicInfo};
4737

38+
#[stable(feature = "catch_unwind", since = "1.9.0")]
39+
pub use core::panic::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};
40+
4841
/// Panic the current thread with the given message as the panic payload.
4942
///
5043
/// The message can be of any (`Any + Send`) type, not just strings.
@@ -60,259 +53,16 @@ pub fn panic_any<M: 'static + Any + Send>(msg: M) -> ! {
6053
crate::panicking::begin_panic(msg);
6154
}
6255

63-
/// A marker trait which represents "panic safe" types in Rust.
64-
///
65-
/// This trait is implemented by default for many types and behaves similarly in
66-
/// terms of inference of implementation to the [`Send`] and [`Sync`] traits. The
67-
/// purpose of this trait is to encode what types are safe to cross a [`catch_unwind`]
68-
/// boundary with no fear of unwind safety.
69-
///
70-
/// ## What is unwind safety?
71-
///
72-
/// In Rust a function can "return" early if it either panics or calls a
73-
/// function which transitively panics. This sort of control flow is not always
74-
/// anticipated, and has the possibility of causing subtle bugs through a
75-
/// combination of two critical components:
76-
///
77-
/// 1. A data structure is in a temporarily invalid state when the thread
78-
/// panics.
79-
/// 2. This broken invariant is then later observed.
80-
///
81-
/// Typically in Rust, it is difficult to perform step (2) because catching a
82-
/// panic involves either spawning a thread (which in turns makes it difficult
83-
/// to later witness broken invariants) or using the `catch_unwind` function in this
84-
/// module. Additionally, even if an invariant is witnessed, it typically isn't a
85-
/// problem in Rust because there are no uninitialized values (like in C or C++).
86-
///
87-
/// It is possible, however, for **logical** invariants to be broken in Rust,
88-
/// which can end up causing behavioral bugs. Another key aspect of unwind safety
89-
/// in Rust is that, in the absence of `unsafe` code, a panic cannot lead to
90-
/// memory unsafety.
91-
///
92-
/// That was a bit of a whirlwind tour of unwind safety, but for more information
93-
/// about unwind safety and how it applies to Rust, see an [associated RFC][rfc].
94-
///
95-
/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md
96-
///
97-
/// ## What is `UnwindSafe`?
98-
///
99-
/// Now that we've got an idea of what unwind safety is in Rust, it's also
100-
/// important to understand what this trait represents. As mentioned above, one
101-
/// way to witness broken invariants is through the `catch_unwind` function in this
102-
/// module as it allows catching a panic and then re-using the environment of
103-
/// the closure.
104-
///
105-
/// Simply put, a type `T` implements `UnwindSafe` if it cannot easily allow
106-
/// witnessing a broken invariant through the use of `catch_unwind` (catching a
107-
/// panic). This trait is an auto trait, so it is automatically implemented for
108-
/// many types, and it is also structurally composed (e.g., a struct is unwind
109-
/// safe if all of its components are unwind safe).
110-
///
111-
/// Note, however, that this is not an unsafe trait, so there is not a succinct
112-
/// contract that this trait is providing. Instead it is intended as more of a
113-
/// "speed bump" to alert users of `catch_unwind` that broken invariants may be
114-
/// witnessed and may need to be accounted for.
115-
///
116-
/// ## Who implements `UnwindSafe`?
117-
///
118-
/// Types such as `&mut T` and `&RefCell<T>` are examples which are **not**
119-
/// unwind safe. The general idea is that any mutable state which can be shared
120-
/// across `catch_unwind` is not unwind safe by default. This is because it is very
121-
/// easy to witness a broken invariant outside of `catch_unwind` as the data is
122-
/// simply accessed as usual.
123-
///
124-
/// Types like `&Mutex<T>`, however, are unwind safe because they implement
125-
/// poisoning by default. They still allow witnessing a broken invariant, but
126-
/// they already provide their own "speed bumps" to do so.
127-
///
128-
/// ## When should `UnwindSafe` be used?
129-
///
130-
/// It is not intended that most types or functions need to worry about this trait.
131-
/// It is only used as a bound on the `catch_unwind` function and as mentioned
132-
/// above, the lack of `unsafe` means it is mostly an advisory. The
133-
/// [`AssertUnwindSafe`] wrapper struct can be used to force this trait to be
134-
/// implemented for any closed over variables passed to `catch_unwind`.
135-
#[stable(feature = "catch_unwind", since = "1.9.0")]
136-
#[cfg_attr(not(test), rustc_diagnostic_item = "unwind_safe_trait")]
137-
#[rustc_on_unimplemented(
138-
message = "the type `{Self}` may not be safely transferred across an unwind boundary",
139-
label = "`{Self}` may not be safely transferred across an unwind boundary"
140-
)]
141-
pub auto trait UnwindSafe {}
142-
143-
/// A marker trait representing types where a shared reference is considered
144-
/// unwind safe.
145-
///
146-
/// This trait is namely not implemented by [`UnsafeCell`], the root of all
147-
/// interior mutability.
148-
///
149-
/// This is a "helper marker trait" used to provide impl blocks for the
150-
/// [`UnwindSafe`] trait, for more information see that documentation.
151-
#[stable(feature = "catch_unwind", since = "1.9.0")]
152-
#[cfg_attr(not(test), rustc_diagnostic_item = "ref_unwind_safe_trait")]
153-
#[rustc_on_unimplemented(
154-
message = "the type `{Self}` may contain interior mutability and a reference may not be safely \
155-
transferrable across a catch_unwind boundary",
156-
label = "`{Self}` may contain interior mutability and a reference may not be safely \
157-
transferrable across a catch_unwind boundary"
158-
)]
159-
pub auto trait RefUnwindSafe {}
160-
161-
/// A simple wrapper around a type to assert that it is unwind safe.
162-
///
163-
/// When using [`catch_unwind`] it may be the case that some of the closed over
164-
/// variables are not unwind safe. For example if `&mut T` is captured the
165-
/// compiler will generate a warning indicating that it is not unwind safe. It
166-
/// might not be the case, however, that this is actually a problem due to the
167-
/// specific usage of [`catch_unwind`] if unwind safety is specifically taken into
168-
/// account. This wrapper struct is useful for a quick and lightweight
169-
/// annotation that a variable is indeed unwind safe.
170-
///
171-
/// # Examples
172-
///
173-
/// One way to use `AssertUnwindSafe` is to assert that the entire closure
174-
/// itself is unwind safe, bypassing all checks for all variables:
175-
///
176-
/// ```
177-
/// use std::panic::{self, AssertUnwindSafe};
178-
///
179-
/// let mut variable = 4;
180-
///
181-
/// // This code will not compile because the closure captures `&mut variable`
182-
/// // which is not considered unwind safe by default.
183-
///
184-
/// // panic::catch_unwind(|| {
185-
/// // variable += 3;
186-
/// // });
187-
///
188-
/// // This, however, will compile due to the `AssertUnwindSafe` wrapper
189-
/// let result = panic::catch_unwind(AssertUnwindSafe(|| {
190-
/// variable += 3;
191-
/// }));
192-
/// // ...
193-
/// ```
194-
///
195-
/// Wrapping the entire closure amounts to a blanket assertion that all captured
196-
/// variables are unwind safe. This has the downside that if new captures are
197-
/// added in the future, they will also be considered unwind safe. Therefore,
198-
/// you may prefer to just wrap individual captures, as shown below. This is
199-
/// more annotation, but it ensures that if a new capture is added which is not
200-
/// unwind safe, you will get a compilation error at that time, which will
201-
/// allow you to consider whether that new capture in fact represent a bug or
202-
/// not.
203-
///
204-
/// ```
205-
/// use std::panic::{self, AssertUnwindSafe};
206-
///
207-
/// let mut variable = 4;
208-
/// let other_capture = 3;
209-
///
210-
/// let result = {
211-
/// let mut wrapper = AssertUnwindSafe(&mut variable);
212-
/// panic::catch_unwind(move || {
213-
/// **wrapper += other_capture;
214-
/// })
215-
/// };
216-
/// // ...
217-
/// ```
218-
#[stable(feature = "catch_unwind", since = "1.9.0")]
219-
pub struct AssertUnwindSafe<T>(#[stable(feature = "catch_unwind", since = "1.9.0")] pub T);
220-
221-
// Implementations of the `UnwindSafe` trait:
222-
//
223-
// * By default everything is unwind safe
224-
// * pointers T contains mutability of some form are not unwind safe
225-
// * Unique, an owning pointer, lifts an implementation
226-
// * Types like Mutex/RwLock which are explicitly poisoned are unwind safe
227-
// * Our custom AssertUnwindSafe wrapper is indeed unwind safe
228-
229-
#[stable(feature = "catch_unwind", since = "1.9.0")]
230-
impl<T: ?Sized> !UnwindSafe for &mut T {}
231-
#[stable(feature = "catch_unwind", since = "1.9.0")]
232-
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for &T {}
233-
#[stable(feature = "catch_unwind", since = "1.9.0")]
234-
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for *const T {}
235-
#[stable(feature = "catch_unwind", since = "1.9.0")]
236-
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for *mut T {}
237-
#[unstable(feature = "ptr_internals", issue = "none")]
238-
impl<T: UnwindSafe + ?Sized> UnwindSafe for Unique<T> {}
239-
#[stable(feature = "nonnull", since = "1.25.0")]
240-
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for NonNull<T> {}
24156
#[stable(feature = "catch_unwind", since = "1.9.0")]
24257
impl<T: ?Sized> UnwindSafe for Mutex<T> {}
24358
#[stable(feature = "catch_unwind", since = "1.9.0")]
24459
impl<T: ?Sized> UnwindSafe for RwLock<T> {}
245-
#[stable(feature = "catch_unwind", since = "1.9.0")]
246-
impl<T> UnwindSafe for AssertUnwindSafe<T> {}
247-
248-
// not covered via the Shared impl above b/c the inner contents use
249-
// Cell/AtomicUsize, but the usage here is unwind safe so we can lift the
250-
// impl up one level to Arc/Rc itself
251-
#[stable(feature = "catch_unwind", since = "1.9.0")]
252-
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Rc<T> {}
253-
#[stable(feature = "catch_unwind", since = "1.9.0")]
254-
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Arc<T> {}
255-
256-
// Pretty simple implementations for the `RefUnwindSafe` marker trait,
257-
// basically just saying that `UnsafeCell` is the
258-
// only thing which doesn't implement it (which then transitively applies to
259-
// everything else).
260-
#[stable(feature = "catch_unwind", since = "1.9.0")]
261-
impl<T: ?Sized> !RefUnwindSafe for UnsafeCell<T> {}
262-
#[stable(feature = "catch_unwind", since = "1.9.0")]
263-
impl<T> RefUnwindSafe for AssertUnwindSafe<T> {}
26460

26561
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
26662
impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}
26763
#[stable(feature = "unwind_safe_lock_refs", since = "1.12.0")]
26864
impl<T: ?Sized> RefUnwindSafe for RwLock<T> {}
26965

270-
#[cfg(target_has_atomic_load_store = "ptr")]
271-
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
272-
impl RefUnwindSafe for atomic::AtomicIsize {}
273-
#[cfg(target_has_atomic_load_store = "8")]
274-
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
275-
impl RefUnwindSafe for atomic::AtomicI8 {}
276-
#[cfg(target_has_atomic_load_store = "16")]
277-
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
278-
impl RefUnwindSafe for atomic::AtomicI16 {}
279-
#[cfg(target_has_atomic_load_store = "32")]
280-
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
281-
impl RefUnwindSafe for atomic::AtomicI32 {}
282-
#[cfg(target_has_atomic_load_store = "64")]
283-
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
284-
impl RefUnwindSafe for atomic::AtomicI64 {}
285-
#[cfg(target_has_atomic_load_store = "128")]
286-
#[unstable(feature = "integer_atomics", issue = "32976")]
287-
impl RefUnwindSafe for atomic::AtomicI128 {}
288-
289-
#[cfg(target_has_atomic_load_store = "ptr")]
290-
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
291-
impl RefUnwindSafe for atomic::AtomicUsize {}
292-
#[cfg(target_has_atomic_load_store = "8")]
293-
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
294-
impl RefUnwindSafe for atomic::AtomicU8 {}
295-
#[cfg(target_has_atomic_load_store = "16")]
296-
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
297-
impl RefUnwindSafe for atomic::AtomicU16 {}
298-
#[cfg(target_has_atomic_load_store = "32")]
299-
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
300-
impl RefUnwindSafe for atomic::AtomicU32 {}
301-
#[cfg(target_has_atomic_load_store = "64")]
302-
#[stable(feature = "integer_atomics_stable", since = "1.34.0")]
303-
impl RefUnwindSafe for atomic::AtomicU64 {}
304-
#[cfg(target_has_atomic_load_store = "128")]
305-
#[unstable(feature = "integer_atomics", issue = "32976")]
306-
impl RefUnwindSafe for atomic::AtomicU128 {}
307-
308-
#[cfg(target_has_atomic_load_store = "8")]
309-
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
310-
impl RefUnwindSafe for atomic::AtomicBool {}
311-
312-
#[cfg(target_has_atomic_load_store = "ptr")]
313-
#[stable(feature = "unwind_safe_atomic_refs", since = "1.14.0")]
314-
impl<T> RefUnwindSafe for atomic::AtomicPtr<T> {}
315-
31666
// https://github.com/rust-lang/rust/issues/62301
31767
#[stable(feature = "hashbrown", since = "1.36.0")]
31868
impl<K, V, S> UnwindSafe for collections::HashMap<K, V, S>
@@ -323,61 +73,6 @@ where
32373
{
32474
}
32575

326-
#[stable(feature = "catch_unwind", since = "1.9.0")]
327-
impl<T> Deref for AssertUnwindSafe<T> {
328-
type Target = T;
329-
330-
fn deref(&self) -> &T {
331-
&self.0
332-
}
333-
}
334-
335-
#[stable(feature = "catch_unwind", since = "1.9.0")]
336-
impl<T> DerefMut for AssertUnwindSafe<T> {
337-
fn deref_mut(&mut self) -> &mut T {
338-
&mut self.0
339-
}
340-
}
341-
342-
#[stable(feature = "catch_unwind", since = "1.9.0")]
343-
impl<R, F: FnOnce() -> R> FnOnce<()> for AssertUnwindSafe<F> {
344-
type Output = R;
345-
346-
extern "rust-call" fn call_once(self, _args: ()) -> R {
347-
(self.0)()
348-
}
349-
}
350-
351-
#[stable(feature = "std_debug", since = "1.16.0")]
352-
impl<T: fmt::Debug> fmt::Debug for AssertUnwindSafe<T> {
353-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354-
f.debug_tuple("AssertUnwindSafe").field(&self.0).finish()
355-
}
356-
}
357-
358-
#[stable(feature = "futures_api", since = "1.36.0")]
359-
impl<F: Future> Future for AssertUnwindSafe<F> {
360-
type Output = F::Output;
361-
362-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
363-
let pinned_field = unsafe { Pin::map_unchecked_mut(self, |x| &mut x.0) };
364-
F::poll(pinned_field, cx)
365-
}
366-
}
367-
368-
#[unstable(feature = "async_stream", issue = "79024")]
369-
impl<S: Stream> Stream for AssertUnwindSafe<S> {
370-
type Item = S::Item;
371-
372-
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<S::Item>> {
373-
unsafe { self.map_unchecked_mut(|x| &mut x.0) }.poll_next(cx)
374-
}
375-
376-
fn size_hint(&self) -> (usize, Option<usize>) {
377-
self.0.size_hint()
378-
}
379-
}
380-
38176
/// Invokes a closure, capturing the cause of an unwinding panic if one occurs.
38277
///
38378
/// This function will return `Ok` with the closure's result if the closure

0 commit comments

Comments
 (0)
Please sign in to comment.