Skip to content

Commit a47fd3d

Browse files
committed
make #[unwind] attribute specify expectations more clearly
You can now choose between the following: - `#[unwind(allowed)]` - `#[unwind(aborts)]` Per #48251, the default is `#[unwind(allowed)]`, though I think we should change this eventually.
1 parent 27a046e commit a47fd3d

File tree

13 files changed

+113
-19
lines changed

13 files changed

+113
-19
lines changed

src/libcore/panicking.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32))
6464
#[allow(improper_ctypes)]
6565
extern {
6666
#[lang = "panic_fmt"]
67-
#[unwind]
67+
#[cfg_attr(stage0, unwind)]
68+
#[cfg_attr(not(stage0), unwind(allowed))]
6869
fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: u32, col: u32) -> !;
6970
}
7071
let (file, line, col) = *file_line_col;

src/libpanic_unwind/gcc.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,8 @@ unsafe fn find_eh_action(context: *mut uw::_Unwind_Context)
286286
// See docs in the `unwind` module.
287287
#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
288288
#[lang = "eh_unwind_resume"]
289-
#[unwind]
289+
#[cfg_attr(stage0, unwind)]
290+
#[cfg_attr(not(stage0), unwind(allowed))]
290291
unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {
291292
uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception);
292293
}

src/libpanic_unwind/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8),
112112
// Entry point for raising an exception, just delegates to the platform-specific
113113
// implementation.
114114
#[no_mangle]
115-
#[unwind]
115+
#[cfg_attr(stage0, unwind)]
116+
#[cfg_attr(not(stage0), unwind(allowed))]
116117
pub unsafe extern "C" fn __rust_start_panic(data: usize, vtable: usize) -> u32 {
117118
imp::panic(mem::transmute(raw::TraitObject {
118119
data: data as *mut (),

src/libpanic_unwind/seh64_gnu.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut c::EXCEPTION_RECO
108108
}
109109

110110
#[lang = "eh_unwind_resume"]
111-
#[unwind]
111+
#[cfg_attr(stage0, unwind)]
112+
#[cfg_attr(not(stage0), unwind(allowed))]
112113
unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! {
113114
let params = [panic_ctx as c::ULONG_PTR];
114115
c::RaiseException(RUST_PANIC,

src/libpanic_unwind/windows.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -79,18 +79,21 @@ pub enum EXCEPTION_DISPOSITION {
7979
pub use self::EXCEPTION_DISPOSITION::*;
8080

8181
extern "system" {
82-
#[unwind]
82+
#[cfg_attr(stage0, unwind)]
83+
#[cfg_attr(not(stage0), unwind(allowed))]
8384
pub fn RaiseException(dwExceptionCode: DWORD,
8485
dwExceptionFlags: DWORD,
8586
nNumberOfArguments: DWORD,
8687
lpArguments: *const ULONG_PTR);
87-
#[unwind]
88+
#[cfg_attr(stage0, unwind)]
89+
#[cfg_attr(not(stage0), unwind(allowed))]
8890
pub fn RtlUnwindEx(TargetFrame: LPVOID,
8991
TargetIp: LPVOID,
9092
ExceptionRecord: *const EXCEPTION_RECORD,
9193
ReturnValue: LPVOID,
9294
OriginalContext: *const CONTEXT,
9395
HistoryTable: *const UNWIND_HISTORY_TABLE);
94-
#[unwind]
96+
#[cfg_attr(stage0, unwind)]
97+
#[cfg_attr(not(stage0), unwind(allowed))]
9598
pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8);
9699
}

src/librustc_mir/build/mod.rs

+14-5
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use std::mem;
2828
use std::u32;
2929
use syntax::abi::Abi;
3030
use syntax::ast;
31+
use syntax::attr::{self, UnwindAttr};
3132
use syntax::symbol::keywords;
3233
use syntax_pos::Span;
3334
use transform::MirSource;
@@ -355,10 +356,9 @@ macro_rules! unpack {
355356
}
356357

357358
fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
358-
fn_id: ast::NodeId,
359+
fn_def_id: DefId,
359360
abi: Abi)
360361
-> bool {
361-
362362
// Not callable from C, so we can safely unwind through these
363363
if abi == Abi::Rust || abi == Abi::RustCall { return false; }
364364

@@ -370,9 +370,17 @@ fn should_abort_on_panic<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
370370

371371
// This is a special case: some functions have a C abi but are meant to
372372
// unwind anyway. Don't stop them.
373-
if tcx.has_attr(tcx.hir.local_def_id(fn_id), "unwind") { return false; }
373+
let attrs = &tcx.get_attrs(fn_def_id);
374+
match attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs) {
375+
None => {
376+
// FIXME(rust-lang/rust#48251) -- Had to disable
377+
// abort-on-panic for backwards compatibility reasons.
378+
false
379+
}
374380

375-
return true;
381+
Some(UnwindAttr::Allowed) => false,
382+
Some(UnwindAttr::Aborts) => true,
383+
}
376384
}
377385

378386
///////////////////////////////////////////////////////////////////////////
@@ -399,13 +407,14 @@ fn construct_fn<'a, 'gcx, 'tcx, A>(hir: Cx<'a, 'gcx, 'tcx>,
399407
safety,
400408
return_ty);
401409

410+
let fn_def_id = tcx.hir.local_def_id(fn_id);
402411
let call_site_scope = region::Scope::CallSite(body.value.hir_id.local_id);
403412
let arg_scope = region::Scope::Arguments(body.value.hir_id.local_id);
404413
let mut block = START_BLOCK;
405414
let source_info = builder.source_info(span);
406415
let call_site_s = (call_site_scope, source_info);
407416
unpack!(block = builder.in_scope(call_site_s, LintLevel::Inherited, block, |builder| {
408-
if should_abort_on_panic(tcx, fn_id, abi) {
417+
if should_abort_on_panic(tcx, fn_def_id, abi) {
409418
builder.schedule_abort();
410419
}
411420

src/libstd/panicking.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ extern {
5555
data: *mut u8,
5656
data_ptr: *mut usize,
5757
vtable_ptr: *mut usize) -> u32;
58-
#[unwind]
58+
#[cfg_attr(stage0, unwind)]
59+
#[cfg_attr(not(stage0), unwind(allowed))]
5960
fn __rust_start_panic(data: usize, vtable: usize) -> u32;
6061
}
6162

@@ -315,7 +316,8 @@ pub fn panicking() -> bool {
315316
/// Entry point of panic from the libcore crate.
316317
#[cfg(not(test))]
317318
#[lang = "panic_fmt"]
318-
#[unwind]
319+
#[cfg_attr(stage0, unwind)]
320+
#[cfg_attr(not(stage0), unwind(allowed))]
319321
pub extern fn rust_begin_panic(msg: fmt::Arguments,
320322
file: &'static str,
321323
line: u32,

src/libsyntax/attr.rs

+45
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,51 @@ pub fn find_inline_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> In
565565
})
566566
}
567567

568+
#[derive(Copy, Clone, PartialEq)]
569+
pub enum UnwindAttr {
570+
Allowed,
571+
Aborts,
572+
}
573+
574+
/// Determine what `#[unwind]` attribute is present in `attrs`, if any.
575+
pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option<UnwindAttr> {
576+
let syntax_error = |attr: &Attribute| {
577+
mark_used(attr);
578+
diagnostic.map(|d| {
579+
span_err!(d, attr.span, E0633, "malformed `#[unwind]` attribute");
580+
});
581+
None
582+
};
583+
584+
attrs.iter().fold(None, |ia, attr| {
585+
if attr.path != "unwind" {
586+
return ia;
587+
}
588+
let meta = match attr.meta() {
589+
Some(meta) => meta.node,
590+
None => return ia,
591+
};
592+
match meta {
593+
MetaItemKind::Word => {
594+
syntax_error(attr)
595+
}
596+
MetaItemKind::List(ref items) => {
597+
mark_used(attr);
598+
if items.len() != 1 {
599+
syntax_error(attr)
600+
} else if list_contains_name(&items[..], "allowed") {
601+
Some(UnwindAttr::Allowed)
602+
} else if list_contains_name(&items[..], "aborts") {
603+
Some(UnwindAttr::Aborts)
604+
} else {
605+
syntax_error(attr)
606+
}
607+
}
608+
_ => ia,
609+
}
610+
})
611+
}
612+
568613
/// True if `#[inline]` or `#[inline(always)]` is present in `attrs`.
569614
pub fn requests_inline(attrs: &[Attribute]) -> bool {
570615
match find_inline_attr(None, attrs) {

src/libsyntax/diagnostic_list.rs

+27
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,33 @@ fn main() {
342342
```
343343
"##,
344344

345+
E0633: r##"
346+
The `unwind` attribute was malformed.
347+
348+
Erroneous code example:
349+
350+
```ignore (compile_fail not working here; see Issue #43707)
351+
#[unwind()] // error: expected one argument
352+
pub extern fn something() {}
353+
354+
fn main() {}
355+
```
356+
357+
The `#[unwind]` attribute should be used as follows:
358+
359+
- `#[unwind(aborts)]` -- specifies that if a non-Rust ABI function
360+
should abort the process if it attempts to unwind. This is the safer
361+
and preferred option.
362+
363+
- `#[unwind(allowed)]` -- specifies that a non-Rust ABI function
364+
should be allowed to unwind. This can easily result in Undefined
365+
Behavior (UB), so be careful.
366+
367+
NB. The default behavior here is "allowed", but this is unspecified
368+
and likely to change in the future.
369+
370+
"##,
371+
345372
}
346373

347374
register_diagnostics! {

src/libsyntax/feature_gate.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ declare_features! (
233233
// allow `extern "platform-intrinsic" { ... }`
234234
(active, platform_intrinsics, "1.4.0", Some(27731)),
235235

236-
// allow `#[unwind]`
236+
// allow `#[unwind(..)]`
237237
// rust runtime internal
238238
(active, unwind_attributes, "1.4.0", None),
239239

src/libunwind/libunwind.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ pub enum _Unwind_Context {}
8383
pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code,
8484
exception: *mut _Unwind_Exception);
8585
extern "C" {
86-
#[unwind]
86+
#[cfg_attr(stage0, unwind)]
87+
#[cfg_attr(not(stage0), unwind(allowed))]
8788
pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !;
8889
pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception);
8990
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"))))] {
220221
if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] {
221222
// Not 32-bit iOS
222223
extern "C" {
223-
#[unwind]
224+
#[cfg_attr(stage0, unwind)]
225+
#[cfg_attr(not(stage0), unwind(allowed))]
224226
pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
225227
pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn,
226228
trace_argument: *mut c_void)
@@ -229,7 +231,8 @@ if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] {
229231
} else {
230232
// 32-bit iOS uses SjLj and does not provide _Unwind_Backtrace()
231233
extern "C" {
232-
#[unwind]
234+
#[cfg_attr(stage0, unwind)]
235+
#[cfg_attr(not(stage0), unwind(allowed))]
233236
pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code;
234237
}
235238

src/test/codegen/extern-functions.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ extern {
1919
fn extern_fn();
2020
// CHECK-NOT: Function Attrs: nounwind
2121
// CHECK: declare void @unwinding_extern_fn
22-
#[unwind]
22+
#[unwind(allowed)]
2323
fn unwinding_extern_fn();
2424
}
2525

src/test/run-pass/abort-on-c-abi.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use std::io::prelude::*;
1919
use std::io;
2020
use std::process::{Command, Stdio};
2121

22+
#[unwind(aborts)]
2223
extern "C" fn panic_in_ffi() {
2324
panic!("Test");
2425
}

0 commit comments

Comments
 (0)