Skip to content

Commit 2b59c48

Browse files
committed
[cxx-interop] Fix metadata mismatch regarding fields of structs
1 parent d12cb84 commit 2b59c48

File tree

11 files changed

+267
-29
lines changed

11 files changed

+267
-29
lines changed

lib/IRGen/Field.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,20 @@ struct Field {
9696
bool operator!=(Field other) const { return declOrKind != other.declOrKind; }
9797
};
9898

99+
// Don't export private C++ fields that were imported as private Swift fields.
100+
// The type of a private field might not have all the type witness operations
101+
// that Swift requires, for instance, `std::unique_ptr<IncompleteType>` would
102+
// not have a destructor.
103+
bool isExportableField(Field field);
104+
99105
/// Iterate all the fields of the given struct or class type, including
100106
/// any implicit fields that might be accounted for in
101107
/// getFieldVectorLength.
102108
void forEachField(IRGenModule &IGM, const NominalTypeDecl *typeDecl,
103109
llvm::function_ref<void(Field field)> fn);
104110

111+
unsigned countExportableFields(IRGenModule &IGM, const NominalTypeDecl *type);
112+
105113
} // end namespace irgen
106114
} // end namespace swift
107115

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1840,7 +1840,7 @@ namespace {
18401840

18411841
void addLayoutInfo() {
18421842
// uint32_t NumFields;
1843-
B.addInt32(getNumFields(getType()));
1843+
B.addInt32(countExportableFields(IGM, getType()));
18441844

18451845
// uint32_t FieldOffsetVectorOffset;
18461846
B.addInt32(FieldVectorOffset / IGM.getPointerSize());

lib/IRGen/GenReflection.cpp

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -946,35 +946,13 @@ class FieldTypeMetadataBuilder : public ReflectionMetadataBuilder {
946946
B.addInt16(uint16_t(kind));
947947
B.addInt16(FieldRecordSize);
948948

949-
// Filter to select which fields we'll export FieldDescriptors for.
950-
auto exportable_field =
951-
[](Field field) {
952-
// Don't export private C++ fields that were imported as private Swift fields.
953-
// The type of a private field might not have all the type witness
954-
// operations that Swift requires, for instance,
955-
// `std::unique_ptr<IncompleteType>` would not have a destructor.
956-
if (field.getKind() == Field::Kind::Var &&
957-
field.getVarDecl()->getClangDecl() &&
958-
field.getVarDecl()->getFormalAccess() == AccessLevel::Private)
959-
return false;
960-
// All other fields are exportable
961-
return true;
962-
};
963-
964-
// Count exportable fields
965-
int exportableFieldCount = 0;
966-
forEachField(IGM, NTD, [&](Field field) {
967-
if (exportable_field(field)) {
968-
++exportableFieldCount;
969-
}
970-
});
971-
972949
// Emit exportable fields, prefixed with a count
973-
B.addInt32(exportableFieldCount);
950+
B.addInt32(countExportableFields(IGM, NTD));
951+
952+
// Filter to select which fields we'll export FieldDescriptor for.
974953
forEachField(IGM, NTD, [&](Field field) {
975-
if (exportable_field(field)) {
954+
if (isExportableField(field))
976955
addField(field);
977-
}
978956
});
979957
}
980958

lib/IRGen/StructLayout.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,15 @@ unsigned irgen::getNumFields(const NominalTypeDecl *target) {
589589
return numFields;
590590
}
591591

592+
bool irgen::isExportableField(Field field) {
593+
if (field.getKind() == Field::Kind::Var &&
594+
field.getVarDecl()->getClangDecl() &&
595+
field.getVarDecl()->getFormalAccess() == AccessLevel::Private)
596+
return false;
597+
// All other fields are exportable
598+
return true;
599+
}
600+
592601
void irgen::forEachField(IRGenModule &IGM, const NominalTypeDecl *typeDecl,
593602
llvm::function_ref<void(Field field)> fn) {
594603
auto classDecl = dyn_cast<ClassDecl>(typeDecl);
@@ -610,6 +619,17 @@ void irgen::forEachField(IRGenModule &IGM, const NominalTypeDecl *typeDecl,
610619
}
611620
}
612621

622+
unsigned irgen::countExportableFields(IRGenModule &IGM,
623+
const NominalTypeDecl *type) {
624+
// Don't count private C++ fields that were imported as private Swift fields
625+
unsigned exportableFieldCount = 0;
626+
forEachField(IGM, type, [&](Field field) {
627+
if (isExportableField(field))
628+
++exportableFieldCount;
629+
});
630+
return exportableFieldCount;
631+
}
632+
613633
SILType Field::getType(IRGenModule &IGM, SILType baseType) const {
614634
switch (getKind()) {
615635
case Field::Var:

lib/IRGen/StructMetadataVisitor.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,11 @@ template <class Impl> class StructMetadataVisitor
6363

6464
// Struct field offsets.
6565
asImpl().noteStartOfFieldOffsets();
66-
for (VarDecl *prop : Target->getStoredProperties())
67-
asImpl().addFieldOffset(prop);
66+
for (VarDecl *prop : Target->getStoredProperties()) {
67+
if (!(prop->getClangDecl() &&
68+
prop->getFormalAccess() == AccessLevel::Private))
69+
asImpl().addFieldOffset(prop);
70+
}
6871

6972
asImpl().noteEndOfFieldOffsets();
7073

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,3 +152,9 @@ module PIMPL {
152152
requires cplusplus
153153
export *
154154
}
155+
156+
module SimpleStructs {
157+
header "simple-structs.h"
158+
requires cplusplus
159+
export *
160+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_SIMPLE_STRUCTS_H
2+
#define TEST_INTEROP_CXX_CLASS_INPUTS_SIMPLE_STRUCTS_H
3+
4+
#include <string>
5+
6+
struct HasPrivateFieldsOnly {
7+
private:
8+
int priv1;
9+
int priv2;
10+
11+
public:
12+
HasPrivateFieldsOnly(int i1, int i2) : priv1(i1), priv2(i2) {}
13+
};
14+
15+
struct HasPublicFieldsOnly {
16+
int publ1;
17+
int publ2;
18+
19+
HasPublicFieldsOnly(int i1, int i2) : publ1(i1), publ2(i2) {}
20+
};
21+
22+
struct HasPrivatePublicProtectedFields {
23+
private:
24+
int priv1;
25+
26+
public:
27+
int publ1;
28+
29+
protected:
30+
int prot1;
31+
32+
protected:
33+
int prot2;
34+
35+
private:
36+
int priv2;
37+
38+
public:
39+
int publ2;
40+
41+
HasPrivatePublicProtectedFields(int i1, int i2, int i3, int i4, int i5,
42+
int i6)
43+
: priv1(i1), publ1(i2), prot1(i3), prot2(i4), priv2(i5),
44+
publ2(i6) {}
45+
};
46+
47+
struct Outer {
48+
private:
49+
HasPrivatePublicProtectedFields privStruct;
50+
51+
public:
52+
HasPrivatePublicProtectedFields publStruct;
53+
54+
Outer() : privStruct(1, 2, 3, 4, 5, 6), publStruct(7, 8, 9, 10, 11, 12) {}
55+
};
56+
57+
#endif
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -o %t/a.out -Xfrontend -enable-experimental-cxx-interop -I %S/Inputs
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out | %FileCheck %s
5+
6+
// REQUIRES: executable_test
7+
8+
import SimpleStructs
9+
10+
func printCxxStructPrivateFields() {
11+
let s = HasPrivateFieldsOnly(1, 2)
12+
print(s)
13+
}
14+
15+
func printCxxStructPublicFields() {
16+
let s = HasPublicFieldsOnly(1, 2)
17+
print(s)
18+
}
19+
20+
func printCxxStructPrivatePublicProtectedFields() {
21+
let s = HasPrivatePublicProtectedFields(1, 2, 3, 4, 5, 6)
22+
print(s)
23+
}
24+
25+
func printCxxStructNested() {
26+
let s = Outer()
27+
print(s)
28+
}
29+
30+
printCxxStructPrivateFields()
31+
// CHECK: HasPrivateFieldsOnly()
32+
33+
printCxxStructPublicFields()
34+
// CHECK: HasPublicFieldsOnly(publ1: 1, publ2: 2)
35+
36+
printCxxStructPrivatePublicProtectedFields()
37+
// CHECK: HasPrivatePublicProtectedFields(publ1: 2, publ2: 6)
38+
39+
printCxxStructNested()
40+
// CHECK: Outer(publStruct: {{.*}}.HasPrivatePublicProtectedFields(publ1: 8, publ2: 12))

test/Interop/Cxx/stdlib/Inputs/std-string-and-vector.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,28 @@ struct Item {
99
inline Item get_item() {
1010
return {};
1111
}
12+
13+
std::vector<int> makeVecOfInt() { return {1, 2, 3}; }
14+
std::vector<std::string> makeVecOfString() { return {"Hello", "World"}; }
15+
16+
struct S {
17+
private:
18+
std::string privStr;
19+
std::vector<std::string> privVec;
20+
21+
public:
22+
std::string pubStr;
23+
std::vector<std::string> pubVec;
24+
25+
protected:
26+
std::string protStr;
27+
std::vector<std::string> protVec;
28+
29+
public:
30+
S() : privStr("private"), privVec({"private", "vector"}),
31+
pubStr("public"), pubVec({"a", "public", "vector"}),
32+
protStr("protected"), protVec({"protected", "vector"}) {}
33+
34+
std::vector<std::string> getPrivVec() const { return privVec; }
35+
std::string getProtStr() const { return protStr; }
36+
};

test/Interop/Cxx/stdlib/Inputs/std-unique-ptr.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define TEST_INTEROP_CXX_STDLIB_INPUTS_STD_UNIQUE_PTR_H
33

44
#include <memory>
5+
#include <string>
56

67
struct NonCopyable {
78
NonCopyable(int x) : x(x) {}
@@ -29,6 +30,10 @@ std::unique_ptr<int> makeInt() {
2930
return std::make_unique<int>(42);
3031
}
3132

33+
std::unique_ptr<std::string> makeString() {
34+
return std::make_unique<std::string>("Unique string");
35+
}
36+
3237
std::unique_ptr<int[]> makeArray() {
3338
int *array = new int[3];
3439
array[0] = 1;
@@ -55,4 +60,10 @@ std::unique_ptr<HasDtor> makeDtor() {
5560
return std::make_unique<HasDtor>();
5661
}
5762

63+
std::shared_ptr<int> makeIntShared() { return std::make_unique<int>(42); }
64+
65+
std::shared_ptr<std::string> makeStringShared() {
66+
return std::make_unique<std::string>("Shared string");
67+
}
68+
5869
#endif // TEST_INTEROP_CXX_STDLIB_INPUTS_STD_UNIQUE_PTR_H
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -o %t/a.out -Xfrontend -enable-experimental-cxx-interop -I %S/Inputs
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out | %FileCheck %s
5+
6+
// REQUIRES: executable_test
7+
// XFAIL: OS=windows-msvc
8+
// FIXME: We can't import std::unique_ptr or std::shared_ptr properly on Windows (https://github.com/apple/swift/issues/70226)
9+
10+
import StdStringAndVector
11+
import StdUniquePtr
12+
13+
func printStdString(s: std.string) {
14+
print(s)
15+
let swiftString = String(s)
16+
print(swiftString)
17+
}
18+
19+
func printStdUniquePtrOfInt() {
20+
let uint = makeInt()
21+
print(uint.pointee)
22+
}
23+
24+
func printStdUniquePtrOfString() {
25+
let ustring = makeString()
26+
print(ustring.pointee)
27+
}
28+
29+
func printStdSharedPtrOfInt() {
30+
let sint = makeIntShared()
31+
print(sint.pointee)
32+
print(sint)
33+
}
34+
35+
func printStdSharedPtrOfString() {
36+
let sstring = makeStringShared()
37+
print(sstring.pointee)
38+
print(sstring)
39+
}
40+
41+
func printStdVectorOfInt() {
42+
let vecOfInt = makeVecOfInt()
43+
print(vecOfInt[0])
44+
print(vecOfInt)
45+
}
46+
47+
func printStdVectorOfString() {
48+
let vecOfString = makeVecOfString()
49+
print(vecOfString[0])
50+
print(vecOfString)
51+
}
52+
53+
func printStruct() {
54+
let s = S()
55+
print(s.getPrivVec())
56+
print(s.getProtStr())
57+
print(s.pubStr)
58+
print(s.pubVec)
59+
print(s)
60+
}
61+
62+
printStdString(s: "Hello")
63+
// CHECK: basic_string<CChar, {{.*}}, {{.*}}>()
64+
// CHECK: Hello
65+
66+
printStdUniquePtrOfInt()
67+
// CHECK: 42
68+
printStdUniquePtrOfString()
69+
// CHECK: basic_string<CChar, {{.*}}, {{.*}}>()
70+
71+
printStdSharedPtrOfInt()
72+
// CHECK: 42
73+
// CHECK: shared_ptr<CInt>()
74+
printStdSharedPtrOfString()
75+
// CHECK: basic_string<CChar, {{.*}}, {{.*}}>()
76+
// CHECK: shared_ptr<{{.*}}.basic_string<CChar, {{.*}}, {{.*}}>>
77+
78+
printStdVectorOfInt()
79+
// CHECK: 1
80+
// CHECK: vector<CInt, {{.*}}>()
81+
printStdVectorOfString()
82+
// CHECK: basic_string<CChar, {{.*}}, {{.*}}>()
83+
// CHECK: vector<{{.*}}.basic_string<CChar, {{.*}}, {{.*}}>, {{.*}}>()
84+
85+
printStruct()
86+
// CHECK: vector<{{.*}}.basic_string<CChar, {{.*}}, {{.*}}>, {{.*}}>()
87+
// CHECK: basic_string<CChar, {{.*}}, {{.*}}>()
88+
// CHECK: basic_string<CChar, {{.*}}, {{.*}}>()
89+
// CHECK: vector<{{.*}}.basic_string<CChar, {{.*}}, {{.*}}>, {{.*}}>()
90+
// CHECK: S(pubStr: {{.*}}.basic_string<CChar, {{.*}}, {{.*}}>(), pubVec: {{.*}}.vector<{{.*}}.basic_string<CChar, {{.*}}, {{.*}}>, {{.*}}>())

0 commit comments

Comments
 (0)