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
