Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/intrinsic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ mod simd;
#[cfg(feature = "master")]
use std::iter;

#[cfg(feature = "master")]
use gccjit::FnAttribute;
use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, Type, UnaryOp};
use rustc_abi::{Align, BackendRepr, HasDataLayout, WrappingRange};
use rustc_codegen_ssa::base::wants_msvc_seh;
Expand Down Expand Up @@ -1489,6 +1491,14 @@ fn get_rust_try_fn<'a, 'gcc, 'tcx>(
rustc_hir::Safety::Unsafe,
));
let rust_try = gen_fn(cx, "__rust_try", rust_fn_sig, codegen);
// NOTE: The `__rust_try` shim must not be inlined into its caller. As explained on
// `codegen_gnu_try` above, the shim exists precisely so that its try/catch has the correct
// (single) personality function. When GCC inlines the shim into the caller under
// optimization (-O2/-O3), the try/catch landing pad is degraded into a
// cleanup-and-`_Unwind_Resume`: the exception is re-raised instead of being caught, so
// `catch_unwind` silently stops catching in release mode. Keeping the shim out-of-line
// preserves the catch semantics.
rust_try.1.add_attribute(FnAttribute::NoInline);
cx.rust_try_fn.set(Some(rust_try));
rust_try
}
Expand Down
36 changes: 36 additions & 0 deletions tests/run/catch_unwind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Compiler:
//
// Run-time:
// status: 0
// stdout: Caught

// Regression test for a release-mode (-O3) unwinding bug: `catch_unwind` must
// still catch a panic when the panicking closure is inlined across the catch
// boundary. The `#[inline]` `call_once` below forces that inlining.
//
// Previously, GCC inlined the out-of-line `__rust_try` shim into its caller
// under optimization, which degraded the try/catch landing pad into a plain
// `_Unwind_Resume`. The panic was then re-raised instead of caught, escaped
// `main`, and the process exited with code 101 (in release only; debug and the
// LLVM backend were unaffected). See `get_rust_try_fn` in `src/intrinsic/mod.rs`,
// which now marks `__rust_try` as `NoInline`.

#![feature(fn_traits, unboxed_closures)]

struct Wrapper<A>(A);

impl<R, F: FnOnce() -> R> FnOnce<()> for Wrapper<F> {
type Output = R;

#[inline]
extern "rust-call" fn call_once(self, _args: ()) -> R {
(self.0)()
}
}

fn main() {
std::panic::set_hook(Box::new(|_| {}));
let result = std::panic::catch_unwind(Wrapper(|| panic!()));
assert!(result.is_err());
println!("Caught");
}
Loading