Change:
I would expect the following code to abort, and never print : But instead, the attribute . Cc
That looks like a bug. cc
is just , and having in the testcase may create the wrong impression (i.e. this bug has nothing to do with FFI). Still, the attribute should either work and cause an abort or error as unapplicable.
I deliberately made it explicit, but sure, I can adjust the example.
Agreed that this just looks like a bug that needs to be fixed.

Discussion:
}; } <del> fn should_abort_on_panic(tcx: TyCtxt<'_>, 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; } // Validate `#[unwind]` syntax regardless of platform-specific panic strategy </del> <ins> fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: DefId, _abi: Abi) -> bool { // Validate `#[unwind]` syntax regardless of platform-specific panic strategy. </ins> let attrs = &tcx.get_attrs(fn_def_id); let unwind_attr = attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs); <del> // We never unwind, so it's not relevant to stop an unwind </del> <ins> // We never unwind, so it's not relevant to stop an unwind. </ins> if tcx.sess.panic_strategy() != PanicStrategy::Unwind { return false; } <del> // We cannot add landing pads, so don't add one </del> <ins> // We cannot add landing pads, so don't add one. </ins> if tcx.sess.no_landing_pads() { 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 { <del> None => false, // FIXME(#58794) </del> <ins> None => false, // FIXME(#58794); should be `!(abi == Abi::Rust || abi == Abi::RustCall)` </ins> Some(UnwindAttr::Allowed) => false, Some(UnwindAttr::Aborts) => true, }

<del> // run-pass #![allow(unused_must_use)] #![feature(unwind_attributes)] // Since we mark some ABIs as "nounwind" to LLVM, we must make sure that // we never unwind through them. // ignore-cloudabi no env and process // ignore-emscripten no processes // ignore-sgx no processes use std::{env, panic}; use std::io::prelude::*; use std::io; use std::process::{Command, Stdio}; #[unwind(aborts)] // FIXME(#58794) extern "C" fn panic_in_ffi() { panic!("Test"); } fn test() { let _ = panic::catch_unwind(|| { panic_in_ffi(); }); // The process should have aborted by now. io::stdout().write(b"This should never be printed.n"); let _ = io::stdout().flush(); } fn main() { let args: Vec<String> = env::args().collect(); if args.len() > 1 && args[1] == "test" { return test(); } let mut p = Command::new(&args[0]) .stdout(Stdio::piped()) .stdin(Stdio::piped()) .arg("test").spawn().unwrap(); assert!(!p.wait().unwrap().success()); } </del>

<ins> // run-pass #![allow(unused_must_use)] #![feature(unwind_attributes)] // Since we mark some ABIs as "nounwind" to LLVM, we must make sure that // we never unwind through them. // ignore-cloudabi no env and process // ignore-emscripten no processes // ignore-sgx no processes use std::{env, panic}; use std::io::prelude::*; use std::io; use std::process::{Command, Stdio}; #[unwind(aborts)] // FIXME(#58794) should work even without the attribute extern "C" fn panic_in_ffi() { panic!("Test"); } #[unwind(aborts)] extern "Rust" fn panic_in_rust_abi() { panic!("TestRust"); } fn test() { let _ = panic::catch_unwind(|| { panic_in_ffi(); }); // The process should have aborted by now. io::stdout().write(b"This should never be printed.n"); let _ = io::stdout().flush(); } fn testrust() { let _ = panic::catch_unwind(|| { panic_in_rust_abi(); }); // The process should have aborted by now. io::stdout().write(b"This should never be printed.n"); let _ = io::stdout().flush(); } fn main() { let args: Vec<String> = env::args().collect(); if args.len() > 1 { // This is inside the self-executed command. match &*args[1] { "test" => return test(), "testrust" => return testrust(), _ => panic!("bad test"), } } // These end up calling the self-execution branches above. let mut p = Command::new(&args[0]) .stdout(Stdio::piped()) .stdin(Stdio::piped()) .arg("test").spawn().unwrap(); assert!(!p.wait().unwrap().success()); let mut p = Command::new(&args[0]) .stdout(Stdio::piped()) .stdin(Stdio::piped()) .arg("testrust").spawn().unwrap(); assert!(!p.wait().unwrap().success()); } </ins>