From 370cab2c0970175ee840f1283a4c4de3e12b3161 Mon Sep 17 00:00:00 2001 From: KaiJewson Date: Wed, 2 Jun 2021 08:04:10 +0100 Subject: [PATCH] Add `std::panic::drop_unwind` --- library/std/src/panic.rs | 66 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index c1c039584979d..e40d87dc2f513 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -4,6 +4,7 @@ use crate::any::Any; use crate::collections; +use crate::mem; use crate::panicking; use crate::sync::{Mutex, RwLock}; use crate::thread::Result; @@ -106,6 +107,11 @@ where /// aborting the process as well. This function *only* catches unwinding panics, /// not those that abort the process. /// +/// This function returns the payload that the closure panicked with. Because the payload +/// is allowed to be any type, it is possible for it to be a type that itself panics when +/// dropped. To make sure that badly-behaved panic payloads like these do not cause bugs, +/// use [`drop_unwind`] instead. +/// /// Also note that unwinding into Rust code with a foreign exception (e.g. /// an exception thrown from C++ code) is undefined behavior. /// @@ -129,6 +135,66 @@ pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { unsafe { panicking::r#try(f) } } +/// Invokes a closure, dropping the cause of an unwinding panic if one occurs. +/// +/// Unlike [`catch_unwind`], this function does not return the payload that the +/// closure panicked with if it panics. Instead, the payload is dropped in such +/// a way that avoids propagating panics the payload causes when it is dropped. +/// As such, unlike [`catch_unwind`], you can safely ignore and drop the result +/// of this function without potentially causing the outer function to unwind. +/// +/// It is currently undefined behavior to unwind from Rust code into foreign +/// code, so this function is particularly useful when Rust is called from +/// another language (normally C). This can run arbitrary Rust code, capturing a +/// panic and allowing a graceful handling of the error. +/// +/// The closure provided is required to adhere to the [`UnwindSafe`] trait to ensure +/// that all captured variables are safe to cross this boundary. The purpose of +/// this bound is to encode the concept of [exception safety][rfc] in the type +/// system. Most usage of this function should not need to worry about this +/// bound as programs are naturally unwind safe without `unsafe` code. If it +/// becomes a problem the [`AssertUnwindSafe`] wrapper struct can be used to quickly +/// assert that the usage here is indeed unwind safe. +/// +/// [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/1236-stabilize-catch-panic.md +/// +/// # Notes +/// +/// Note that this function **may not catch all panics** in Rust. A panic in +/// Rust is not always implemented via unwinding, but can be implemented by +/// aborting the process as well. This function *only* catches unwinding panics, +/// not those that abort the process. +/// +/// Also note that unwinding into Rust code with a foreign exception (e.g. +/// an exception thrown from C++ code) is undefined behavior. +/// +/// # Examples +/// +/// ``` +/// #![feature(drop_unwind)] +/// +/// use std::panic; +/// +/// let _ = panic::drop_unwind(|| { +/// panic!("oh no!"); +/// }); +/// println!("hello!"); +/// ``` +#[unstable(feature = "drop_unwind", issue = "none")] +pub fn drop_unwind R + UnwindSafe, R>(f: F) -> core::result::Result { + struct AbortOnDrop; + impl Drop for AbortOnDrop { + fn drop(&mut self) { + crate::process::abort(); + } + } + + let abort_on_drop = AbortOnDrop; + let res = catch_unwind(f).map_err(drop); + mem::forget(abort_on_drop); + res +} + /// Triggers a panic without invoking the panic hook. /// /// This is designed to be used in conjunction with [`catch_unwind`] to, for