Skip to content

Commit 4e17994

Browse files
committed
Move UnwindSafe, RefUnwindSafe, AssertUnwindSafe to core
1 parent 76e73b7 commit 4e17994

File tree

5 files changed

+319
-309
lines changed

5 files changed

+319
-309
lines changed

library/alloc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ mod boxed {
177177
pub mod borrow;
178178
pub mod collections;
179179
pub mod fmt;
180+
mod panic;
180181
pub mod prelude;
181182
pub mod raw_vec;
182183
pub mod rc;

library/alloc/src/panic.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use crate::rc::Rc;
2+
use crate::sync::Arc;
3+
use core::panic::{RefUnwindSafe, UnwindSafe};
4+
5+
// not covered via the Shared impl above b/c the inner contents use
6+
// Cell/AtomicUsize, but the usage here is unwind safe so we can lift the
7+
// impl up one level to Arc/Rc itself
8+
#[stable(feature = "catch_unwind", since = "1.9.0")]
9+
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Rc<T> {}
10+
#[stable(feature = "catch_unwind", since = "1.9.0")]
11+
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for Arc<T> {}

library/core/src/panic.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44

55
mod location;
66
mod panic_info;
7+
mod unwind_safe;
78

89
use crate::any::Any;
910

1011
#[stable(feature = "panic_hooks", since = "1.10.0")]
1112
pub use self::location::Location;
1213
#[stable(feature = "panic_hooks", since = "1.10.0")]
1314
pub use self::panic_info::PanicInfo;
15+
#[stable(feature = "catch_unwind", since = "1.9.0")]
16+
pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};
1417

1518
#[doc(hidden)]
1619
#[unstable(feature = "edition_panic", issue = "none", reason = "use panic!() instead")]

library/core/src/panic/unwind_safe.rs

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

0 commit comments

Comments
 (0)