[clang] [CIR] Implement EH handling for field initializers (PR #192360)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 15 16:52:51 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Andy Kaylor (andykaylor)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/192360.diff
4 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenClass.cpp (+1-1)
- (modified) clang/lib/CIR/CodeGen/CIRGenDecl.cpp (+11)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+3)
- (added) clang/test/CIR/CodeGen/field-init-eh.cpp (+83)
``````````diff
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 } %{{.*}}
``````````
</details>
https://github.com/llvm/llvm-project/pull/192360
More information about the cfe-commits
mailing list