From 4c2ddb33ad9e2dbfc3713438472ca85cb5aefd07 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 23 Mar 2015 14:25:56 -0700 Subject: std: Reexport std::rt::unwind::try in std::thread This commit provides a safe, but unstable interface for the `try` functionality of running a closure and determining whether it panicked or not. There are two primary reasons that this function was previously marked `unsafe`: 1. A vanilla version of this function exposes the problem of exception safety by allowing a bare try/catch in the language. It is not clear whether this concern should be directly tied to `unsafe` in Rust at the API level. At this time, however, the bounds on `ffi::try` require the closure to be both `'static` and `Send` (mirroring those of `thread::spawn`). It may be possible to relax the bounds in the future, but for now it's the level of safety that we're willing to commit to. 2. Panicking while panicking will leak resources by not running destructors. Because panicking is still controlled by the standard library, safeguards remain in place to prevent this from happening. The new API is now called `catch_panic` and is marked as `#[unstable]` for now. --- src/libstd/thread/mod.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'src/libstd/thread') diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 57baeb1fb74..7e96745d225 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -434,6 +434,55 @@ pub fn panicking() -> bool { unwind::panicking() } +/// Invoke a closure, capturing the cause of panic if one occurs. +/// +/// This function will return `Ok(())` if the closure does not panic, and will +/// return `Err(cause)` if the closure panics. The `cause` returned is the +/// object with which panic was originally invoked. +/// +/// 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. +/// +/// It is **not** recommended to use this function for a general try/catch +/// mechanism. The `Result` type is more appropriate to use for functions that +/// can fail on a regular basis. +/// +/// The closure provided is required to adhere to the `'static` bound to ensure +/// that it cannot reference data in the parent stack frame, mitigating problems +/// with exception safety. Furthermore, a `Send` bound is also required, +/// providing the same safety guarantees as `thread::spawn` (ensuring the +/// closure is properly isolated from the parent). +/// +/// # Examples +/// +/// ``` +/// # #![feature(catch_panic)] +/// use std::thread; +/// +/// let result = thread::catch_panic(|| { +/// println!("hello!"); +/// }); +/// assert!(result.is_ok()); +/// +/// let result = thread::catch_panic(|| { +/// panic!("oh no!"); +/// }); +/// assert!(result.is_err()); +/// ``` +#[unstable(feature = "catch_panic", reason = "recent API addition")] +pub fn catch_panic(f: F) -> Result + where F: FnOnce() -> R + Send + 'static +{ + let mut result = None; + unsafe { + let result = &mut result; + try!(::rt::unwind::try(move || *result = Some(f()))) + } + Ok(result.unwrap()) +} + /// Put the current thread to sleep for the specified amount of time. /// /// The thread may sleep longer than the duration specified due to scheduling -- cgit 1.4.1-3-g733a5 From 3752958e4029e9d9cfb1ff020e92142b53fb810f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 26 Mar 2015 13:30:33 -0700 Subject: syntax: Remove support for #[should_fail] This attribute has been deprecated in favor of #[should_panic]. This also updates rustdoc to no longer accept the `should_fail` directive and instead renames it to `should_panic`. --- src/doc/trpl/documentation.md | 2 +- src/libcore/option.rs | 4 ++-- src/libcore/result.rs | 4 ++-- src/librustdoc/html/markdown.rs | 18 +++++++++--------- src/libstd/io/buffered.rs | 2 +- src/libstd/macros.rs | 2 +- src/libstd/old_io/fs.rs | 2 +- src/libstd/old_io/process.rs | 2 +- src/libstd/process.rs | 2 +- src/libstd/thread/mod.rs | 4 ++-- src/libsyntax/feature_gate.rs | 1 - src/libsyntax/test.rs | 14 +++----------- 12 files changed, 24 insertions(+), 33 deletions(-) (limited to 'src/libstd/thread') diff --git a/src/doc/trpl/documentation.md b/src/doc/trpl/documentation.md index 54821e3ce30..43b49c09ae4 100644 --- a/src/doc/trpl/documentation.md +++ b/src/doc/trpl/documentation.md @@ -352,7 +352,7 @@ Here’s an example of documenting a macro: /// # } /// ``` /// -/// ```should_fail +/// ```should_panic /// # #[macro_use] extern crate foo; /// # fn main() { /// panic_unless!(true == false, “I’m broken.”); diff --git a/src/libcore/option.rs b/src/libcore/option.rs index a565b137cc8..623fcc56e6a 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -320,7 +320,7 @@ impl Option { /// assert_eq!(x.expect("the world is ending"), "value"); /// ``` /// - /// ```{.should_fail} + /// ```{.should_panic} /// let x: Option<&str> = None; /// x.expect("the world is ending"); // panics with `world is ending` /// ``` @@ -352,7 +352,7 @@ impl Option { /// assert_eq!(x.unwrap(), "air"); /// ``` /// - /// ```{.should_fail} + /// ```{.should_panic} /// let x: Option<&str> = None; /// assert_eq!(x.unwrap(), "air"); // fails /// ``` diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 62e1bcd827a..6b25bbdb0bb 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -762,7 +762,7 @@ impl Result { /// assert_eq!(x.unwrap(), 2); /// ``` /// - /// ```{.should_fail} + /// ```{.should_panic} /// let x: Result = Err("emergency failure"); /// x.unwrap(); // panics with `emergency failure` /// ``` @@ -788,7 +788,7 @@ impl Result { /// /// # Examples /// - /// ```{.should_fail} + /// ```{.should_panic} /// let x: Result = Ok(2); /// x.unwrap_err(); // panics with `2` /// ``` diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index cfa84de5ca7..8022f2d466f 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -356,7 +356,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { }); let text = lines.collect::>().connect("\n"); tests.add_test(text.to_string(), - block_info.should_fail, block_info.no_run, + block_info.should_panic, block_info.no_run, block_info.ignore, block_info.test_harness); } } @@ -397,7 +397,7 @@ pub fn find_testable_code(doc: &str, tests: &mut ::test::Collector) { #[derive(Eq, PartialEq, Clone, Debug)] struct LangString { - should_fail: bool, + should_panic: bool, no_run: bool, ignore: bool, rust: bool, @@ -407,7 +407,7 @@ struct LangString { impl LangString { fn all_false() -> LangString { LangString { - should_fail: false, + should_panic: false, no_run: false, ignore: false, rust: true, // NB This used to be `notrust = false` @@ -427,7 +427,7 @@ impl LangString { for token in tokens { match token { "" => {}, - "should_fail" => { data.should_fail = true; seen_rust_tags = true; }, + "should_panic" => { data.should_panic = true; seen_rust_tags = true; }, "no_run" => { data.no_run = true; seen_rust_tags = true; }, "ignore" => { data.ignore = true; seen_rust_tags = true; }, "rust" => { data.rust = true; seen_rust_tags = true; }, @@ -528,9 +528,9 @@ mod tests { #[test] fn test_lang_string_parse() { fn t(s: &str, - should_fail: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool) { + should_panic: bool, no_run: bool, ignore: bool, rust: bool, test_harness: bool) { assert_eq!(LangString::parse(s), LangString { - should_fail: should_fail, + should_panic: should_panic, no_run: no_run, ignore: ignore, rust: rust, @@ -538,16 +538,16 @@ mod tests { }) } - // marker | should_fail | no_run | ignore | rust | test_harness + // marker | should_panic| no_run | ignore | rust | test_harness t("", false, false, false, true, false); t("rust", false, false, false, true, false); t("sh", false, false, false, false, false); t("ignore", false, false, true, true, false); - t("should_fail", true, false, false, true, false); + t("should_panic", true, false, false, true, false); t("no_run", false, true, false, true, false); t("test_harness", false, false, false, true, true); t("{.no_run .example}", false, true, false, true, false); - t("{.sh .should_fail}", true, false, false, true, false); + t("{.sh .should_panic}", true, false, false, true, false); t("{.example .rust}", false, false, false, true, false); t("{.test_harness .rust}", false, false, false, true, true); } diff --git a/src/libstd/io/buffered.rs b/src/libstd/io/buffered.rs index 2a1294f23b2..23cbe79145a 100644 --- a/src/libstd/io/buffered.rs +++ b/src/libstd/io/buffered.rs @@ -681,7 +681,7 @@ mod tests { } #[test] - #[should_fail] + #[should_panic] fn dont_panic_in_drop_on_panicked_flush() { struct FailFlushWriter; diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index 1681ed4282f..52492a019a2 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -28,7 +28,7 @@ /// /// # Examples /// -/// ```should_fail +/// ```should_panic /// # #![allow(unreachable_code)] /// panic!(); /// panic!("this is a terrible mistake!"); diff --git a/src/libstd/old_io/fs.rs b/src/libstd/old_io/fs.rs index 40a7cce81dd..c2a0477b4d1 100644 --- a/src/libstd/old_io/fs.rs +++ b/src/libstd/old_io/fs.rs @@ -105,7 +105,7 @@ impl File { /// /// # Examples /// - /// ```rust,should_fail + /// ```rust,should_panic /// # #![feature(old_io, old_path)] /// use std::old_io::*; /// use std::old_path::Path; diff --git a/src/libstd/old_io/process.rs b/src/libstd/old_io/process.rs index d7ede451fb8..40be2a8b9d9 100644 --- a/src/libstd/old_io/process.rs +++ b/src/libstd/old_io/process.rs @@ -60,7 +60,7 @@ use thread; /// /// # Examples /// -/// ```should_fail +/// ```should_panic /// # #![feature(old_io)] /// use std::old_io::*; /// diff --git a/src/libstd/process.rs b/src/libstd/process.rs index 6a36ecefcf4..b4bd513e8f0 100644 --- a/src/libstd/process.rs +++ b/src/libstd/process.rs @@ -37,7 +37,7 @@ use thread; /// /// # Examples /// -/// ```should_fail +/// ```should_panic /// # #![feature(process)] /// /// use std::process::Command; diff --git a/src/libstd/thread/mod.rs b/src/libstd/thread/mod.rs index 27b50fc9aaa..8f08bf76fc4 100644 --- a/src/libstd/thread/mod.rs +++ b/src/libstd/thread/mod.rs @@ -821,13 +821,13 @@ mod test { } #[test] - #[should_fail] + #[should_panic] fn test_scoped_panic() { thread::scoped(|| panic!()).join(); } #[test] - #[should_fail] + #[should_panic] fn test_scoped_implicit_panic() { let _ = thread::scoped(|| panic!()); } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 60f81dac1e9..d5a8d3cc439 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -201,7 +201,6 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ ("no_mangle", Normal), ("no_link", Normal), ("derive", Normal), - ("should_fail", Normal), ("should_panic", Normal), ("ignore", Normal), ("no_implicit_prelude", Normal), diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 2a47a696b1c..fbee11ee657 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -134,7 +134,7 @@ impl<'a> fold::Folder for TestHarnessGenerator<'a> { path: self.cx.path.clone(), bench: is_bench_fn(&self.cx, &*i), ignore: is_ignored(&*i), - should_panic: should_panic(&*i, self.cx.span_diagnostic) + should_panic: should_panic(&*i) }; self.cx.testfns.push(test); self.tests.push(i.ident); @@ -386,16 +386,8 @@ fn is_ignored(i: &ast::Item) -> bool { i.attrs.iter().any(|attr| attr.check_name("ignore")) } -fn should_panic(i: &ast::Item, diag: &diagnostic::SpanHandler) -> ShouldPanic { - match i.attrs.iter().find(|attr| { - if attr.check_name("should_panic") { return true; } - if attr.check_name("should_fail") { - diag.span_warn(attr.span, "`#[should_fail]` is deprecated. Use `#[should_panic]` \ - instead"); - return true; - } - false - }) { +fn should_panic(i: &ast::Item) -> ShouldPanic { + match i.attrs.iter().find(|attr| attr.check_name("should_panic")) { Some(attr) => { let msg = attr.meta_item_list() .and_then(|list| list.iter().find(|mi| mi.check_name("expected"))) -- cgit 1.4.1-3-g733a5