about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-02-20 13:49:54 -0500
committerNiko Matsakis <niko@alum.mit.edu>2018-02-20 19:12:52 -0500
commita47fd3df89c267829d96748b3bdff305f20d27d5 (patch)
tree676d0df0845297a873c5e0a6620f464dea49a642
parent27a046e9338fb0455c33b13e8fe28da78212dedc (diff)
downloadrust-a47fd3df89c267829d96748b3bdff305f20d27d5.tar.gz
rust-a47fd3df89c267829d96748b3bdff305f20d27d5.zip
make `#[unwind]` attribute specify expectations more clearly
You can now choose between the following:

- `#[unwind(allowed)]`
- `#[unwind(aborts)]`

Per rust-lang/rust#48251, the default is `#[unwind(allowed)]`, though
I think we should change this eventually.
-rw-r--r--src/libcore/panicking.rs3
-rw-r--r--src/libpanic_unwind/gcc.rs3
-rw-r--r--src/libpanic_unwind/lib.rs3
-rw-r--r--src/libpanic_unwind/seh64_gnu.rs3
-rw-r--r--src/libpanic_unwind/windows.rs9
-rw-r--r--src/librustc_mir/build/mod.rs19
-rw-r--r--src/libstd/panicking.rs6
-rw-r--r--src/libsyntax/attr.rs45
-rw-r--r--src/libsyntax/diagnostic_list.rs27
-rw-r--r--src/libsyntax/feature_gate.rs2
-rw-r--r--src/libunwind/libunwind.rs9
-rw-r--r--src/test/codegen/extern-functions.rs2
-rw-r--r--src/test/run-pass/abort-on-c-abi.rs1
13 files changed, 113 insertions, 19 deletions
diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs
index 4170d91e5fc..94db0baa3f9 100644
--- a/src/libcore/panicking.rs
+++ b/src/libcore/panicking.rs
@@ -64,7 +64,8 @@ pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32))
     #[allow(improper_ctypes)]
     extern {
         #[lang = "panic_fmt"]
-        #[unwind]
+        #[cfg_attr(stage0, unwind)]
+        #[cfg_attr(not(stage0), unwind(allowed))]
         fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: u32, col: u32) -> !;
     }
     let (file, line, col) = *file_line_col;
diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs
index 63e44f71a3a..ca2fd561cad 100644
--- a/src/libpanic_unwind/gcc.rs
+++ b/src/libpanic_unwind/gcc.rs
@@ -286,7 +286,8 @@ unsafe fn find_eh_action(context: *mut uw::_Unwind_Context)
 // See docs in the `unwind` module.
 #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
 #[lang = "eh_unwind_resume"]
-#[unwind]
+#[cfg_attr(stage0, unwind)]
+#[cfg_attr(not(stage0), unwind(allowed))]
 unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {
     uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception);
 }
diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs
index 92e40e8f26d..a5cebc3e4d0 100644
--- a/src/libpanic_unwind/lib.rs
+++ b/src/libpanic_unwind/lib.rs
@@ -112,7 +112,8 @@ pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8),
 // Entry point for raising an exception, just delegates to the platform-specific
 // implementation.
 #[no_mangle]
-#[unwind]
+#[cfg_attr(stage0, unwind)]
+#[cfg_attr(not(stage0), unwind(allowed))]
 pub unsafe extern "C" fn __rust_start_panic(data: usize, vtable: usize) -> u32 {
     imp::panic(mem::transmute(raw::TraitObject {
         data: data as *mut (),
diff --git a/src/libpanic_unwind/seh64_gnu.rs b/src/libpanic_unwind/seh64_gnu.rs
index 0a9fa7d9a80..090cd095380 100644
--- a/src/libpanic_unwind/seh64_gnu.rs
+++ b/src/libpanic_unwind/seh64_gnu.rs
@@ -108,7 +108,8 @@ unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut c::EXCEPTION_RECO
 }
 
 #[lang = "eh_unwind_resume"]
-#[unwind]
+#[cfg_attr(stage0, unwind)]
+#[cfg_attr(not(stage0), unwind(allowed))]
 unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! {
     let params = [panic_ctx as c::ULONG_PTR];
     c::RaiseException(RUST_PANIC,
diff --git a/src/libpanic_unwind/windows.rs b/src/libpanic_unwind/windows.rs
index a7e90071cea..50fba5faee7 100644
--- a/src/libpanic_unwind/windows.rs
+++ b/src/libpanic_unwind/windows.rs
@@ -79,18 +79,21 @@ pub enum EXCEPTION_DISPOSITION {
 pub use self::EXCEPTION_DISPOSITION::*;
 
 extern "system" {
-    #[unwind]
+    #[cfg_attr(stage0, unwind)]
+    #[cfg_attr(not(stage0), unwind(allowed))]
     pub fn RaiseException(dwExceptionCode: DWORD,
                           dwExceptionFlags: DWORD,
                           nNumberOfArguments: DWORD,
                           lpArguments: *const ULONG_PTR);
-    #[unwind]
+    #[cfg_attr(stage0, unwind)]
+    #[cfg_attr(not(stage0), unwind(allowed))]
     pub fn RtlUnwindEx(TargetFrame: LPVOID,
                        TargetIp: LPVOID,
                        ExceptionRecord: *const EXCEPTION_RECORD,
                        ReturnValue: LPVOID,
                        OriginalContext: *const CONTEXT,
                        HistoryTable: *const UNWIND_HISTORY_TABLE);
-    #[unwind]
+    #[cfg_attr(stage0, unwind)]
+    #[cfg_attr(not(stage0), unwind(allowed))]
     pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8);
 }
diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs
index 57059cd31a1..a325cfe3eaa 100644
--- a/src/librustc_mir/build/mod.rs
+++ b/src/librustc_mir/build/mod.rs
@@ -28,6 +28,7 @@ use std::mem;
 use std::u32;
 use syntax::abi::Abi;
 use syntax::ast;
+use syntax::attr::{self, UnwindAttr};
 use syntax::symbol::keywords;
 use syntax_pos::Span;
 use transform::MirSource;
@@ -355,10 +356,9 @@ macro_rules! unpack {
 }
 
 fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
-                                         fn_id: ast::NodeId,
+                                         fn_def_id: DefId,
                                          abi: Abi)
                                          -> bool {
-
     // Not callable from C, so we can safely unwind through these
     if abi == Abi::Rust || abi == Abi::RustCall { return false; }
 
@@ -370,9 +370,17 @@ fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
 
     // This is a special case: some functions have a C abi but are meant to
     // unwind anyway. Don't stop them.
-    if tcx.has_attr(tcx.hir.local_def_id(fn_id), "unwind") { return false; }
+    let attrs = &tcx.get_attrs(fn_def_id);
+    match attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs) {
+        None => {
+            // FIXME(rust-lang/rust#48251) -- Had to disable
+            // abort-on-panic for backwards compatibility reasons.
+            false
+        }
 
-    return true;
+        Some(UnwindAttr::Allowed) => false,
+        Some(UnwindAttr::Aborts) => true,
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
@@ -399,13 +407,14 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
         safety,
         return_ty);
 
+    let fn_def_id = tcx.hir.local_def_id(fn_id);
     let call_site_scope = region::Scope::CallSite(body.value.hir_id.local_id);
     let arg_scope = region::Scope::Arguments(body.value.hir_id.local_id);
     let mut block = START_BLOCK;
     let source_info = builder.source_info(span);
     let call_site_s = (call_site_scope, source_info);
     unpack!(block = builder.in_scope(call_site_s, LintLevel::Inherited, block, |builder| {
-        if should_abort_on_panic(tcx, fn_id, abi) {
+        if should_abort_on_panic(tcx, fn_def_id, abi) {
             builder.schedule_abort();
         }
 
diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs
index 161c3fc7113..454ac64735c 100644
--- a/src/libstd/panicking.rs
+++ b/src/libstd/panicking.rs
@@ -55,7 +55,8 @@ extern {
                                 data: *mut u8,
                                 data_ptr: *mut usize,
                                 vtable_ptr: *mut usize) -> u32;
-    #[unwind]
+    #[cfg_attr(stage0, unwind)]
+    #[cfg_attr(not(stage0), unwind(allowed))]
     fn __rust_start_panic(data: usize, vtable: usize) -> u32;
 }
 
@@ -315,7 +316,8 @@ pub fn panicking() -> bool {
 /// Entry point of panic from the libcore crate.
 #[cfg(not(test))]
 #[lang = "panic_fmt"]
-#[unwind]
+#[cfg_attr(stage0, unwind)]
+#[cfg_attr(not(stage0), unwind(allowed))]
 pub extern fn rust_begin_panic(msg: fmt::Arguments,
                                file: &'static str,
                                line: u32,
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index d18d6f5e6bd..d0822b69aa6 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -565,6 +565,51 @@ pub fn find_inline_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> In
     })
 }
 
+#[derive(Copy, Clone, PartialEq)]
+pub enum UnwindAttr {
+    Allowed,
+    Aborts,
+}
+
+/// Determine what `#[unwind]` attribute is present in `attrs`, if any.
+pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option<UnwindAttr> {
+    let syntax_error = |attr: &Attribute| {
+        mark_used(attr);
+        diagnostic.map(|d| {
+            span_err!(d, attr.span, E0633, "malformed `#[unwind]` attribute");
+        });
+        None
+    };
+
+    attrs.iter().fold(None, |ia, attr| {
+        if attr.path != "unwind" {
+            return ia;
+        }
+        let meta = match attr.meta() {
+            Some(meta) => meta.node,
+            None => return ia,
+        };
+        match meta {
+            MetaItemKind::Word => {
+                syntax_error(attr)
+            }
+            MetaItemKind::List(ref items) => {
+                mark_used(attr);
+                if items.len() != 1 {
+                    syntax_error(attr)
+                } else if list_contains_name(&items[..], "allowed") {
+                    Some(UnwindAttr::Allowed)
+                } else if list_contains_name(&items[..], "aborts") {
+                    Some(UnwindAttr::Aborts)
+                } else {
+                    syntax_error(attr)
+                }
+            }
+            _ => ia,
+        }
+    })
+}
+
 /// True if `#[inline]` or `#[inline(always)]` is present in `attrs`.
 pub fn requests_inline(attrs: &[Attribute]) -> bool {
     match find_inline_attr(None, attrs) {
diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs
index d841281e485..84ab0336f16 100644
--- a/src/libsyntax/diagnostic_list.rs
+++ b/src/libsyntax/diagnostic_list.rs
@@ -342,6 +342,33 @@ fn main() {
 ```
 "##,
 
+E0633: r##"
+The `unwind` attribute was malformed.
+
+Erroneous code example:
+
+```ignore (compile_fail not working here; see Issue #43707)
+#[unwind()] // error: expected one argument
+pub extern fn something() {}
+
+fn main() {}
+```
+
+The `#[unwind]` attribute should be used as follows:
+
+- `#[unwind(aborts)]` -- specifies that if a non-Rust ABI function
+  should abort the process if it attempts to unwind. This is the safer
+  and preferred option.
+
+- `#[unwind(allowed)]` -- specifies that a non-Rust ABI function
+  should be allowed to unwind. This can easily result in Undefined
+  Behavior (UB), so be careful.
+
+NB. The default behavior here is "allowed", but this is unspecified
+and likely to change in the future.
+
+"##,
+
 }
 
 register_diagnostics! {
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 3b137f9570a..f1d0a70a22c 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -233,7 +233,7 @@ declare_features! (
     // allow `extern "platform-intrinsic" { ... }`
     (active, platform_intrinsics, "1.4.0", Some(27731)),
 
-    // allow `#[unwind]`
+    // allow `#[unwind(..)]`
     // rust runtime internal
     (active, unwind_attributes, "1.4.0", None),
 
diff --git a/src/libunwind/libunwind.rs b/src/libunwind/libunwind.rs
index e6fff7963f7..aa73b11fb38 100644
--- a/src/libunwind/libunwind.rs
+++ b/src/libunwind/libunwind.rs
@@ -83,7 +83,8 @@ pub enum _Unwind_Context {}
 pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code,
                                                       exception: *mut _Unwind_Exception);
 extern "C" {
-    #[unwind]
+    #[cfg_attr(stage0, unwind)]
+    #[cfg_attr(not(stage0), unwind(allowed))]
     pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
     pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
     pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void;
@@ -220,7 +221,8 @@ if #[cfg(all(any(target_os = "ios", not(target_arch = "arm"))))] {
 if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] {
     // Not 32-bit iOS
     extern "C" {
-        #[unwind]
+        #[cfg_attr(stage0, unwind)]
+        #[cfg_attr(not(stage0), unwind(allowed))]
         pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
         pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
                                  trace_argument: *mut c_void)
@@ -229,7 +231,8 @@ if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] {
 } else {
     // 32-bit iOS uses SjLj and does not provide _Unwind_Backtrace()
     extern "C" {
-        #[unwind]
+        #[cfg_attr(stage0, unwind)]
+        #[cfg_attr(not(stage0), unwind(allowed))]
         pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
     }
 
diff --git a/src/test/codegen/extern-functions.rs b/src/test/codegen/extern-functions.rs
index 7ee31070b26..90ee0c75680 100644
--- a/src/test/codegen/extern-functions.rs
+++ b/src/test/codegen/extern-functions.rs
@@ -19,7 +19,7 @@ extern {
     fn extern_fn();
 // CHECK-NOT: Function Attrs: nounwind
 // CHECK: declare void @unwinding_extern_fn
-    #[unwind]
+    #[unwind(allowed)]
     fn unwinding_extern_fn();
 }
 
diff --git a/src/test/run-pass/abort-on-c-abi.rs b/src/test/run-pass/abort-on-c-abi.rs
index 17661c0b120..5039c334f26 100644
--- a/src/test/run-pass/abort-on-c-abi.rs
+++ b/src/test/run-pass/abort-on-c-abi.rs
@@ -19,6 +19,7 @@ use std::io::prelude::*;
 use std::io;
 use std::process::{Command, Stdio};
 
+#[unwind(aborts)]
 extern "C" fn panic_in_ffi() {
     panic!("Test");
 }