Skip to content

Commit 323e2b1

Browse files
committed
[cxx-interop] Fix linker errors when using std-vector.
Adds tests for using std-vector and some other interesting types. This patch fixes four mis conceptions that the compiler was previously making: 1. Implicit destructors have no side effects. (Yes, this means we were not cleaning up some objects.) 2. Implicit destructors have bodies. (Technically they do, but the body doesn't include CallExprs that they make when lowered to IR.) 3. Functions other than methods can be uninstantiated templates. 4. Uninstantiated templates may have executable code. (I.e., we can never take the fast path.) And makes sure that we visit the destructor of any VarDecl (including parameters).
1 parent 2928459 commit 323e2b1

File tree

8 files changed

+209
-17
lines changed

8 files changed

+209
-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/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 -lc++)
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)