[clang] [CIR] Implement EH handling for field initializers (PR #192360)

Andy Kaylor via cfe-commits cfe-commits at lists.llvm.org
Thu Apr 16 10:50:11 PDT 2026


https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/192360

>From 8a83392d53edf4fe80a9f6d355b1d9da2e395daf 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 1/2] [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 f195b43ab1e57..3363548058ee2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -621,7 +621,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 } %{{.*}}

>From 2a4876c15ff27f7c0edfbe805bbd118b8c793e0c Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Thu, 16 Apr 2026 09:34:48 -0700
Subject: [PATCH 2/2] Address review feedback

---
 clang/lib/CIR/CodeGen/CIRGenClass.cpp  |  3 +--
 clang/lib/CIR/CodeGen/CIRGenDecl.cpp   | 12 ++++++------
 clang/lib/CIR/CodeGen/CIRGenFunction.h |  4 ++--
 3 files changed, 9 insertions(+), 10 deletions(-)

diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 3363548058ee2..5f9fdfba9d31b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -620,8 +620,7 @@ void CIRGenFunction::emitInitializerForField(FieldDecl *field, LValue lhs,
   // Ensure that we destroy this object if an exception is thrown later in the
   // constructor.
   QualType::DestructionKind dtorKind = fieldType.isDestructedType();
-  if (needsEHCleanup(dtorKind))
-    pushEHDestroy(dtorKind, lhs.getAddress(), fieldType);
+  pushEHDestroyIfNeeded(dtorKind, lhs.getAddress(), fieldType);
 }
 
 Address CIRGenFunction::emitCXXMemberDataPointerAddress(
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 1cd810520c27b..be86d34545a2c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -1071,12 +1071,12 @@ 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));
+/// pushEHDestroyIfNeeded - Push the standard destructor for the given type as
+/// an EH-only cleanup. If EH cleanup is not needed, just return.
+void CIRGenFunction::pushEHDestroyIfNeeded(QualType::DestructionKind dtorKind,
+                                           Address addr, QualType type) {
+  if (!needsEHCleanup(dtorKind))
+    return;
 
   assert(!cir::MissingFeatures::useEHCleanupForArray());
   pushDestroy(EHCleanup, addr, type, getDestroyer(dtorKind));
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index dc6c64c99bac2..e2729e9cfd8d6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1395,8 +1395,8 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   static Destroyer destroyCXXObject;
 
-  void pushEHDestroy(QualType::DestructionKind dtorKind, Address addr,
-                     QualType type);
+  void pushEHDestroyIfNeeded(QualType::DestructionKind dtorKind, Address addr,
+                             QualType type);
 
   void pushDestroy(QualType::DestructionKind dtorKind, Address addr,
                    QualType type);



More information about the cfe-commits mailing list