[clang] [CIR] Fix handling of cleanup scopes inside a try body (PR #183869)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 27 16:01:56 PST 2026
https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/183869
We had a problem where scope terminators were not being created correctly when a cleanup scope appeared inside the body of a try operation. This was caused by cleanup scope operation not being properly flushed before the try body scope was completed.
This change fixes the problem by creating a RunCleanupsScope in the lambda that pupulates the try body and forcing cleanups before adding the yield terminator to the try body.
The test case also exposed a secondary bug where we were not properly updating the innermostEHScope variable when popping a cleanup from the eh stack. That is also fixed here.
>From 82e1717ec22a20f7ac95171e95ece8a5de55ce1c Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 27 Feb 2026 15:24:33 -0800
Subject: [PATCH] [CIR] Fix handling of cleanup scopes inside a try body
We had a problem where scope terminators were not being created correctly
when a cleanup scope appeared inside the body of a try operation. This
was caused by cleanup scope operation not being properly flushed before
the try body scope was completed.
This change fixes the problem by creating a RunCleanupsScope in the
lambda that pupulates the try body and forcing cleanups before adding the
yield terminator to the try body.
The test case also exposed a secondary bug where we were not properly
updating the innermostEHScope variable when popping a cleanup from the
eh stack. That is also fixed here.
---
clang/lib/CIR/CodeGen/CIRGenCleanup.cpp | 1 +
clang/lib/CIR/CodeGen/CIRGenException.cpp | 5 ++
clang/test/CIR/CodeGen/try-catch-tmp.cpp | 60 +++++++++++++++++++++++
3 files changed, 66 insertions(+)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
index c9806de61acc6..44b8ff7d4ad63 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp
@@ -160,6 +160,7 @@ void EHScopeStack::popCleanup() {
assert(isa<EHCleanupScope>(*begin()));
EHCleanupScope &cleanup = cast<EHCleanupScope>(*begin());
innermostNormalCleanup = cleanup.getEnclosingNormalCleanup();
+ innermostEHScope = cleanup.getEnclosingEHScope();
deallocate(cleanup.getAllocatedSize());
cir::CleanupScopeOp cleanupScope = cleanup.getCleanupScopeOp();
diff --git a/clang/lib/CIR/CodeGen/CIRGenException.cpp b/clang/lib/CIR/CodeGen/CIRGenException.cpp
index 94b0e251a1420..be90e19d03c76 100644
--- a/clang/lib/CIR/CodeGen/CIRGenException.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenException.cpp
@@ -322,8 +322,13 @@ mlir::LogicalResult CIRGenFunction::emitCXXTryStmt(const CXXTryStmt &s) {
builder, tryLoc,
/*tryBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
+ // Create a RunCleanupsScope that allows us to apply any cleanups that
+ // are created for statements within the try body before exiting the
+ // try body.
+ RunCleanupsScope tryBodyCleanups(*this);
if (emitStmt(s.getTryBlock(), /*useCurrentScope=*/true).failed())
tryRes = mlir::failure();
+ tryBodyCleanups.forceCleanup();
cir::YieldOp::create(builder, loc);
},
/*handlersBuilder=*/
diff --git a/clang/test/CIR/CodeGen/try-catch-tmp.cpp b/clang/test/CIR/CodeGen/try-catch-tmp.cpp
index 02f0df6d8753d..5cb60aaf6b2cc 100644
--- a/clang/test/CIR/CodeGen/try-catch-tmp.cpp
+++ b/clang/test/CIR/CodeGen/try-catch-tmp.cpp
@@ -327,3 +327,63 @@ void call_function_inside_try_catch_with_exception_type_and_catch_all() {
// OGCG: %[[BEGIN_CATCH:.*]] = call ptr @__cxa_begin_catch(ptr %[[TMP_EXCEPTION]])
// OGCG: call void @__cxa_end_catch()
// OGCG: br label %[[TRY_CONT]]
+
+struct S {
+ ~S();
+};
+
+void cleanup_inside_try_body() {
+ try {
+ S s;
+ division();
+ } catch (...) {
+ }
+}
+
+// CIR: cir.func {{.*}} @_Z23cleanup_inside_try_bodyv(){{.*}} personality(@__gxx_personality_v0) {
+// CIR: cir.scope {
+// CIR: %[[S:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["s"]
+// CIR: cir.try {
+// CIR: cir.cleanup.scope {
+// CIR: cir.call @_Z8divisionv()
+// CIR: cir.yield
+// CIR: } cleanup all {
+// CIR: cir.call @_ZN1SD1Ev(%[[S]])
+// CIR: cir.yield
+// CIR: }
+// CIR: cir.yield
+// CIR: } catch all (%[[TOKEN:.*]]: !cir.eh_token {{.*}}) {
+// CIR: %[[CATCH_TOKEN:.*]], %[[EXN_PTR:.*]] = cir.begin_catch %[[TOKEN]] : !cir.eh_token -> (!cir.catch_token, !cir.ptr<!void>)
+// CIR: cir.cleanup.scope {
+// CIR: cir.yield
+// CIR: } cleanup all {
+// CIR: cir.end_catch %[[CATCH_TOKEN]] : !cir.catch_token
+// CIR: cir.yield
+// CIR: }
+// CIR: cir.yield
+// CIR: }
+// CIR: }
+
+// OGCG: define {{.*}} void @_Z23cleanup_inside_try_bodyv() {{.*}} personality ptr @__gxx_personality_v0 {
+// OGCG: %[[S:.*]] = alloca %struct.S
+// OGCG: %[[EXN_SLOT:.*]] = alloca ptr
+// OGCG: %[[EHSELECTOR_SLOT:.*]] = alloca i32
+// OGCG: %[[CALL:.*]] = invoke {{.*}} i32 @_Z8divisionv()
+// OGCG: to label %[[INVOKE_CONT:.*]] unwind label %[[LANDING_PAD:.*]]
+// OGCG: [[INVOKE_CONT]]:
+// OGCG: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) %[[S]])
+// OGCG: br label %[[TRY_CONT:.*]]
+// OGCG: [[LANDING_PAD]]:
+// OGCG: %[[LANDING_PAD:.*]] = landingpad { ptr, i32 }
+// OGCG: catch ptr null
+// OGCG: %[[EXCEPTION:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 0
+// OGCG: store ptr %[[EXCEPTION]], ptr %[[EXN_SLOT]]
+// OGCG: %[[EH_TYPE_ID:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 1
+// OGCG: store i32 %[[EH_TYPE_ID]], ptr %[[EHSELECTOR_SLOT]]
+// OGCG: call void @_ZN1SD1Ev(ptr noundef nonnull align 1 dereferenceable(1) %[[S]])
+// OGCG: br label %[[CATCH:.*]]
+// OGCG: [[CATCH]]:
+// OGCG: %[[EXCEPTION:.*]] = load ptr, ptr %[[EXN_SLOT]]
+// OGCG: %[[BEGIN_CATCH:.*]] = call ptr @__cxa_begin_catch(ptr %[[EXCEPTION]])
+// OGCG: call void @__cxa_end_catch()
+// OGCG: br label %[[TRY_CONT]]
More information about the cfe-commits
mailing list