Author: Andy Kaylor
Date: 2026-03-31T11:04:55-07:00
New Revision: ccfba7736f2d39434ae768ae9c9c0165b1959c4b

URL: 
https://github.com/llvm/llvm-project/commit/ccfba7736f2d39434ae768ae9c9c0165b1959c4b
DIFF: 
https://github.com/llvm/llvm-project/commit/ccfba7736f2d39434ae768ae9c9c0165b1959c4b.diff

LOG: [CIR] Add support for cleanups after calling a delegating ctor (#189513)

This adds support for calling the destructor of types with a non-trivial
destructor if the initialization throws an exception after a delegating
constructor has been called.

Added: 
    clang/test/CIR/CodeGen/delegating-ctor-exceptions.cpp

Modified: 
    clang/lib/CIR/CodeGen/CIRGenClass.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp 
b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
index 107ae3a03ae08..3eead9a62317a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp
@@ -185,6 +185,25 @@ struct CallBaseDtor final : EHScopeStack::Cleanup {
   }
 };
 
+/// If the delegating constructor's body throws after the delegated-to
+/// constructor completes, destroy the object (mirrors CGClass.cpp's
+/// CallDelegatingCtorDtor).
+struct CallDelegatingCtorDtor final : EHScopeStack::Cleanup {
+  const CXXDestructorDecl *dtor;
+  Address addr;
+  CXXDtorType type;
+
+  CallDelegatingCtorDtor(const CXXDestructorDecl *dtor, Address addr,
+                         CXXDtorType type)
+      : dtor(dtor), addr(addr), type(type) {}
+
+  void emit(CIRGenFunction &cgf, Flags flags) override {
+    QualType thisTy = dtor->getFunctionObjectParameterType();
+    cgf.emitCXXDestructorCall(dtor, type, /*forVirtualBase=*/false,
+                              /*delegating=*/true, addr, thisTy);
+  }
+};
+
 /// A visitor which checks whether an initializer uses 'this' in a
 /// way which requires the vtable to be properly set.
 struct DynamicThisUseChecker
@@ -1110,9 +1129,10 @@ void CIRGenFunction::emitDelegatingCXXConstructorCall(
 
   const CXXRecordDecl *classDecl = ctor->getParent();
   if (cgm.getLangOpts().Exceptions && !classDecl->hasTrivialDestructor()) {
-    cgm.errorNYI(ctor->getSourceRange(),
-                 "emitDelegatingCXXConstructorCall: exception");
-    return;
+    CXXDtorType dtorType =
+        curGD.getCtorType() == Ctor_Complete ? Dtor_Complete : Dtor_Base;
+    ehStack.pushCleanup<CallDelegatingCtorDtor>(
+        EHCleanup, classDecl->getDestructor(), thisPtr, dtorType);
   }
 }
 

diff  --git a/clang/test/CIR/CodeGen/delegating-ctor-exceptions.cpp 
b/clang/test/CIR/CodeGen/delegating-ctor-exceptions.cpp
new file mode 100644
index 0000000000000..d6c35272eb9f0
--- /dev/null
+++ b/clang/test/CIR/CodeGen/delegating-ctor-exceptions.cpp
@@ -0,0 +1,98 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 
-fcxx-exceptions -fexceptions \
+// RUN:     -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck -check-prefixes=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 
-fcxx-exceptions -fexceptions \
+// RUN:     -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck -check-prefixes=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 
-fcxx-exceptions -fexceptions \
+// RUN:     -emit-llvm %s -o %t.ll
+// RUN: FileCheck -check-prefixes=OGCG --input-file=%t.ll %s
+
+// Delegating constructor with a non-trivial destructor and C++ exceptions 
enabled
+// exercises CIRGenFunction::emitDelegatingCXXConstructorCall's NYI for EH 
cleanup
+// around the delegated subobject initialization.
+
+void mayThrow();
+
+struct HasDtor {
+  ~HasDtor() {}
+  // expected-error@+1 {{ClangIR code gen Not Yet Implemented: 
emitDelegatingCXXConstructorCall: exception}}
+  HasDtor() : HasDtor(0) { mayThrow(); }
+  HasDtor(int);
+};
+
+HasDtor::HasDtor(int) {}
+
+void force_default_ctor() { HasDtor x; }
+
+// CIR: cir.func {{.*}} @_ZN7HasDtorC1Ev(%[[ARG0:.*]]: !cir.ptr<!rec_HasDtor>
+// CIR:   %[[THIS_ADDR:.*]] = cir.alloca !cir.ptr<!rec_HasDtor>, 
!cir.ptr<!cir.ptr<!rec_HasDtor>>, ["this", init]
+// CIR:   cir.store %[[ARG0]], %[[THIS_ADDR]]
+// CIR:   %[[THIS:.*]] = cir.load %[[THIS_ADDR]] : 
!cir.ptr<!cir.ptr<!rec_HasDtor>>, !cir.ptr<!rec_HasDtor>
+// CIR:   %[[ZERO:.*]] = cir.const #cir.int<0> : !s32i
+// CIR:   cir.call @_ZN7HasDtorC1Ei(%[[THIS]], %[[ZERO]]) : 
(!cir.ptr<!rec_HasDtor> {{.*}}, !s32i {{.*}}) -> ()
+// CIR:   cir.cleanup.scope {
+// CIR:     cir.call @_Z8mayThrowv() : () -> ()
+// CIR:     cir.yield
+// CIR:   } cleanup eh {
+// CIR:     cir.call @_ZN7HasDtorD1Ev(%[[THIS]]) nothrow : 
(!cir.ptr<!rec_HasDtor> {{.*}}) -> ()
+// CIR:     cir.yield
+// CIR:   }
+// CIR:   cir.return
+// CIR: }
+
+// LLVM: define {{.*}} void @_ZN7HasDtorC1Ev(ptr {{.*}} %[[ARG0:.*]])
+// LLVM:   %[[THIS_ADDR:.*]] = alloca ptr
+// LLVM:   store ptr %[[ARG0]], ptr %[[THIS_ADDR]]
+// LLVM:   %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// LLVM:   call void @_ZN7HasDtorC1Ei(ptr{{.*}} %[[THIS]], i32{{.*}} 0)
+// LLVM:   br label %[[CLEANUP_SCOPE:.*]]
+// LLVM: [[CLEANUP_SCOPE]]:
+// LLVM:   invoke void @_Z8mayThrowv()
+// LLVM:     to label %[[INVOKE_CONT:.*]] unwind label %[[INVOKE_UNWIND:.*]]
+// LLVM: [[INVOKE_CONT]]:
+// LLVM:   br label %[[EXIT_CLEANUP_SCOPE:.*]]
+// LLVM: [[INVOKE_UNWIND]]:
+// LLVM:   %[[LANDING_PAD:.*]] = landingpad { ptr, i32 }
+// LLVM:     cleanup
+// LLVM:   %[[EXN_PTR:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 0
+// LLVM:   %[[EXN_SEL:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 1
+// LLVM:   br label %[[EH_CLEANUP:.*]]
+// LLVM: [[EH_CLEANUP]]:
+// LLVM:   %[[EXN_PTR_PHI:.*]] = phi ptr [ %[[EXN_PTR]], %[[INVOKE_UNWIND]] ]
+// LLVM:   %[[EXN_SEL_PHI:.*]] = phi i32 [ %[[EXN_SEL]], %[[INVOKE_UNWIND]] ]
+// LLVM:   call void @_ZN7HasDtorD1Ev(ptr{{.*}} %[[THIS]])
+// LLVM:   %[[EXN_PARTIAL:.*]] = insertvalue { ptr, i32 } poison, ptr 
%[[EXN_PTR_PHI]], 0
+// LLVM:   %[[EXN:.*]] = insertvalue { ptr, i32 } %[[EXN_PARTIAL]], i32 
%[[EXN_SEL_PHI]], 1
+// LLVM:   resume { ptr, i32 } %[[EXN]]
+// LLVM: [[EXIT_CLEANUP_SCOPE]]:
+// LLVM:   ret void
+// LLVM: }
+
+// OGCG: define {{.*}} void @_ZN7HasDtorC1Ev(ptr {{.*}} %[[ARG0:.*]])
+// OGCG:   %[[THIS_ADDR:.*]] = alloca ptr
+// OGCG:   %[[EXN_SLOT:.*]] = alloca ptr
+// OGCG:   %[[EHSELECTOR_SLOT:.*]] = alloca i32
+// OGCG:   store ptr %[[ARG0]], ptr %[[THIS_ADDR]]
+// OGCG:   %[[THIS:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// OGCG:   call void @_ZN7HasDtorC1Ei(ptr{{.*}} %[[THIS]], i32{{.*}} 0)
+// OGCG:   invoke void @_Z8mayThrowv()
+// OGCG:     to label %[[INVOKE_CONT:.*]] unwind label %[[INVOKE_UNWIND:.*]]
+// OGCG: [[INVOKE_CONT]]:
+// OGCG:   ret void
+// OGCG: [[INVOKE_UNWIND]]:
+// OGCG:   %[[LANDING_PAD:.*]] = landingpad { ptr, i32 }
+// OGCG:     cleanup
+// OGCG:   %[[EXN_PTR:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 0
+// OGCG:   store ptr %[[EXN_PTR]], ptr %[[EXN_SLOT]]
+// OGCG:   %[[EXN_INT:.*]] = extractvalue { ptr, i32 } %[[LANDING_PAD]], 1
+// OGCG:   store i32 %[[EXN_INT]], ptr %[[EHSELECTOR_SLOT]]
+// OGCG:   call void @_ZN7HasDtorD1Ev(ptr{{.*}} %[[THIS]])
+// OGCG:   br label %[[EH_RESUME:.*]]
+// OGCG: [[EH_RESUME]]:
+// OGCG:   %[[EXN_PTR2:.*]] = load ptr, ptr %[[EXN_SLOT]]
+// OGCG:   %[[EHSELECTOR:.*]] = load i32, ptr %[[EHSELECTOR_SLOT]]
+// OGCG:   %[[EXN_PARTIAL:.*]] = insertvalue { ptr, i32 } poison, ptr 
%[[EXN_PTR2]], 0
+// OGCG:   %[[EXN:.*]] = insertvalue { ptr, i32 } %[[EXN_PARTIAL]], i32 
%[[EHSELECTOR]], 1
+// OGCG:   resume { ptr, i32 } %[[EXN]]
+// OGCG: }


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to