[clang] [CIR] Implement EH handling for field initializers (PR #192360)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 15 16:52:24 PDT 2026
https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/192360
This implements the handling to call the dtor for any previously initialized fields of destructed type if an exception is thrown later in the initialization of the containing class.
The basic infrastructure to handle this was already in place. We just needed a function to push an EH-only destroy cleanup on the EH stack and a call to that function.
>From 7f2d3e89e7b44b2f43d682dc245380ab77c8cfe6 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Wed, 15 Apr 2026 16:47:26 -0700
Subject: [PATCH] [CIR] Implement EH handling for field initializers
This implements the handling to call the dtor for any previously
initialized fields of destructed type if an exception is thrown
later in the initialization of the containing class.
The basic infrastructure to handle this was already in place. We
just needed a function to push an EH-only destroy cleanup on the
EH stack and a call to that function.
---
clang/lib/CIR/CodeGen/CIRGenClass.cpp | 2 +-
clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 11 ++++
clang/lib/CIR/CodeGen/CIRGenFunction.h | 3 +
clang/test/CIR/CodeGen/field-init-eh.cpp | 83 ++++++++++++++++++++++++
4 files changed, 98 insertions(+), 1 deletion(-)
create mode 100644 clang/test/CIR/CodeGen/field-init-eh.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 9c84fe574be45..a69326961c5e3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -620,7 +620,7 @@ void CIRGenFunction::emitInitializerForField(FieldDecl *field, LValue lhs,
// constructor.
QualType::DestructionKind dtorKind = fieldType.isDestructedType();
if (needsEHCleanup(dtorKind))
- cgm.errorNYI(init->getSourceRange(), "call field destructor");
+ pushEHDestroy(dtorKind, lhs.getAddress(), fieldType);
}
Address CIRGenFunction::emitCXXMemberDataPointerAddress(
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 7e9f6a8a230f3..1cd810520c27b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -1071,6 +1071,17 @@ void CIRGenFunction::pushIrregularPartialArrayCleanup(mlir::Value arrayBegin,
destroyer);
}
+/// pushEHDestroy - Push the standard destructor for the given type as
+/// an EH-only cleanup.
+void CIRGenFunction::pushEHDestroy(QualType::DestructionKind dtorKind,
+ Address addr, QualType type) {
+ assert(dtorKind && "cannot push destructor for trivial type");
+ assert(needsEHCleanup(dtorKind));
+
+ assert(!cir::MissingFeatures::useEHCleanupForArray());
+ pushDestroy(EHCleanup, addr, type, getDestroyer(dtorKind));
+}
+
/// Push the standard destructor for the given type as
/// at least a normal cleanup.
void CIRGenFunction::pushDestroy(QualType::DestructionKind dtorKind,
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 7f91cba115f04..dc6c64c99bac2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1395,6 +1395,9 @@ class CIRGenFunction : public CIRGenTypeCache {
static Destroyer destroyCXXObject;
+ void pushEHDestroy(QualType::DestructionKind dtorKind, Address addr,
+ QualType type);
+
void pushDestroy(QualType::DestructionKind dtorKind, Address addr,
QualType type);
diff --git a/clang/test/CIR/CodeGen/field-init-eh.cpp b/clang/test/CIR/CodeGen/field-init-eh.cpp
new file mode 100644
index 0000000000000..37065d5c952de
--- /dev/null
+++ b/clang/test/CIR/CodeGen/field-init-eh.cpp
@@ -0,0 +1,83 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+class Contained {
+public:
+ Contained(int);
+ ~Contained();
+};
+
+void mayThrow();
+
+class Container {
+public:
+ Container() : x(0), contained(1) { mayThrow(); }
+
+ int x;
+ Contained contained;
+};
+
+void test_field_initializer() {
+ Container c;
+}
+
+// CIR: cir.func {{.*}} @_ZN9ContainerC2Ev
+// CIR: %[[THIS:.*]] = cir.load %{{.*}}
+// CIR: %[[X_ADDR:.*]] = cir.get_member %[[THIS]][0] {name = "x"} : !cir.ptr<!rec_Container> -> !cir.ptr<!s32i>
+// CIR: %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CIR: cir.store align(4) %[[ZERO]], %[[X_ADDR]] : !s32i, !cir.ptr<!s32i>
+// CIR: %[[VOID_PTR_THIS:.*]] = cir.cast bitcast %[[THIS]] : !cir.ptr<!rec_Container> -> !cir.ptr<!u8i>
+// CIR: %[[FOUR:.*]] = cir.const #cir.int<4> : !u64i
+// CIR: %[[CONTAINED_ADDR_VOID:.*]] = cir.ptr_stride %[[VOID_PTR_THIS]], %[[FOUR]] : (!cir.ptr<!u8i>, !u64i) -> !cir.ptr<!u8i>
+// CIR: %[[CONTAINED_ADDR:.*]] = cir.cast bitcast %[[CONTAINED_ADDR_VOID]] : !cir.ptr<!u8i> -> !cir.ptr<!rec_Contained>
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
+// CIR: cir.call @_ZN9ContainedC1Ei(%[[CONTAINED_ADDR]], %[[ONE]])
+// CIR: cir.cleanup.scope {
+// CIR: cir.call @_Z8mayThrowv() : () -> ()
+// CIR: cir.yield
+// CIR: } cleanup eh {
+// CIR: cir.call @_ZN9ContainedD1Ev(%[[CONTAINED_ADDR]])
+// CIR: cir.yield
+// CIR: }
+
+// LLVM: define {{.*}} void @_ZN9ContainerC2Ev
+// LLVM: %[[THIS:.*]] = load ptr, ptr %{{.*}}
+// LLVM: %[[X_ADDR:.*]] = getelementptr %class.Container, ptr %[[THIS]], i32 0, i32 0
+// LLVM: store i32 0, ptr %[[X_ADDR]]
+// LLVM: %[[CONTAINED_ADDR:.*]] = getelementptr i8, ptr %[[THIS]], i64 4
+// LLVM: call void @_ZN9ContainedC1Ei(ptr {{.*}} %[[CONTAINED_ADDR]], i32 {{.*}} 1)
+// LLVM: invoke void @_Z8mayThrowv()
+// LLVM: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]]
+// LLVM: [[INVOKE_CONT:.*]]:
+// LLVM: br label %[[EXIT:.*]]
+// LLVM: [[LPAD:.*]]:
+// LLVM: %[[EXN:.*]] = landingpad { ptr, i32 }
+// LLVM: cleanup
+// LLVM: br label %[[EH_CLEANUP:.*]]
+// LLVM: [[EH_CLEANUP:.*]]:
+// LLVM: call void @_ZN9ContainedD1Ev(ptr {{.*}} %[[CONTAINED_ADDR]])
+// LLVM: resume { ptr, i32 } %{{.*}}
+// LLVM: [[EXIT:.*]]:
+// LLVM: ret void
+
+// OGCG: define {{.*}} void @_ZN9ContainerC2Ev
+// OGCG: %[[THIS:.*]] = load ptr, ptr %{{.*}}
+// OGCG: %[[X_ADDR:.*]] = getelementptr inbounds nuw %class.Container, ptr %[[THIS]], i32 0, i32 0
+// OGCG: store i32 0, ptr %[[X_ADDR]]
+// OGCG: %[[CONTAINED_ADDR:.*]] = getelementptr inbounds i8, ptr %[[THIS]], i64 4
+// OGCG: call void @_ZN9ContainedC1Ei(ptr {{.*}} %[[CONTAINED_ADDR]], i32 {{.*}} 1)
+// OGCG: invoke void @_Z8mayThrowv()
+// OGCG: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]]
+// OGCG: [[INVOKE_CONT:.*]]:
+// OGCG: ret void
+// OGCG: [[LPAD:.*]]:
+// OGCG: %[[EXN:.*]] = landingpad { ptr, i32 }
+// OGCG: cleanup
+// OGCG: call void @_ZN9ContainedD1Ev(ptr {{.*}} %[[CONTAINED_ADDR]])
+// OGCG: br label %[[EH_RESUME:.*]]
+// OGCG: [[EH_RESUME:.*]]:
+// OGCG: resume { ptr, i32 } %{{.*}}
More information about the cfe-commits
mailing list