Skip to content

Commit be39f38

Browse files
authored
Merge pull request #40867 from zoecarver/interop-fix-linker-errors
[cxx-interop] Fix linker errors when using std-vector.
2 parents d546f5e + ba44bfe commit be39f38

File tree

9 files changed

+213
-17
lines changed

9 files changed

+213
-17
lines changed

lib/IRGen/GenClangDecl.cpp

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class ClangDeclFinder
4040
isa<clang::VarDecl>(DRE->getDecl())) {
4141
callback(DRE->getDecl());
4242
}
43+
4344
return true;
4445
}
4546

@@ -56,6 +57,17 @@ class ClangDeclFinder
5657
callback(CXXCE->getConstructor());
5758
return true;
5859
}
60+
61+
bool VisitVarDecl(clang::VarDecl *VD) {
62+
if (auto cxxRecord = VD->getType()->getAsCXXRecordDecl())
63+
if (auto dtor = cxxRecord->getDestructor())
64+
callback(dtor);
65+
66+
return true;
67+
}
68+
69+
bool shouldVisitTemplateInstantiations() const { return true; }
70+
bool shouldVisitImplicitCode() const { return true; }
5971
};
6072

6173
// If any (re)declaration of `decl` contains executable code, returns that
@@ -65,6 +77,11 @@ class ClangDeclFinder
6577
// the initializer of the variable.
6678
clang::Decl *getDeclWithExecutableCode(clang::Decl *decl) {
6779
if (auto fd = dyn_cast<clang::FunctionDecl>(decl)) {
80+
// If this is a potentially not-yet-instanciated template, we might
81+
// still have a body.
82+
if (fd->getTemplateInstantiationPattern())
83+
return fd;
84+
6885
const clang::FunctionDecl *definition;
6986
if (fd->hasBody(definition)) {
7087
return const_cast<clang::FunctionDecl *>(definition);
@@ -100,7 +117,7 @@ void IRGenModule::emitClangDecl(const clang::Decl *decl) {
100117
SmallVector<const clang::Decl *, 8> stack;
101118
stack.push_back(decl);
102119

103-
ClangDeclFinder refFinder([&](const clang::Decl *D) {
120+
auto callback = [&](const clang::Decl *D) {
104121
for (auto *DC = D->getDeclContext();; DC = DC->getParent()) {
105122
// Check that this is not a local declaration inside a function.
106123
if (DC->isFunctionOrMethod()) {
@@ -117,31 +134,55 @@ void IRGenModule::emitClangDecl(const clang::Decl *decl) {
117134
if (!GlobalClangDecls.insert(D->getCanonicalDecl()).second) {
118135
return;
119136
}
137+
120138
stack.push_back(D);
121-
});
139+
};
140+
141+
ClangDeclFinder refFinder(callback);
122142

123143
while (!stack.empty()) {
124144
auto *next = const_cast<clang::Decl *>(stack.pop_back_val());
145+
146+
// If a function calls another method in a class template specialization, we
147+
// need to instantiate that other function. Do that here.
148+
if (auto *fn = dyn_cast<clang::FunctionDecl>(next)) {
149+
// Make sure that this method is part of a class template specialization.
150+
if (fn->getTemplateInstantiationPattern())
151+
Context.getClangModuleLoader()
152+
->getClangSema()
153+
.InstantiateFunctionDefinition(fn->getLocation(), fn);
154+
}
155+
125156
if (clang::Decl *executableDecl = getDeclWithExecutableCode(next)) {
126157
refFinder.TraverseDecl(executableDecl);
127158
next = executableDecl;
128159
}
129160

161+
// Unfortunately, implicitly defined CXXDestructorDecls don't have a real
162+
// body, so we need to traverse these manually.
163+
if (auto *dtor = dyn_cast<clang::CXXDestructorDecl>(next)) {
164+
auto cxxRecord = dtor->getParent();
165+
166+
for (auto field : cxxRecord->fields()) {
167+
if (auto fieldCxxRecord = field->getType()->getAsCXXRecordDecl())
168+
if (auto *fieldDtor = fieldCxxRecord->getDestructor())
169+
callback(fieldDtor);
170+
}
171+
172+
for (auto base : cxxRecord->bases()) {
173+
if (auto baseCxxRecord = base.getType()->getAsCXXRecordDecl())
174+
if (auto *baseDtor = baseCxxRecord->getDestructor())
175+
callback(baseDtor);
176+
}
177+
}
178+
130179
if (auto var = dyn_cast<clang::VarDecl>(next))
131180
if (!var->isFileVarDecl())
132181
continue;
133182
if (isa<clang::FieldDecl>(next)) {
134183
continue;
135184
}
136-
// If a method calls another method in a class template specialization, we
137-
// need to instantiate that other method. Do that here.
138-
if (auto *method = dyn_cast<clang::CXXMethodDecl>(next)) {
139-
// Make sure that this method is part of a class template specialization.
140-
if (method->getTemplateInstantiationPattern())
141-
Context.getClangModuleLoader()
142-
->getClangSema()
143-
.InstantiateFunctionDefinition(method->getLocation(), method);
144-
}
185+
145186
ClangCodeGen->HandleTopLevelDecl(clang::DeclGroupRef(next));
146187
}
147188
}

lib/IRGen/GenDecl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,10 @@ void IRGenModule::emitSourceFile(SourceFile &SF) {
468468

469469
if (ObjCInterop)
470470
this->addLinkLibrary(LinkLibrary("objc", LibraryKind::Library));
471+
472+
// Automatically with libc++ when possible.
473+
if (Context.LangOpts.EnableCXXInterop && Context.LangOpts.Target.isOSDarwin())
474+
this->addLinkLibrary(LinkLibrary("c++", LibraryKind::Library));
471475

472476
// FIXME: It'd be better to have the driver invocation or build system that
473477
// executes the linker introduce these compatibility libraries, since at

lib/IRGen/GenStruct.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,7 @@ static clang::CXXDestructorDecl *getCXXDestructor(SILType type) {
8181
dyn_cast<clang::CXXRecordDecl>(structDecl->getClangDecl());
8282
if (!cxxRecordDecl)
8383
return nullptr;
84-
for (auto member : cxxRecordDecl->methods()) {
85-
if (auto dest = dyn_cast<clang::CXXDestructorDecl>(member)) {
86-
return dest;
87-
}
88-
}
89-
return nullptr;
84+
return cxxRecordDecl->getDestructor();
9085
}
9186

9287
namespace {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module StdVector {
2+
header "std-vector.h"
3+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef TEST_INTEROP_CXX_STDLIB_INPUTS_STD_VECTOR_H
2+
#define TEST_INTEROP_CXX_STDLIB_INPUTS_STD_VECTOR_H
3+
4+
#include <vector>
5+
6+
using Vector = std::vector<int>;
7+
8+
inline Vector initVector() { return {}; }
9+
10+
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_VECTOR_H
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop)
2+
//
3+
// REQUIRES: executable_test
4+
//
5+
// Enable this everywhere once we have a solution for modularizing libstdc++: rdar://87654514
6+
// REQUIRES: OS=macosx
7+
8+
import StdlibUnittest
9+
import StdVector
10+
import std.vector
11+
12+
var StdVectorTestSuite = TestSuite("StdVector")
13+
14+
extension Vector : RandomAccessCollection {
15+
public var startIndex: Int { 0 }
16+
public var endIndex: Int { size() }
17+
}
18+
19+
StdVectorTestSuite.test("init") {
20+
let v = Vector()
21+
expectEqual(v.size(), 0)
22+
expectTrue(v.empty())
23+
}
24+
25+
StdVectorTestSuite.test("push back") {
26+
var v = Vector()
27+
var _42: CInt = 42
28+
v.push_back(&_42)
29+
expectEqual(v.size(), 1)
30+
expectFalse(v.empty())
31+
expectEqual(v[0], 42)
32+
}
33+
34+
func fill(vector v: inout Vector) {
35+
var _1: CInt = 1, _2: CInt = 2, _3: CInt = 3
36+
v.push_back(&_1)
37+
v.push_back(&_2)
38+
v.push_back(&_3)
39+
}
40+
41+
StdVectorTestSuite.test("for loop") {
42+
var v = Vector()
43+
fill(vector: &v)
44+
45+
var count: CInt = 1
46+
for e in v {
47+
expectEqual(e, count)
48+
count += 1
49+
}
50+
expectEqual(count, 4)
51+
}
52+
53+
StdVectorTestSuite.test("map") {
54+
var v = Vector()
55+
fill(vector: &v)
56+
57+
let a = v.map { $0 + 5 }
58+
expectEqual(a, [6, 7, 8])
59+
}
60+
61+
runAllTests()
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
#ifndef TEST_INTEROP_CXX_TEMPLATES_INPUTS_DEFINE_REFERENCED_INLINE_TYPES_H
2+
#define TEST_INTEROP_CXX_TEMPLATES_INPUTS_DEFINE_REFERENCED_INLINE_TYPES_H
3+
4+
template <class T>
5+
inline void inlineFn1(T) { }
6+
7+
template <class T>
8+
inline void inlineFn2(T) { }
9+
10+
inline void inlineFn3() { }
11+
12+
template<class T>
13+
struct HasInlineDtor {
14+
inline ~HasInlineDtor() { inlineFn1(T()); }
15+
};
16+
17+
template<class T>
18+
struct CtorCallsInlineFn {
19+
CtorCallsInlineFn(T x) { inlineFn2(x); }
20+
};
21+
22+
template<class T>
23+
struct HasInlineStaticMember {
24+
inline static void member() { inlineFn3(); }
25+
};
26+
27+
template<class T>
28+
struct ChildWithInlineCtorDtor1 {
29+
inline ChildWithInlineCtorDtor1() { HasInlineStaticMember<T>::member(); }
30+
inline ~ChildWithInlineCtorDtor1() { HasInlineStaticMember<T>::member(); }
31+
};
32+
33+
template<class T>
34+
struct ChildWithInlineCtorDtor2 {
35+
inline ChildWithInlineCtorDtor2() { HasInlineStaticMember<T>::member(); }
36+
inline ~ChildWithInlineCtorDtor2() { HasInlineStaticMember<T>::member(); }
37+
};
38+
39+
template<class T>
40+
struct ParentWithChildWithInlineCtorDtor : ChildWithInlineCtorDtor1<T> {};
41+
42+
template<class T>
43+
struct HolderWithChildWithInlineCtorDtor {
44+
ChildWithInlineCtorDtor2<T> x;
45+
};
46+
47+
template<class T>
48+
struct DtorCallsInlineMethod {
49+
inline void unique_name() {}
50+
51+
~DtorCallsInlineMethod() {
52+
unique_name();
53+
}
54+
};
55+
56+
#endif // TEST_INTEROP_CXX_TEMPLATES_INPUTS_DEFINE_REFERENCED_INLINE_TYPES_H

test/Interop/Cxx/templates/Inputs/module.modulemap

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,8 @@ module TemplateTypeParameterNotInSignature {
127127
header "template-type-parameter-not-in-signature.h"
128128
requires cplusplus
129129
}
130+
131+
module DefineReferencedInline {
132+
header "define-referenced-inline.h"
133+
requires cplusplus
134+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-run-simple-swift(-I %S/Inputs -Xfrontend -enable-cxx-interop -Xcc -fno-exceptions)
2+
//
3+
// REQUIRES: executable_test
4+
//
5+
// Enable this everywhere once we have a solution for modularizing libstdc++: rdar://87654514
6+
// REQUIRES: OS=macosx
7+
8+
import DefineReferencedInline
9+
import StdlibUnittest
10+
11+
var TemplatesTestSuite = TestSuite("TemplatesTestSuite")
12+
13+
TemplatesTestSuite.test("tests") {
14+
let a = CtorCallsInlineFn<CInt>(0)
15+
let b = HasInlineDtor<CInt>()
16+
let c = ParentWithChildWithInlineCtorDtor<CInt>()
17+
let d = HolderWithChildWithInlineCtorDtor<CInt>()
18+
let e = DtorCallsInlineMethod<CInt>()
19+
}
20+
21+
runAllTests()

0 commit comments

Comments
 (0)