Skip to content

Commit e805d93

Browse files
authored
Merge pull request #81035 from swiftlang/susmonteiro/metadata-private-fields
[cxx-interop] Fix metadata mismatch regarding fields of structs
2 parents bbe9f65 + 72b13b3 commit e805d93

12 files changed

+335
-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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_SIMPLE_STRUCTS_H
2+
#define TEST_INTEROP_CXX_CLASS_INPUTS_SIMPLE_STRUCTS_H
3+
4+
struct HasPrivateFieldsOnly {
5+
private:
6+
int priv1;
7+
int priv2;
8+
9+
public:
10+
HasPrivateFieldsOnly(int i1, int i2) : priv1(i1), priv2(i2) {}
11+
};
12+
13+
struct HasPublicFieldsOnly {
14+
int publ1;
15+
int publ2;
16+
17+
HasPublicFieldsOnly(int i1, int i2) : publ1(i1), publ2(i2) {}
18+
};
19+
20+
struct HasPrivatePublicProtectedFields {
21+
private:
22+
int priv1;
23+
24+
public:
25+
int publ1;
26+
27+
protected:
28+
int prot1;
29+
30+
protected:
31+
int prot2;
32+
33+
private:
34+
int priv2;
35+
36+
public:
37+
int publ2;
38+
39+
HasPrivatePublicProtectedFields(int i1, int i2, int i3, int i4, int i5,
40+
int i6)
41+
: priv1(i1), publ1(i2), prot1(i3), prot2(i4), priv2(i5),
42+
publ2(i6) {}
43+
};
44+
45+
struct Outer {
46+
private:
47+
HasPrivatePublicProtectedFields privStruct;
48+
49+
public:
50+
HasPrivatePublicProtectedFields publStruct;
51+
52+
Outer() : privStruct(1, 2, 3, 4, 5, 6), publStruct(7, 8, 9, 10, 11, 12) {}
53+
};
54+
55+
#endif
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// RUN: %target-run-simple-swift(-cxx-interoperability-mode=default -Xfrontend -disable-availability-checking -I %S/Inputs)
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: reflection
5+
6+
@_spi(Reflection) import Swift
7+
import SimpleStructs
8+
import StdlibUnittest
9+
10+
func checkFieldsWithKeyPath<T>(
11+
of type: T.Type,
12+
options: _EachFieldOptions = [],
13+
fields: [String: PartialKeyPath<T>]
14+
) {
15+
var count = 0
16+
17+
_forEachFieldWithKeyPath(of: T.self, options: options) {
18+
charPtr, keyPath in
19+
count += 1
20+
21+
let fieldName = String(cString: charPtr)
22+
if fieldName == "" {
23+
expectTrue(false, "Empty field name")
24+
return true
25+
}
26+
guard let checkKeyPath = fields[fieldName] else {
27+
expectTrue(false, "Unexpected field '\(fieldName)'")
28+
return true
29+
}
30+
31+
expectTrue(checkKeyPath == keyPath)
32+
return true
33+
}
34+
35+
expectEqual(fields.count, count)
36+
}
37+
38+
var ForEachFieldTestSuite = TestSuite("ForEachField")
39+
40+
ForEachFieldTestSuite.test("HasPrivateFieldsOnly") {
41+
checkFieldsWithKeyPath(
42+
of: HasPrivateFieldsOnly.self,
43+
fields: [:]
44+
)
45+
}
46+
47+
ForEachFieldTestSuite.test("HasPublicFieldsOnly") {
48+
checkFieldsWithKeyPath(
49+
of: HasPublicFieldsOnly.self,
50+
fields: [
51+
"publ1": \HasPublicFieldsOnly.publ1,
52+
"publ2": \HasPublicFieldsOnly.publ2
53+
]
54+
)
55+
}
56+
57+
ForEachFieldTestSuite.test("HasPrivatePublicProtectedFields") {
58+
checkFieldsWithKeyPath(
59+
of: HasPrivatePublicProtectedFields.self,
60+
fields: [
61+
"publ1": \HasPrivatePublicProtectedFields.publ1,
62+
"publ2": \HasPrivatePublicProtectedFields.publ2
63+
]
64+
)
65+
}
66+
67+
ForEachFieldTestSuite.test("Outer") {
68+
checkFieldsWithKeyPath(
69+
of: Outer.self,
70+
fields: [
71+
"publStruct": \Outer.publStruct
72+
]
73+
)
74+
}
75+
76+
runAllTests()
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-run-simple-swift(-cxx-interoperability-mode=default -Xfrontend -disable-availability-checking -I %S/Inputs) | %FileCheck %s
2+
3+
// REQUIRES: executable_test
4+
5+
import SimpleStructs
6+
7+
func printCxxStructPrivateFields() {
8+
let s = HasPrivateFieldsOnly(1, 2)
9+
print(s)
10+
}
11+
12+
func printCxxStructPublicFields() {
13+
let s = HasPublicFieldsOnly(1, 2)
14+
print(s)
15+
}
16+
17+
func printCxxStructPrivatePublicProtectedFields() {
18+
let s = HasPrivatePublicProtectedFields(1, 2, 3, 4, 5, 6)
19+
print(s)
20+
}
21+
22+
func printCxxStructNested() {
23+
let s = Outer()
24+
print(s)
25+
}
26+
27+
printCxxStructPrivateFields()
28+
// CHECK: HasPrivateFieldsOnly()
29+
30+
printCxxStructPublicFields()
31+
// CHECK: HasPublicFieldsOnly(publ1: 1, publ2: 2)
32+
33+
printCxxStructPrivatePublicProtectedFields()
34+
// CHECK: HasPrivatePublicProtectedFields(publ1: 2, publ2: 6)
35+
36+
printCxxStructNested()
37+
// 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

0 commit comments

Comments
 (0)