[clang] [ObjC][SEH] Fix clang crash when using finally statements (PR #176779)
Eli Friedman via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 28 13:53:56 PDT 2026
https://github.com/efriedma-quic updated https://github.com/llvm/llvm-project/pull/176779
>From 07877a6890340f3adeb4e963ca7f868df64151f2 Mon Sep 17 00:00:00 2001
From: hmelder <service at hugomelder.com>
Date: Mon, 19 Jan 2026 13:57:33 +0000
Subject: [PATCH 1/2] [ObjC][SEH] Push cleanup before catch handlers
The cleanup funclet for an `@finally` statement needs to be pushed onto
EHStack before the catch handlers are pushed.
---
clang/lib/CodeGen/CGObjCRuntime.cpp | 29 +++++++++++++++++++++++++++--
1 file changed, 27 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CodeGen/CGObjCRuntime.cpp b/clang/lib/CodeGen/CGObjCRuntime.cpp
index 099622f690678..3935987144756 100644
--- a/clang/lib/CodeGen/CGObjCRuntime.cpp
+++ b/clang/lib/CodeGen/CGObjCRuntime.cpp
@@ -179,6 +179,26 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
}
}
+ if (useFunclets)
+ if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) {
+ CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true);
+ if (!CGF.CurSEHParent)
+ CGF.CurSEHParent = cast<NamedDecl>(CGF.CurFuncDecl);
+ // Outline the finally block.
+ const Stmt *FinallyBlock = Finally->getFinallyBody();
+ HelperCGF.startOutlinedSEHHelper(CGF, /*isFilter*/ false, FinallyBlock);
+
+ // Emit the original filter expression, convert to i32, and return.
+ HelperCGF.EmitStmt(FinallyBlock);
+
+ HelperCGF.FinishFunction(FinallyBlock->getEndLoc());
+
+ llvm::Function *FinallyFunc = HelperCGF.CurFn;
+
+ // Push a cleanup for __finally blocks.
+ CGF.pushSEHCleanup(NormalAndEHCleanup, FinallyFunc);
+ }
+
SmallVector<CatchHandler, 8> Handlers;
// Enter the catch, if there is one.
@@ -312,8 +332,13 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,
CGF.Builder.restoreIP(SavedIP);
// Pop out of the finally.
- if (!useFunclets && S.getFinallyStmt())
- FinallyInfo.exit(CGF);
+ if (S.getFinallyStmt()) {
+ if (useFunclets) {
+ CGF.PopCleanupBlock();
+ } else {
+ FinallyInfo.exit(CGF);
+ }
+ }
if (Cont.isValid())
CGF.EmitBlock(Cont.getBlock());
>From f406cbbc8b02563ea7b17a283f0f5bb0f2f90e4f Mon Sep 17 00:00:00 2001
From: hmelder <service at hugomelder.com>
Date: Mon, 19 Jan 2026 15:27:04 +0000
Subject: [PATCH 2/2] [ObjC][SEH] Add regression test for finally cleanup
---
clang/test/CodeGenObjC/exceptions-seh.m | 53 +++++++++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 clang/test/CodeGenObjC/exceptions-seh.m
diff --git a/clang/test/CodeGenObjC/exceptions-seh.m b/clang/test/CodeGenObjC/exceptions-seh.m
new file mode 100644
index 0000000000000..326eef0da3a78
--- /dev/null
+++ b/clang/test/CodeGenObjC/exceptions-seh.m
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -triple aarch64-pc-windows -emit-llvm -fexceptions -fobjc-exceptions -fobjc-runtime=gnustep-2.2 -o - %s | FileCheck %s
+
+void may_throw(void);
+void puts(const char *);
+
+int main(void) {
+ @try {
+ may_throw();
+ // CHECK: invoke void @may_throw()
+ // CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[CATCH_DISPATCH:.*]]
+ }
+
+ // Check that the dispatch block has been emitted correctly. We capture the
+ // normal and unwind edge for later checks.
+ // CHECK: [[CATCH_DISPATCH]]:
+ // CHECK-NEXT: %[[CATCHSWITCH_OUTER:.*]] = catchswitch within none [label %[[CATCH_A:.*]], label %[[CATCH_B:.*]]] unwind label %[[EH_CLEANUP_OUTER_FINALLY:.*]]
+
+ @catch(id a) {
+ // CHECK: %[[CATCHPAD_A:.*]] = catchpad within %[[CATCHSWITCH_OUTER]]
+ puts("catch");
+ @try {
+ may_throw();
+ // CHECK: invoke void @may_throw() [ "funclet"(token %{{.*}}) ]
+ // CHECK-NEXT: to label %[[INVOKE_CONT_INNER:.*]] unwind label %[[CATCH_DISPATCH_INNER:.*]]
+
+ // CHECK: [[CATCH_DISPATCH_INNER]]:
+ // CHECK-NEXT: %{{.*}} = catchswitch within %[[CATCHPAD_A]] [label %[[CATCHPAD_A_INNER:.*]]] unwind label %[[EH_CLEANUP_INNER_FINALLY:.*]]
+ } @catch(...) {
+ // CHECK: [[CATCHPAD_A_INNER]]:
+ // CHECK: to label %invoke.cont{{[0-9]+}} unwind label %[[EH_CLEANUP_INNER_FINALLY]]
+ puts("inner catch all");
+ } @finally {
+ // CHECK: [[EH_CLEANUP_INNER_FINALLY]]:
+ // CHECK: to label %invoke.cont{{[0-9]+}} unwind label %[[EH_CLEANUP_OUTER_FINALLY]]
+ puts("inner finally");
+ }
+ return 42;
+ }
+
+ @catch(id b) {
+ // CHECK: %[[CATCHPAD_B:.*]] = catchpad within %[[CATCHSWITCH_OUTER]]
+ // CHECK: to label %invoke.cont{{[0-9]+}} unwind label %[[EH_CLEANUP_OUTER_FINALLY]]
+ puts("catch 2");
+ return 43;
+ }
+
+ // Check that the cleanuppad from the SEH finally funclet was correctly emitted.
+ // CHECK: [[EH_CLEANUP_OUTER_FINALLY]]:
+ @finally {
+ puts("cleanup");
+ }
+ return 0;
+}
More information about the cfe-commits
mailing list