-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[cxx-interop] Fix metadata mismatch regarding fields of structs #81035
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_SIMPLE_STRUCTS_H | ||
#define TEST_INTEROP_CXX_CLASS_INPUTS_SIMPLE_STRUCTS_H | ||
|
||
struct HasPrivateFieldsOnly { | ||
private: | ||
int priv1; | ||
int priv2; | ||
|
||
public: | ||
HasPrivateFieldsOnly(int i1, int i2) : priv1(i1), priv2(i2) {} | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I recommend also testing:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You might be able to reuse some of the structs I defined in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added some tests for records with
Currently we don't print inherited fields. It's something we want to fix, but for now I decided not to add tests for this.
These are also not getting printed, just like computed properties in Swift code. |
||
|
||
struct HasPublicFieldsOnly { | ||
int publ1; | ||
int publ2; | ||
|
||
HasPublicFieldsOnly(int i1, int i2) : publ1(i1), publ2(i2) {} | ||
}; | ||
|
||
struct HasPrivatePublicProtectedFields { | ||
private: | ||
int priv1; | ||
|
||
public: | ||
int publ1; | ||
|
||
protected: | ||
int prot1; | ||
|
||
protected: | ||
int prot2; | ||
|
||
private: | ||
int priv2; | ||
|
||
public: | ||
int publ2; | ||
|
||
HasPrivatePublicProtectedFields(int i1, int i2, int i3, int i4, int i5, | ||
int i6) | ||
: priv1(i1), publ1(i2), prot1(i3), prot2(i4), priv2(i5), | ||
publ2(i6) {} | ||
}; | ||
|
||
struct Outer { | ||
private: | ||
HasPrivatePublicProtectedFields privStruct; | ||
|
||
public: | ||
HasPrivatePublicProtectedFields publStruct; | ||
|
||
Outer() : privStruct(1, 2, 3, 4, 5, 6), publStruct(7, 8, 9, 10, 11, 12) {} | ||
}; | ||
|
||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
// RUN: %target-run-simple-swift(-cxx-interoperability-mode=default -Xfrontend -disable-availability-checking -I %S/Inputs) | ||
|
||
// REQUIRES: executable_test | ||
// REQUIRES: reflection | ||
|
||
@_spi(Reflection) import Swift | ||
import SimpleStructs | ||
import StdlibUnittest | ||
|
||
func checkFieldsWithKeyPath<T>( | ||
of type: T.Type, | ||
options: _EachFieldOptions = [], | ||
fields: [String: PartialKeyPath<T>] | ||
) { | ||
var count = 0 | ||
|
||
_forEachFieldWithKeyPath(of: T.self, options: options) { | ||
charPtr, keyPath in | ||
count += 1 | ||
|
||
let fieldName = String(cString: charPtr) | ||
if fieldName == "" { | ||
expectTrue(false, "Empty field name") | ||
return true | ||
} | ||
guard let checkKeyPath = fields[fieldName] else { | ||
expectTrue(false, "Unexpected field '\(fieldName)'") | ||
return true | ||
} | ||
|
||
expectTrue(checkKeyPath == keyPath) | ||
return true | ||
} | ||
|
||
expectEqual(fields.count, count) | ||
} | ||
|
||
var ForEachFieldTestSuite = TestSuite("ForEachField") | ||
|
||
ForEachFieldTestSuite.test("HasPrivateFieldsOnly") { | ||
checkFieldsWithKeyPath( | ||
of: HasPrivateFieldsOnly.self, | ||
fields: [:] | ||
) | ||
} | ||
|
||
ForEachFieldTestSuite.test("HasPublicFieldsOnly") { | ||
checkFieldsWithKeyPath( | ||
of: HasPublicFieldsOnly.self, | ||
fields: [ | ||
"publ1": \HasPublicFieldsOnly.publ1, | ||
"publ2": \HasPublicFieldsOnly.publ2 | ||
] | ||
) | ||
} | ||
|
||
ForEachFieldTestSuite.test("HasPrivatePublicProtectedFields") { | ||
checkFieldsWithKeyPath( | ||
of: HasPrivatePublicProtectedFields.self, | ||
fields: [ | ||
"publ1": \HasPrivatePublicProtectedFields.publ1, | ||
"publ2": \HasPrivatePublicProtectedFields.publ2 | ||
] | ||
) | ||
} | ||
|
||
ForEachFieldTestSuite.test("Outer") { | ||
checkFieldsWithKeyPath( | ||
of: Outer.self, | ||
fields: [ | ||
"publStruct": \Outer.publStruct | ||
] | ||
) | ||
} | ||
|
||
runAllTests() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// RUN: %target-run-simple-swift(-cxx-interoperability-mode=default -Xfrontend -disable-availability-checking -I %S/Inputs) | %FileCheck %s | ||
|
||
// REQUIRES: executable_test | ||
|
||
import SimpleStructs | ||
|
||
func printCxxStructPrivateFields() { | ||
let s = HasPrivateFieldsOnly(1, 2) | ||
print(s) | ||
} | ||
|
||
func printCxxStructPublicFields() { | ||
let s = HasPublicFieldsOnly(1, 2) | ||
print(s) | ||
} | ||
|
||
func printCxxStructPrivatePublicProtectedFields() { | ||
let s = HasPrivatePublicProtectedFields(1, 2, 3, 4, 5, 6) | ||
print(s) | ||
} | ||
|
||
func printCxxStructNested() { | ||
let s = Outer() | ||
print(s) | ||
} | ||
|
||
printCxxStructPrivateFields() | ||
// CHECK: HasPrivateFieldsOnly() | ||
|
||
printCxxStructPublicFields() | ||
// CHECK: HasPublicFieldsOnly(publ1: 1, publ2: 2) | ||
|
||
printCxxStructPrivatePublicProtectedFields() | ||
// CHECK: HasPrivatePublicProtectedFields(publ1: 2, publ2: 6) | ||
|
||
printCxxStructNested() | ||
// CHECK: Outer(publStruct: {{.*}}.HasPrivatePublicProtectedFields(publ1: 8, publ2: 12)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we need to do something similar for
ClassContextDescriptorBuilder
too.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am less sure about
emitInitializeFieldOffsetVector
and co. Maybe those are good as is but maybe we want to ask someone familiar with type metadata.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I think you're right. I intend to check if we need a similar fix for classes and add those changes in a future PR
Regarding
emitInitializeFieldOffsetVector
, I'm also not sure. I'll investigate this furtherThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
emitInitializeFieldOffsetVector
should include all fields. We are using that information to handle values in value witness functions.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@drexin would it be a problem to have a discrepancy between the fields in
emitInitializeFieldOffsetVector
and the fields inStructContextDescriptorBuilder
/FieldTypeMetadataBuilder
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I am not sure. cc: @mikeash
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reflection relies on the indexes in the field offset vector and the indexes of the descriptor's fields lining up. In theory you could get away with having additional entries at the end of the field offset vector, but not in the middle, and not more field records than field offsets.