Skip to content

Commit a39010b

Browse files
committed
[cxx-interop] Fix metadata mismatch regarding fields of structs
1 parent 5674b6e commit a39010b

11 files changed

+245
-28
lines changed

lib/IRGen/Field.h

+10
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,22 @@ struct Field {
9696
bool operator!=(Field other) const { return declOrKind != other.declOrKind; }
9797
};
9898

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

113+
unsigned countExportableFields(IRGenModule &IGM, const NominalTypeDecl *type);
114+
105115
} // end namespace irgen
106116
} // end namespace swift
107117

lib/IRGen/GenMeta.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1845,7 +1845,7 @@ namespace {
18451845

18461846
void addLayoutInfo() {
18471847
// uint32_t NumFields;
1848-
B.addInt32(getNumFields(getType()));
1848+
B.addInt32(countExportableFields(IGM, getType()));
18491849

18501850
// uint32_t FieldOffsetVectorOffset;
18511851
B.addInt32(FieldVectorOffset / IGM.getPointerSize());

lib/IRGen/GenReflection.cpp

+4-26
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

+23
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,18 @@ unsigned irgen::getNumFields(const NominalTypeDecl *target) {
589589
return numFields;
590590
}
591591

592+
bool irgen::isPrivateField(VarDecl *decl) {
593+
return (decl->getClangDecl() &&
594+
decl->getFormalAccess() == AccessLevel::Private);
595+
}
596+
597+
bool irgen::isExportableField(Field field) {
598+
if (field.getKind() == Field::Kind::Var && isPrivateField(field.getVarDecl()))
599+
return false;
600+
// All other fields are exportable
601+
return true;
602+
}
603+
592604
void irgen::forEachField(IRGenModule &IGM, const NominalTypeDecl *typeDecl,
593605
llvm::function_ref<void(Field field)> fn) {
594606
auto classDecl = dyn_cast<ClassDecl>(typeDecl);
@@ -610,6 +622,17 @@ void irgen::forEachField(IRGenModule &IGM, const NominalTypeDecl *typeDecl,
610622
}
611623
}
612624

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

lib/IRGen/StructMetadataVisitor.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef SWIFT_IRGEN_STRUCTMETADATALAYOUT_H
1818
#define SWIFT_IRGEN_STRUCTMETADATALAYOUT_H
1919

20+
#include "Field.h"
2021
#include "NominalMetadataVisitor.h"
2122
#include "swift/AST/IRGenOptions.h"
2223

@@ -64,7 +65,8 @@ template <class Impl> class StructMetadataVisitor
6465
// Struct field offsets.
6566
asImpl().noteStartOfFieldOffsets();
6667
for (VarDecl *prop : Target->getStoredProperties())
67-
asImpl().addFieldOffset(prop);
68+
if (!isPrivateField(prop))
69+
asImpl().addFieldOffset(prop);
6870

6971
asImpl().noteEndOfFieldOffsets();
7072

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

+6
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+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 privInt;
9+
std::string privStr;
10+
11+
public:
12+
HasPrivateFieldsOnly(int i, std::string s) : privInt(i), privStr(s) {}
13+
};
14+
15+
struct HasPublicFieldsOnly {
16+
int pubInt;
17+
std::string pubStr;
18+
19+
HasPublicFieldsOnly(int i, std::string s) : pubInt(i), pubStr(s) {}
20+
};
21+
22+
struct HasIntFieldsOnly {
23+
private:
24+
int a = 1;
25+
26+
public:
27+
int b = 2;
28+
29+
private:
30+
int c = 3;
31+
32+
public:
33+
int d = 4;
34+
};
35+
36+
struct HasPrivateAndPublicFields {
37+
private:
38+
std::string privStr;
39+
40+
public:
41+
int pubInt;
42+
43+
private:
44+
int privInt;
45+
46+
public:
47+
std::string pubStr;
48+
49+
HasPrivateAndPublicFields(int i1, int i2, std::string s1, std::string s2)
50+
: privStr(s2), pubInt(i2), privInt(i1), pubStr(s1) {}
51+
};
52+
53+
#endif

test/Interop/Cxx/class/print.swift

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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(42, "Hello")
12+
print(s)
13+
}
14+
15+
func printCxxStructPublicFields() {
16+
let s = HasPublicFieldsOnly(42, "Hello")
17+
print(s)
18+
}
19+
20+
func printCxxStructIntFields() {
21+
let s = HasIntFieldsOnly()
22+
print(s)
23+
}
24+
25+
func printCxxStructPrivateAndPublicFields() {
26+
let s = HasPrivateAndPublicFields(24, 42, "Hello", "World")
27+
print(s)
28+
}
29+
30+
printCxxStructPrivateFields()
31+
// CHECK: HasPrivateFieldsOnly()
32+
33+
printCxxStructIntFields()
34+
// CHECK: HasIntFieldsOnly(b: 2, d: 4)
35+
36+
#if os(macOS)
37+
// std::string has a different representation in other OSes
38+
39+
printCxxStructPublicFields()
40+
// CHECK: HasPublicFieldsOnly(pubInt: 42, pubStr: __C.std.std::__1.basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>())
41+
printCxxStructPrivateAndPublicFields()
42+
// CHECK: HasPrivateAndPublicFields(pubInt: 42, pubStr: __C.std.std::__1.basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>())
43+
#endif

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

+6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ module MsvcUseVecIt {
6464
export *
6565
}
6666

67+
module StdlibTypes {
68+
header "stdlib-types.h"
69+
requires cplusplus
70+
export *
71+
}
72+
6773
module StdUniquePtr {
6874
header "std-unique-ptr.h"
6975
requires cplusplus
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#ifndef TEST_INTEROP_CXX_STDLIB_INPUTS_STD_TYPES_H
2+
#define TEST_INTEROP_CXX_STDLIB_INPUTS_STD_TYPES_H
3+
4+
#include <memory>
5+
#include <string>
6+
#include <vector>
7+
8+
std::unique_ptr<int> makeInt() { return std::make_unique<int>(42); }
9+
10+
std::unique_ptr<std::string> makeString() {
11+
return std::make_unique<std::string>("Unique string");
12+
}
13+
14+
std::shared_ptr<int> makeIntShared() { return std::make_unique<int>(42); }
15+
16+
std::shared_ptr<std::string> makeStringShared() {
17+
return std::make_unique<std::string>("Shared string");
18+
}
19+
20+
std::vector<int> makeVecOfInt() { return {1, 2, 3}; }
21+
22+
std::vector<std::string> makeVecOfString() { return {"Hello", "World"}; }
23+
24+
#endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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 StdlibTypes
9+
10+
func printStdString(s: std.string) {
11+
print(s)
12+
let swiftString = String(s)
13+
print(swiftString)
14+
}
15+
16+
func printStdUniquePtrOfInt() {
17+
let uint = makeInt()
18+
print(uint.pointee)
19+
}
20+
21+
func printStdSharedPtrOfInt() {
22+
let sint = makeIntShared()
23+
print(sint.pointee)
24+
print(sint)
25+
}
26+
27+
func printStdVectorOfInt() {
28+
let vecOfInt = makeVecOfInt()
29+
print(vecOfInt[0])
30+
print(vecOfInt)
31+
}
32+
33+
func printStdUniquePtrOfString() {
34+
let ustring = makeString()
35+
print(ustring.pointee)
36+
}
37+
38+
func printStdSharedPtrOfString() {
39+
let sstring = makeStringShared()
40+
print(sstring.pointee)
41+
print(sstring)
42+
}
43+
44+
func printStdVectorOfString() {
45+
let vecOfString = makeVecOfString()
46+
print(vecOfString[0])
47+
print(vecOfString)
48+
}
49+
50+
#if !os(Windows)
51+
// FIXME: We can't import std::unique_ptr or std::shared_ptr properly on Windows (https://github.com/apple/swift/issues/70226)
52+
printStdUniquePtrOfInt()
53+
// CHECK: 42
54+
printStdSharedPtrOfInt()
55+
// CHECK: 42
56+
#endif
57+
printStdVectorOfInt()
58+
// CHECK: 1
59+
60+
#if os(macOS)
61+
// std::string has a different representation in other OSes
62+
63+
printStdString(s: "Hello")
64+
// CHECK: basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>()
65+
// CHECK: Hello
66+
printStdUniquePtrOfString()
67+
// CHECK: basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>()
68+
printStdSharedPtrOfString()
69+
// CHECK: basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>()
70+
printStdVectorOfString()
71+
// CHECK: basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>()
72+
#endif

0 commit comments

Comments
 (0)