Skip to content

Commit cd1ca50

Browse files
committed
[cxx-interop] ensure that the body of implicit virtual destructor is defined before emitting the clang decl for it
This ensures that the destructor definition is emitted into the module's LLVM IR, fixing a linker error.
1 parent e6f680d commit cd1ca50

File tree

4 files changed

+129
-8
lines changed

4 files changed

+129
-8
lines changed

lib/IRGen/GenClangDecl.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,12 @@ void IRGenModule::emitClangDecl(const clang::Decl *decl) {
284284
// class because they might be emitted in the vtable even if not used
285285
// directly from Swift.
286286
if (auto *record = dyn_cast<clang::CXXRecordDecl>(next->getDeclContext())) {
287+
if (auto *destructor = record->getDestructor()) {
288+
// Ensure virtual destructors have the body defined, even if they're
289+
// not used directly, as they might be referenced by the emitted vtable.
290+
if (destructor->isVirtual())
291+
ensureImplicitCXXDestructorBodyIsDefined(destructor);
292+
}
287293
for (auto *method : record->methods()) {
288294
if (method->isVirtual()) {
289295
callback(method);
@@ -333,3 +339,15 @@ void IRGenModule::finalizeClangCodeGen() {
333339
ClangCodeGen->HandleTranslationUnit(
334340
*const_cast<clang::ASTContext *>(ClangASTContext));
335341
}
342+
343+
void IRGenModule::ensureImplicitCXXDestructorBodyIsDefined(
344+
clang::CXXDestructorDecl *destructor) {
345+
if (!destructor->isUserProvided() &&
346+
!destructor->doesThisDeclarationHaveABody()) {
347+
assert(!destructor->isDeleted() &&
348+
"Swift cannot handle a type with no known destructor.");
349+
// Make sure we define the destructor so we have something to call.
350+
auto &sema = Context.getClangModuleLoader()->getClangSema();
351+
sema.DefineImplicitDestructor(clang::SourceLocation(), destructor);
352+
}
353+
}

lib/IRGen/GenStruct.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -688,14 +688,7 @@ namespace {
688688
return;
689689
}
690690

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

700693
clang::GlobalDecl destructorGlobalDecl(destructor, clang::Dtor_Complete);
701694
auto *destructorFnAddr =

lib/IRGen/IRGenModule.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ namespace clang {
7777
class ASTContext;
7878
template <class> class CanQual;
7979
class CodeGenerator;
80+
class CXXDestructorDecl;
8081
class Decl;
8182
class GlobalDecl;
8283
class Type;
@@ -1615,6 +1616,9 @@ private: \
16151616
ForeignFunctionInfo *foreignInfo=nullptr);
16161617
ForeignFunctionInfo getForeignFunctionInfo(CanSILFunctionType type);
16171618

1619+
void
1620+
ensureImplicitCXXDestructorBodyIsDefined(clang::CXXDestructorDecl *cxxDtor);
1621+
16181622
llvm::ConstantInt *getInt32(uint32_t value);
16191623
llvm::ConstantInt *getSize(Size size);
16201624
llvm::Constant *getAlignment(Alignment align);
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
// RUN: %target-swiftxx-frontend -emit-ir -I %t/Inputs -validate-tbd-against-ir=none %t/test.swift | %FileCheck %s
4+
5+
//--- Inputs/module.modulemap
6+
module VtableDestructorRef {
7+
header "test.h"
8+
requires cplusplus
9+
}
10+
//--- Inputs/test.h
11+
12+
13+
namespace impl {
14+
15+
template<class T>
16+
class BaseClass
17+
{
18+
public:
19+
virtual ~BaseClass() {}
20+
};
21+
22+
template<class Fp, class T>
23+
class Func: public BaseClass<T>
24+
{
25+
Fp x;
26+
public:
27+
28+
inline explicit Func(Fp x) : x(x) {}
29+
};
30+
31+
32+
template <class _Fp> class ValueFunc;
33+
34+
template <class _Rp, class... _ArgTypes> class ValueFunc<_Rp(_ArgTypes...)>
35+
{
36+
typedef impl::BaseClass<_Rp(_ArgTypes...)> FuncTy;
37+
FuncTy* _Nullable f;
38+
public:
39+
40+
template <class _Fp>
41+
ValueFunc(_Fp fp) {
42+
typedef impl::Func<_Fp, _Rp(_ArgTypes...)> _Fun;
43+
f = ::new _Fun(fp);
44+
}
45+
46+
ValueFunc(ValueFunc&& other) {
47+
if (other.f == nullptr)
48+
f = nullptr;
49+
else
50+
{
51+
f = other.f;
52+
other.f = nullptr;
53+
}
54+
}
55+
};
56+
57+
template<class _Rp>
58+
class Function;
59+
60+
template<class _Rp, class ..._ArgTypes>
61+
class Function<_Rp(_ArgTypes...)> {
62+
ValueFunc<_Rp(_ArgTypes...)> f;
63+
public:
64+
template<class _Fp>
65+
Function(_Fp);
66+
};
67+
68+
template <class _Rp, class... _ArgTypes>
69+
template <class _Fp>
70+
Function<_Rp(_ArgTypes...)>::Function(_Fp f) : f(f) {}
71+
72+
}
73+
74+
class MyFutureBase {
75+
public:
76+
void OnCompletion(impl::Function<void(const MyFutureBase&)> callback) const;
77+
};
78+
79+
template<class T>
80+
class MyFuture : public MyFutureBase {
81+
public:
82+
void OnCompletion(
83+
void (* _Nonnull completion)(void * _Nullable),
84+
void * _Nullable user_data) const {
85+
MyFutureBase::OnCompletion(
86+
[completion, user_data](const MyFutureBase&) {
87+
completion(user_data);
88+
});
89+
}
90+
};
91+
92+
using MyFutureInt = MyFuture<int>;
93+
94+
//--- test.swift
95+
96+
import VtableDestructorRef
97+
98+
public func test() {
99+
let f = MyFutureInt()
100+
f.OnCompletion({ _ in
101+
print("done")
102+
}, nil)
103+
}
104+
105+
// Make sure we reach the virtual destructor of 'Func'.
106+
// CHECK: define linkonce_odr {{.*}} @{{_ZN4impl4FuncIZNK8MyFutureIiE12OnCompletionEPFvPvES3_EUlRK12MyFutureBaseE_FvS8_EED2Ev|\?\?1\?\$BaseClass@\$\$A6AXAEBVMyFutureBase@@@Z@impl@@UEAA@XZ}}

0 commit comments

Comments
 (0)