Skip to content

[cxx-interop] ensure that the body of implicit virtual destructor is … #74640

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions lib/IRGen/GenClangDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,12 @@ void IRGenModule::emitClangDecl(const clang::Decl *decl) {
// class because they might be emitted in the vtable even if not used
// directly from Swift.
if (auto *record = dyn_cast<clang::CXXRecordDecl>(next->getDeclContext())) {
if (auto *destructor = record->getDestructor()) {
// Ensure virtual destructors have the body defined, even if they're
// not used directly, as they might be referenced by the emitted vtable.
if (destructor->isVirtual() && !destructor->isDeleted())
ensureImplicitCXXDestructorBodyIsDefined(destructor);
}
for (auto *method : record->methods()) {
if (method->isVirtual()) {
callback(method);
Expand Down Expand Up @@ -333,3 +339,15 @@ void IRGenModule::finalizeClangCodeGen() {
ClangCodeGen->HandleTranslationUnit(
*const_cast<clang::ASTContext *>(ClangASTContext));
}

void IRGenModule::ensureImplicitCXXDestructorBodyIsDefined(
clang::CXXDestructorDecl *destructor) {
if (destructor->isUserProvided() ||
destructor->doesThisDeclarationHaveABody())
return;
assert(!destructor->isDeleted() &&
"Swift cannot handle a type with no known destructor.");
// Make sure we define the destructor so we have something to call.
auto &sema = Context.getClangModuleLoader()->getClangSema();
sema.DefineImplicitDestructor(clang::SourceLocation(), destructor);
}
9 changes: 1 addition & 8 deletions lib/IRGen/GenStruct.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -688,14 +688,7 @@ namespace {
return;
}

if (!destructor->isUserProvided() &&
!destructor->doesThisDeclarationHaveABody()) {
assert(!destructor->isDeleted() &&
"Swift cannot handle a type with no known destructor.");
// Make sure we define the destructor so we have something to call.
auto &sema = IGF.IGM.Context.getClangModuleLoader()->getClangSema();
sema.DefineImplicitDestructor(clang::SourceLocation(), destructor);
}
IGF.IGM.ensureImplicitCXXDestructorBodyIsDefined(destructor);

clang::GlobalDecl destructorGlobalDecl(destructor, clang::Dtor_Complete);
auto *destructorFnAddr =
Expand Down
4 changes: 4 additions & 0 deletions lib/IRGen/IRGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ namespace clang {
class ASTContext;
template <class> class CanQual;
class CodeGenerator;
class CXXDestructorDecl;
class Decl;
class GlobalDecl;
class Type;
Expand Down Expand Up @@ -1615,6 +1616,9 @@ private: \
ForeignFunctionInfo *foreignInfo=nullptr);
ForeignFunctionInfo getForeignFunctionInfo(CanSILFunctionType type);

void
ensureImplicitCXXDestructorBodyIsDefined(clang::CXXDestructorDecl *cxxDtor);

llvm::ConstantInt *getInt32(uint32_t value);
llvm::ConstantInt *getSize(Size size);
llvm::Constant *getAlignment(Alignment align);
Expand Down
129 changes: 129 additions & 0 deletions test/Interop/Cxx/class/virtual-destructor-vtable-ref-irgen.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// RUN: rm -rf %t
// RUN: split-file %s %t
// RUN: %target-swiftxx-frontend -emit-ir -I %t/Inputs -validate-tbd-against-ir=none %t/test.swift | %FileCheck %s
// RUN: %target-swiftxx-frontend -verify -emit-ir -I %t/Inputs -validate-tbd-against-ir=none %t/test.swift -Xcc -DDELETED -o /dev/null

//--- Inputs/module.modulemap
module VtableDestructorRef {
header "test.h"
requires cplusplus
}
//--- Inputs/test.h


namespace impl {

#ifdef DELETED

template<class T>
class BaseClass
{
public:
~BaseClass() = delete;
};

template<class Fp, class T>
class Func: public BaseClass<T>
{
Fp x;
public:
inline explicit Func(Fp x);
Func(const Func &) = delete;
~Func();
};

#else

template<class T>
class BaseClass
{
public:
virtual ~BaseClass() {}
};

template<class Fp, class T>
class Func: public BaseClass<T>
{
Fp x;
public:

inline explicit Func(Fp x) : x(x) {}
};

#endif

template <class _Fp> class ValueFunc;

template <class _Rp, class... _ArgTypes> class ValueFunc<_Rp(_ArgTypes...)>
{
typedef impl::BaseClass<_Rp(_ArgTypes...)> FuncTy;
FuncTy* _Nullable f;
public:

template <class _Fp>
ValueFunc(_Fp fp) {
typedef impl::Func<_Fp, _Rp(_ArgTypes...)> _Fun;
f = ::new _Fun(fp);
}

ValueFunc(ValueFunc&& other) {
if (other.f == nullptr)
f = nullptr;
else
{
f = other.f;
other.f = nullptr;
}
}
};

template<class _Rp>
class Function;

template<class _Rp, class ..._ArgTypes>
class Function<_Rp(_ArgTypes...)> {
ValueFunc<_Rp(_ArgTypes...)> f;
public:
template<class _Fp>
Function(_Fp);
};

template <class _Rp, class... _ArgTypes>
template <class _Fp>
Function<_Rp(_ArgTypes...)>::Function(_Fp f) : f(f) {}

}

class MyFutureBase {
public:
void OnCompletion(impl::Function<void(const MyFutureBase&)> callback) const;
};

template<class T>
class MyFuture : public MyFutureBase {
public:
void OnCompletion(
void (* _Nonnull completion)(void * _Nullable),
void * _Nullable user_data) const {
MyFutureBase::OnCompletion(
[completion, user_data](const MyFutureBase&) {
completion(user_data);
});
}
};

using MyFutureInt = MyFuture<int>;

//--- test.swift

import VtableDestructorRef

public func test() {
let f = MyFutureInt()
f.OnCompletion({ _ in
print("done")
}, nil)
}

// Make sure we reach the virtual destructor of 'Func'.
// CHECK: define linkonce_odr {{.*}} @{{_ZN4impl4FuncIZNK8MyFutureIiE12OnCompletionEPFvPvES3_EUlRK12MyFutureBaseE_FvS8_EED2Ev|"\?\?1\?\$BaseClass@\$\$A6AXAEBVMyFutureBase@@@Z@impl@@UEAA@XZ"}}