about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs29
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs22
-rw-r--r--src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs18
-rw-r--r--src/test/codegen/unwind-abis/c-unwind-abi.rs29
-rw-r--r--src/test/codegen/unwind-abis/stdcall-unwind-abi.rs32
-rw-r--r--src/test/codegen/unwind-abis/system-unwind-abi.rs29
-rw-r--r--src/test/codegen/unwind-abis/thiscall-unwind-abi.rs33
7 files changed, 176 insertions, 16 deletions
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 2f5380861c4..ee2dffd8bae 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -2562,6 +2562,7 @@ fn fn_can_unwind(
     panic_strategy: PanicStrategy,
     codegen_fn_attr_flags: CodegenFnAttrFlags,
     call_conv: Conv,
+    abi: SpecAbi,
 ) -> bool {
     if panic_strategy != PanicStrategy::Unwind {
         // In panic=abort mode we assume nothing can unwind anywhere, so
@@ -2586,17 +2587,16 @@ fn fn_can_unwind(
             //
             //  2. A Rust item using a non-Rust ABI (like `extern "C" fn foo() { ... }`).
             //
-            // Foreign items (case 1) are assumed to not unwind; it is
-            // UB otherwise. (At least for now; see also
-            // rust-lang/rust#63909 and Rust RFC 2753.)
-            //
-            // Items defined in Rust with non-Rust ABIs (case 2) are also
-            // not supposed to unwind. Whether this should be enforced
-            // (versus stating it is UB) and *how* it would be enforced
-            // is currently under discussion; see rust-lang/rust#58794.
-            //
-            // In either case, we mark item as explicitly nounwind.
-            false
+            // In both of these cases, we should refer to the ABI to determine whether or not we
+            // should unwind. See Rust RFC 2945 for more information on this behavior, here:
+            // https://github.com/rust-lang/rfcs/blob/master/text/2945-c-unwind-abi.md
+            use SpecAbi::*;
+            match abi {
+                C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
+                    unwind
+                }
+                _ => false,
+            }
         }
     }
 }
@@ -2823,7 +2823,12 @@ where
             c_variadic: sig.c_variadic,
             fixed_count: inputs.len(),
             conv,
-            can_unwind: fn_can_unwind(cx.tcx().sess.panic_strategy(), codegen_fn_attr_flags, conv),
+            can_unwind: fn_can_unwind(
+                cx.tcx().sess.panic_strategy(),
+                codegen_fn_attr_flags,
+                conv,
+                sig.abi,
+            ),
         };
         fn_abi.adjust_for_abi(cx, sig.abi);
         debug!("FnAbi::new_internal = {:?}", fn_abi);
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index b928458df8e..dd9c859544f 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -548,7 +548,7 @@ macro_rules! unpack {
     }};
 }
 
-fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool {
+fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, abi: Abi) -> bool {
     // Validate `#[unwind]` syntax regardless of platform-specific panic strategy.
     let attrs = &tcx.get_attrs(fn_def_id.to_def_id());
     let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs);
@@ -558,12 +558,26 @@ fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> b
         return false;
     }
 
-    // This is a special case: some functions have a C abi but are meant to
-    // unwind anyway. Don't stop them.
     match unwind_attr {
-        None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)`
+        // If an `#[unwind]` attribute was found, we should adhere to it.
         Some(UnwindAttr::Allowed) => false,
         Some(UnwindAttr::Aborts) => true,
+        // If no attribute was found and the panic strategy is `unwind`, then we should examine
+        // the function's ABI string to determine whether it should abort upon panic.
+        None => {
+            use Abi::*;
+            match abi {
+                // In the case of ABI's that have an `-unwind` equivalent, check whether the ABI
+                // permits unwinding. If so, we should not abort. Otherwise, we should.
+                C { unwind } | Stdcall { unwind } | System { unwind } | Thiscall { unwind } => {
+                    !unwind
+                }
+                // Rust and `rust-call` functions are allowed to unwind, and should not abort.
+                Rust | RustCall => false,
+                // Other ABI's should abort.
+                _ => true,
+            }
+        }
     }
 }
 
diff --git a/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs b/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs
new file mode 100644
index 00000000000..afd65ff6741
--- /dev/null
+++ b/src/test/codegen/unwind-abis/c-unwind-abi-panic-abort.rs
@@ -0,0 +1,18 @@
+// compile-flags: -C panic=abort -C opt-level=0
+
+// Test that `nounwind` atributes are applied to `C-unwind` extern functions when the
+// code is compiled with `panic=abort`.  We disable optimizations above to prevent LLVM from
+// inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "C-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make sure that the LLVM attributes for this functions are correct.  First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
diff --git a/src/test/codegen/unwind-abis/c-unwind-abi.rs b/src/test/codegen/unwind-abis/c-unwind-abi.rs
new file mode 100644
index 00000000000..f1576536753
--- /dev/null
+++ b/src/test/codegen/unwind-abis/c-unwind-abi.rs
@@ -0,0 +1,29 @@
+// compile-flags: -C opt-level=0
+
+// Test that `nounwind` atributes are correctly applied to exported `C` and `C-unwind` extern
+// functions. `C-unwind` functions MUST NOT have this attribute. We disable optimizations above
+// to prevent LLVM from inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "C" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "C-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct.  First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
diff --git a/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs b/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs
new file mode 100644
index 00000000000..ed804ca278d
--- /dev/null
+++ b/src/test/codegen/unwind-abis/stdcall-unwind-abi.rs
@@ -0,0 +1,32 @@
+// compile-flags: -C opt-level=0
+// ignore-arm stdcall isn't supported
+// ignore-aarch64 stdcall isn't supported
+// ignore-riscv64 stdcall isn't supported
+
+// Test that `nounwind` atributes are correctly applied to exported `stdcall` and `stdcall-unwind`
+// extern functions. `stdcall-unwind` functions MUST NOT have this attribute. We disable
+// optimizations above to prevent LLVM from inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "stdcall" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "stdcall-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct.  First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
diff --git a/src/test/codegen/unwind-abis/system-unwind-abi.rs b/src/test/codegen/unwind-abis/system-unwind-abi.rs
new file mode 100644
index 00000000000..c4d51328352
--- /dev/null
+++ b/src/test/codegen/unwind-abis/system-unwind-abi.rs
@@ -0,0 +1,29 @@
+// compile-flags: -C opt-level=0
+
+// Test that `nounwind` atributes are correctly applied to exported `system` and `system-unwind`
+// extern functions. `system-unwind` functions MUST NOT have this attribute. We disable
+// optimizations above to prevent LLVM from inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "system" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "system-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct.  First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }
diff --git a/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs b/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs
new file mode 100644
index 00000000000..aaa63ae55c3
--- /dev/null
+++ b/src/test/codegen/unwind-abis/thiscall-unwind-abi.rs
@@ -0,0 +1,33 @@
+// compile-flags: -C opt-level=0
+// ignore-arm thiscall isn't supported
+// ignore-aarch64 thiscall isn't supported
+// ignore-riscv64 thiscall isn't supported
+
+// Test that `nounwind` atributes are correctly applied to exported `thiscall` and
+// `thiscall-unwind` extern functions. `thiscall-unwind` functions MUST NOT have this attribute. We
+// disable optimizations above to prevent LLVM from inferring the attribute.
+
+#![crate_type = "lib"]
+#![feature(abi_thiscall)]
+#![feature(c_unwind)]
+
+// CHECK: @rust_item_that_cannot_unwind() unnamed_addr #0 {
+#[no_mangle]
+pub extern "thiscall" fn rust_item_that_cannot_unwind() {
+}
+
+// CHECK: @rust_item_that_can_unwind() unnamed_addr #1 {
+#[no_mangle]
+pub extern "thiscall-unwind" fn rust_item_that_can_unwind() {
+}
+
+// Now, make some assertions that the LLVM attributes for these functions are correct.  First, make
+// sure that the first item is correctly marked with the `nounwind` attribute:
+//
+// CHECK: attributes #0 = { {{.*}}nounwind{{.*}} }
+//
+// Next, let's assert that the second item, which CAN unwind, does not have this attribute.
+//
+// CHECK: attributes #1 = {
+// CHECK-NOT: nounwind
+// CHECK: }