https://github.com/mmha updated https://github.com/llvm/llvm-project/pull/144719
>From 856aceca89e83604933756e30dfaa24021b604fc Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Wed, 18 Jun 2025 15:09:21 +0100 Subject: [PATCH 1/3] [CIR] Add Minimal Destructor Definition Support This patch upstreams support for writing inline and out of line C++ destructor definitions. Calling a destructor implcitly or explicitly is left for a future patch. Because of that restriction complete destructors (D2 in Itanium mangling) do not call into the base (D1) destructors yet but simply behave like a base destructor. Deleting (D0) destructor support is not part of this patch. Destructor aliases aren't supported, either. Because of this compilation with -mno-constructor-aliases may be required to avoid running into NYI errors. --- clang/include/clang/CIR/MissingFeatures.h | 4 + clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp | 7 ++ clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 13 +++ clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 100 +++++++++++++++++- clang/lib/CIR/CodeGen/CIRGenFunction.h | 1 + clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 41 +++++-- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 28 +++-- clang/test/CIR/CodeGen/destructors.cpp | 50 +++++++++ 8 files changed, 228 insertions(+), 16 deletions(-) create mode 100644 clang/test/CIR/CodeGen/destructors.cpp diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index c76737549a0fc..48e309063d38b 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -250,6 +250,10 @@ struct MissingFeatures { static bool typeChecks() { return false; } static bool weakRefReference() { return false; } static bool writebacks() { return false; } + static bool appleKext() { return false; } + static bool dtorCleanups() { return false; } + static bool completeDtors() { return false; } + static bool vtableInitialization() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp index 6cf4e5c658fb6..33b812ac81f6e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.cpp @@ -41,6 +41,13 @@ void CIRGenCXXABI::buildThisParam(CIRGenFunction &cgf, assert(!cir::MissingFeatures::cxxabiThisAlignment()); } +cir::GlobalLinkageKind CIRGenCXXABI::getCXXDestructorLinkage( + GVALinkage linkage, const CXXDestructorDecl *dtor, CXXDtorType dt) const { + // Delegate back to cgm by default. + return cgm.getCIRLinkageForDeclarator(dtor, linkage, + /*isConstantVariable=*/false); +} + mlir::Value CIRGenCXXABI::loadIncomingCXXThis(CIRGenFunction &cgf) { ImplicitParamDecl *vd = getThisDecl(cgf); Address addr = cgf.getAddrOfLocalVar(vd); diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 2d967fd307e01..eb079b877b7ff 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -72,6 +72,19 @@ class CIRGenCXXABI { /// Emit constructor variants required by this ABI. virtual void emitCXXConstructors(const clang::CXXConstructorDecl *d) = 0; + /// Emit dtor variants required by this ABI. + virtual void emitCXXDestructors(const clang::CXXDestructorDecl *d) = 0; + + /// Returns true if the given destructor type should be emitted as a linkonce + /// delegating thunk, regardless of whether the dtor is defined in this TU or + /// not. + virtual bool useThunkForDtorVariant(const CXXDestructorDecl *dtor, + CXXDtorType dt) const = 0; + + virtual cir::GlobalLinkageKind + getCXXDestructorLinkage(GVALinkage linkage, const CXXDestructorDecl *dtor, + CXXDtorType dt) const; + /// Returns true if the given constructor or destructor is one of the kinds /// that the ABI says returns 'this' (only applies when called non-virtually /// for destructors). diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index c4efabd6b12ab..73df9f7f97ce8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -463,7 +463,7 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin()); if (isa<CXXDestructorDecl>(funcDecl)) { - getCIRGenModule().errorNYI(bodyRange, "C++ destructor definition"); + emitDestructorBody(args); } else if (isa<CXXConstructorDecl>(funcDecl)) { emitConstructorBody(args); } else if (getLangOpts().CUDA && !getLangOpts().CUDAIsDevice && @@ -540,6 +540,104 @@ void CIRGenFunction::emitConstructorBody(FunctionArgList &args) { } } +/// Emits the body of the current destructor. +void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { + const CXXDestructorDecl *dtor = cast<CXXDestructorDecl>(curGD.getDecl()); + CXXDtorType dtorType = curGD.getDtorType(); + + // For an abstract class, non-base destructors are never used (and can't + // be emitted in general, because vbase dtors may not have been validated + // by Sema), but the Itanium ABI doesn't make them optional and Clang may + // in fact emit references to them from other compilations, so emit them + // as functions containing a trap instruction. + if (dtorType != Dtor_Base && dtor->getParent()->isAbstract()) { + SourceLocation loc = + dtor->hasBody() ? dtor->getBody()->getBeginLoc() : dtor->getLocation(); + builder.create<cir::TrapOp>(getLoc(loc)); + // The corresponding clang/CodeGen logic clears the insertion point here, + // but MLIR's builder requires a valid insertion point, so we create a dummy + // block (since the trap is a block terminator). + builder.createBlock(builder.getBlock()->getParent()); + return; + } + + Stmt *body = dtor->getBody(); + if (body) + assert(!cir::MissingFeatures::incrementProfileCounter()); + + // The call to operator delete in a deleting destructor happens + // outside of the function-try-block, which means it's always + // possible to delegate the destructor body to the complete + // destructor. Do so. + if (dtorType == Dtor_Deleting) { + cgm.errorNYI("deleting destructor"); + return; + } + + // If the body is a function-try-block, enter the try before + // anything else. + const bool isTryBody = isa_and_nonnull<CXXTryStmt>(body); + if (isTryBody) { + cgm.errorNYI("function-try-block destructor"); + } + + assert(!cir::MissingFeatures::sanitizers()); + assert(!cir::MissingFeatures::dtorCleanups()); + + // If this is the complete variant, just invoke the base variant; + // the epilogue will destruct the virtual bases. But we can't do + // this optimization if the body is a function-try-block, because + // we'd introduce *two* handler blocks. In the Microsoft ABI, we + // always delegate because we might not have a definition in this TU. + switch (dtorType) { + case Dtor_Comdat: + llvm_unreachable("not expecting a COMDAT"); + case Dtor_Deleting: + llvm_unreachable("already handled deleting case"); + + case Dtor_Complete: + assert((body || getTarget().getCXXABI().isMicrosoft()) && + "can't emit a dtor without a body for non-Microsoft ABIs"); + + assert(!cir::MissingFeatures::dtorCleanups()); + + // TODO(cir): A complete destructor is supposed to call the base destructor. + // Since we have to emit both dtor kinds we just fall through for now and. + // As long as we don't support virtual bases this should be functionally + // equivalent. + assert(!cir::MissingFeatures::completeDtors()); + + // Fallthrough: act like we're in the base variant. + [[fallthrough]]; + + case Dtor_Base: + assert(body); + + assert(!cir::MissingFeatures::dtorCleanups()); + assert(!cir::MissingFeatures::vtableInitialization()); + + if (isTryBody) { + cgm.errorNYI("function-try-block destructor"); + } else if (body) { + (void)emitStmt(body, /*useCurrentScope=*/true); + } else { + assert(dtor->isImplicit() && "bodyless dtor not implicit"); + // nothing to do besides what's in the epilogue + } + // -fapple-kext must inline any call to this dtor into + // the caller's body. + assert(!cir::MissingFeatures::appleKext()); + + break; + } + + assert(!cir::MissingFeatures::dtorCleanups()); + + // Exit the try if applicable. + if (isTryBody) + cgm.errorNYI("function-try-block destructor"); +} + /// Given a value of type T* that may not be to a complete object, construct /// an l-vlaue withi the natural pointee alignment of T. LValue CIRGenFunction::makeNaturalAlignPointeeAddrLValue(mlir::Value val, diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 5feb5fc94d983..1346333739bc1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -830,6 +830,7 @@ class CIRGenFunction : public CIRGenTypeCache { LValue emitCompoundAssignmentLValue(const clang::CompoundAssignOperator *e); void emitConstructorBody(FunctionArgList &args); + void emitDestructorBody(FunctionArgList &args); mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s); diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 1044dfe507471..b0f92124457ad 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -43,7 +43,16 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { CIRGenFunction &cgf) override; void emitCXXConstructors(const clang::CXXConstructorDecl *d) override; + void emitCXXDestructors(const clang::CXXDestructorDecl *d) override; void emitCXXStructor(clang::GlobalDecl gd) override; + + bool useThunkForDtorVariant(const CXXDestructorDecl *dtor, + CXXDtorType dt) const override { + // Itanium does not emit any destructor variant as an inline thunk. + // Delegating may occur as an optimization, but all variants are either + // emitted with external linkage or as linkonce if they are inline and used. + return false; + } }; } // namespace @@ -150,17 +159,15 @@ static void emitConstructorDestructorAlias(CIRGenModule &cgm, void CIRGenItaniumCXXABI::emitCXXStructor(GlobalDecl gd) { auto *md = cast<CXXMethodDecl>(gd.getDecl()); - auto *cd = dyn_cast<CXXConstructorDecl>(md); - StructorCIRGen cirGenType = getCIRGenToUse(cgm, md); + const auto *cd = dyn_cast<CXXConstructorDecl>(md); + const CXXDestructorDecl *dd = cd ? nullptr : cast<CXXDestructorDecl>(md); - if (!cd) { - cgm.errorNYI(md->getSourceRange(), "CXCABI emit destructor"); - return; - } - - if (gd.getCtorType() == Ctor_Complete) { - GlobalDecl baseDecl = gd.getWithCtorType(Ctor_Base); + if (cd ? gd.getCtorType() == Ctor_Complete + : gd.getDtorType() == Dtor_Complete) { + GlobalDecl baseDecl = + cd ? gd.getWithCtorType(Ctor_Base) : gd.getWithDtorType(Dtor_Base); + ; if (cirGenType == StructorCIRGen::Alias || cirGenType == StructorCIRGen::COMDAT) { @@ -197,6 +204,22 @@ void CIRGenItaniumCXXABI::emitCXXConstructors(const CXXConstructorDecl *d) { } } +void CIRGenItaniumCXXABI::emitCXXDestructors(const CXXDestructorDecl *d) { + // The destructor used for destructing this as a base class; ignores + // virtual bases. + cgm.emitGlobal(GlobalDecl(d, Dtor_Base)); + + // The destructor used for destructing this as a most-derived class; + // call the base destructor and then destructs any virtual bases. + cgm.emitGlobal(GlobalDecl(d, Dtor_Complete)); + + // The destructor in a virtual table is always a 'deleting' + // destructor, which calls the complete destructor and then uses the + // appropriate operator delete. + if (d->isVirtual()) + cgm.emitGlobal(GlobalDecl(d, Dtor_Deleting)); +} + /// Return whether the given global decl needs a VTT (virtual table table) /// parameter, which it does if it's a base constructor or destructor with /// virtual bases. diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 8b2883b50d2e2..350270518156e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -1111,14 +1111,14 @@ CIRGenModule::getCIRLinkageVarDefinition(const VarDecl *vd, bool isConstant) { } cir::GlobalLinkageKind CIRGenModule::getFunctionLinkage(GlobalDecl gd) { - const auto *fd = cast<FunctionDecl>(gd.getDecl()); + const auto *d = cast<FunctionDecl>(gd.getDecl()); - GVALinkage linkage = astContext.GetGVALinkageForFunction(fd); + GVALinkage linkage = astContext.GetGVALinkageForFunction(d); - if (isa<CXXDestructorDecl>(fd)) - errorNYI(fd->getSourceRange(), "getFunctionLinkage: CXXDestructorDecl"); + if (const auto *dtor = dyn_cast<CXXDestructorDecl>(d)) + return getCXXABI().getCXXDestructorLinkage(linkage, dtor, gd.getDtorType()); - return getCIRLinkageForDeclarator(fd, linkage, /*IsConstantVariable=*/false); + return getCIRLinkageForDeclarator(d, linkage, /*isConstantVariable=*/false); } static cir::GlobalOp @@ -1274,6 +1274,9 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { case Decl::CXXConstructor: getCXXABI().emitCXXConstructors(cast<CXXConstructorDecl>(decl)); break; + case Decl::CXXDestructor: + getCXXABI().emitCXXDestructors(cast<CXXDestructorDecl>(decl)); + break; // C++ Decls case Decl::LinkageSpec: @@ -1335,6 +1338,17 @@ cir::FuncOp CIRGenModule::getAddrOfFunction(clang::GlobalDecl gd, funcType = convertType(fd->getType()); } + // Devirtualized destructor calls may come through here instead of via + // getAddrOfCXXStructor. Make sure we use the MS ABI base destructor instead + // of the complete destructor when necessary. + if (const auto *dd = dyn_cast<CXXDestructorDecl>(gd.getDecl())) { + if (getTarget().getCXXABI().isMicrosoft() && + gd.getDtorType() == Dtor_Complete && + dd->getParent()->getNumVBases() == 0) + errorNYI(dd->getSourceRange(), + "getAddrOfFunction: MS ABI complete destructor"); + } + StringRef mangledName = getMangledName(gd); cir::FuncOp func = getOrCreateCIRFunction(mangledName, funcType, gd, forVTable, dontDefer, @@ -1729,7 +1743,9 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction( // All MSVC dtors other than the base dtor are linkonce_odr and delegate to // each other bottoming out wiht the base dtor. Therefore we emit non-base // dtors on usage, even if there is no dtor definition in the TU. - if (isa_and_nonnull<CXXDestructorDecl>(d)) + if (isa_and_nonnull<CXXDestructorDecl>(d) && + getCXXABI().useThunkForDtorVariant(cast<CXXDestructorDecl>(d), + gd.getDtorType())) errorNYI(d->getSourceRange(), "getOrCreateCIRFunction: dtor"); // This is the first use or definition of a mangled name. If there is a diff --git a/clang/test/CIR/CodeGen/destructors.cpp b/clang/test/CIR/CodeGen/destructors.cpp new file mode 100644 index 0000000000000..c5e3cea39f63f --- /dev/null +++ b/clang/test/CIR/CodeGen/destructors.cpp @@ -0,0 +1,50 @@ +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-cir -mno-constructor-aliases %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -emit-llvm %s -mno-constructor-aliases -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -mno-constructor-aliases -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +void some_function() noexcept; + +struct out_of_line_destructor { + int prevent_tail_padding_reuse; + ~out_of_line_destructor(); +}; + +out_of_line_destructor::~out_of_line_destructor() { + some_function(); +} + +// CIR: !rec_out_of_line_destructor = !cir.record<struct "out_of_line_destructor" {!s32i}> + +// CIR: cir.func dso_local @_ZN22out_of_line_destructorD2Ev(%{{.+}}: !cir.ptr<!rec_out_of_line_destructor> +// CIR: cir.call @_Z13some_functionv() nothrow : () -> () +// CIR: cir.return + +// LLVM: define dso_local void @_ZN22out_of_line_destructorD2Ev(ptr %{{.+}}) +// LLVM: call void @_Z13some_functionv() +// LLVM: ret void + +// OGCG: define dso_local void @_ZN22out_of_line_destructorD2Ev(ptr {{.*}}%{{.+}}) +// OGCG: call void @_Z13some_functionv() +// OGCG: ret void + +// CIR: cir.func dso_local @_ZN22out_of_line_destructorD1Ev(%{{.+}}: !cir.ptr<!rec_out_of_line_destructor> +// CIR: cir.call @_Z13some_functionv() nothrow : () -> () +// CIR: cir.return + +// LLVM: define dso_local void @_ZN22out_of_line_destructorD1Ev(ptr %{{.+}}) +// LLVM: call void @_Z13some_functionv() +// LLVM: ret void + +// OGCG: define dso_local void @_ZN22out_of_line_destructorD1Ev(ptr {{.*}}%{{.+}}) +// OGCG: call void @_ZN22out_of_line_destructorD2Ev +// OGCG: ret void + +struct inline_destructor { + int prevent_tail_padding_reuse; + ~inline_destructor() noexcept(false) { + some_function(); + } +}; >From f62afec3e598533712e4df34bfe0fdc20f038569 Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Wed, 18 Jun 2025 16:13:53 +0100 Subject: [PATCH 2/3] Add source ranges --- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 8 ++++---- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 73df9f7f97ce8..42cc4cc4314d0 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -570,7 +570,7 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { // possible to delegate the destructor body to the complete // destructor. Do so. if (dtorType == Dtor_Deleting) { - cgm.errorNYI("deleting destructor"); + cgm.errorNYI(dtor->getSourceRange(), "deleting destructor"); return; } @@ -578,7 +578,7 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { // anything else. const bool isTryBody = isa_and_nonnull<CXXTryStmt>(body); if (isTryBody) { - cgm.errorNYI("function-try-block destructor"); + cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor"); } assert(!cir::MissingFeatures::sanitizers()); @@ -617,7 +617,7 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { assert(!cir::MissingFeatures::vtableInitialization()); if (isTryBody) { - cgm.errorNYI("function-try-block destructor"); + cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor"); } else if (body) { (void)emitStmt(body, /*useCurrentScope=*/true); } else { @@ -635,7 +635,7 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { // Exit the try if applicable. if (isTryBody) - cgm.errorNYI("function-try-block destructor"); + cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor"); } /// Given a value of type T* that may not be to a complete object, construct diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index b0f92124457ad..1496d877e7239 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -161,7 +161,6 @@ void CIRGenItaniumCXXABI::emitCXXStructor(GlobalDecl gd) { auto *md = cast<CXXMethodDecl>(gd.getDecl()); StructorCIRGen cirGenType = getCIRGenToUse(cgm, md); const auto *cd = dyn_cast<CXXConstructorDecl>(md); - const CXXDestructorDecl *dd = cd ? nullptr : cast<CXXDestructorDecl>(md); if (cd ? gd.getCtorType() == Ctor_Complete : gd.getDtorType() == Dtor_Complete) { >From 5a4e7f124479255d76784e2bf2857785045ebc9e Mon Sep 17 00:00:00 2001 From: Morris Hafner <mhaf...@nvidia.com> Date: Mon, 14 Jul 2025 17:33:32 +0200 Subject: [PATCH 3/3] Apply review feedback --- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 14 +++----------- clang/test/CIR/CodeGen/destructors.cpp | 7 +++++++ 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 42cc4cc4314d0..e532b9d855843 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -551,19 +551,12 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { // in fact emit references to them from other compilations, so emit them // as functions containing a trap instruction. if (dtorType != Dtor_Base && dtor->getParent()->isAbstract()) { - SourceLocation loc = - dtor->hasBody() ? dtor->getBody()->getBeginLoc() : dtor->getLocation(); - builder.create<cir::TrapOp>(getLoc(loc)); - // The corresponding clang/CodeGen logic clears the insertion point here, - // but MLIR's builder requires a valid insertion point, so we create a dummy - // block (since the trap is a block terminator). - builder.createBlock(builder.getBlock()->getParent()); + cgm.errorNYI(dtor->getSourceRange(), "abstract base class destructors"); return; } Stmt *body = dtor->getBody(); - if (body) - assert(!cir::MissingFeatures::incrementProfileCounter()); + assert(body && !cir::MissingFeatures::incrementProfileCounter()); // The call to operator delete in a deleting destructor happens // outside of the function-try-block, which means it's always @@ -577,9 +570,8 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { // If the body is a function-try-block, enter the try before // anything else. const bool isTryBody = isa_and_nonnull<CXXTryStmt>(body); - if (isTryBody) { + if (isTryBody) cgm.errorNYI(dtor->getSourceRange(), "function-try-block destructor"); - } assert(!cir::MissingFeatures::sanitizers()); assert(!cir::MissingFeatures::dtorCleanups()); diff --git a/clang/test/CIR/CodeGen/destructors.cpp b/clang/test/CIR/CodeGen/destructors.cpp index c5e3cea39f63f..d8f9f23ae191c 100644 --- a/clang/test/CIR/CodeGen/destructors.cpp +++ b/clang/test/CIR/CodeGen/destructors.cpp @@ -48,3 +48,10 @@ struct inline_destructor { some_function(); } }; + +// This inline destructor is not odr-used in this TU. +// Make sure we don't emit a definition + +// CIR-NOT: cir.func {{.*}}inline_destructor{{.*}} +// LLVM-NOT: define {{.*}}inline_destructor{{.*}} +// OGCG-NOT: define {{.*}}inline_destructor{{.*}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits