-
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
Conversation
@swift-ci please smoke test |
f36ff56
to
a39010b
Compare
@swift-ci please smoke test |
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.
This is great, thanks for tracking this down! I am not super confident about some details, asked some questions inline. Maybe we should get someone more familiar with type metadata reviewed this PR.
@@ -1845,7 +1845,7 @@ namespace { | |||
|
|||
void addLayoutInfo() { | |||
// uint32_t NumFields; | |||
B.addInt32(getNumFields(getType())); |
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.
I wonder if we need to do something similar for ClassContextDescriptorBuilder too.
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 further
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.
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 in StructContextDescriptorBuilder
/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.
lib/IRGen/StructMetadataVisitor.h
Outdated
@@ -64,7 +65,8 @@ template <class Impl> class StructMetadataVisitor | |||
// Struct field offsets. | |||
asImpl().noteStartOfFieldOffsets(); | |||
for (VarDecl *prop : Target->getStoredProperties()) | |||
asImpl().addFieldOffset(prop); | |||
if (!isPrivateField(prop)) |
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 can get out of sync with emitInitializeFieldOffsetVector
here.
a39010b
to
a49738e
Compare
@swift-ci please smoke test |
a49738e
to
abd2549
Compare
@swift-ci please smoke test |
// CHECK-linux-gnu: 42 | ||
// CHECK-linux-gnu: shared_ptr<CInt>() | ||
printStdSharedPtrOfString() | ||
// CHECK-macosx: basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>() |
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.
Alternatively, you could add regexes that match on all platforms. I don't have a preference.
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.
Thanks for the suggestion!
Hopefully we will soon fix the conformance of std::string
to CustomStringConvertible
. Then we can remove all these os options, since the value of the string will be printed instead of the type name.
That said, I don't mind switching to regex
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.
Good point, the string one is temporary (maybe not the shared_ptr one though). Let's leave it as is.
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.
Oh right!
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 think using regexes would be a bit more reliable here, since that would ensure the correct behavior on platforms that aren't listed here, e.g. Android or FreeBSD. The regex could be really permissive: something like {{.*}}.basic_string<CChar, {{.*}}>
should do the trick.
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.
Sure, I'll make this change.
abd2549
to
2a99a7d
Compare
@swift-ci please smoke test |
// CHECK-linux-gnu: 42 | ||
// CHECK-linux-gnu: shared_ptr<CInt>() | ||
printStdSharedPtrOfString() | ||
// CHECK-macosx: basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>() |
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.
Good point, the string one is temporary (maybe not the shared_ptr one though). Let's leave it as is.
2a99a7d
to
bdacdd0
Compare
@swift-ci please smoke test |
@swift-ci Please test macOS platform |
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.
This looks great, thanks!
I only have a couple of comments on testing.
// CHECK-linux-gnu: 42 | ||
// CHECK-linux-gnu: shared_ptr<CInt>() | ||
printStdSharedPtrOfString() | ||
// CHECK-macosx: basic_string<CChar, std.__1.char_traits<CChar>, std.__1.allocator<CChar>>() |
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 think using regexes would be a bit more reliable here, since that would ensure the correct behavior on platforms that aren't listed here, e.g. Android or FreeBSD. The regex could be really permissive: something like {{.*}}.basic_string<CChar, {{.*}}>
should do the trick.
bdacdd0
to
3efa921
Compare
@swift-ci please smoke test |
3efa921
to
f21499c
Compare
@swift-ci please smoke test macOS platform |
@swift-ci please smoke test |
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.
With the changes this looks good to me.
|
||
public: | ||
HasPrivateFieldsOnly(int i, std::string s) : privInt(i), privStr(s) {} | ||
}; |
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 recommend also testing:
- some records with
protected
access (just to be thorough; these are also imported asprivate
) - some structs with inherited fields, covering
public
/protected
/private
inheritance +using BaseClass::protected_field;
- A computed property synthesized from
SWIFT_COMPUTED_PROPERTY
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.
You might be able to reuse some of the structs I defined in /Users/j-hui/Documents/open/swift/test/Interop/Cxx/class/access/Inputs/{non-public.h,non-public-inheritance.h,using-non-public.h}
for testing, for the latter two I primarily focused on methods. If you throw in some fields you should be able to reuse the inheritance patterns I cover there.
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 added some tests for records with protected
access. Regarding the rest of the tests you suggested:
some structs with inherited fields, covering
public
/protected
/private
inheritance +using BaseClass::protected_field;
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.
A computed property synthesized from
SWIFT_COMPUTED_PROPERTY
These are also not getting printed, just like computed properties in Swift code.
#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_SIMPLE_STRUCTS_H | ||
#define TEST_INTEROP_CXX_CLASS_INPUTS_SIMPLE_STRUCTS_H | ||
|
||
#include <string> |
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.
nit: If we're trying to keep these structs simple in this test, maybe we should stick to primitive data types, and not have this test depend on the implementation of std::string
? We should still absolutely test that but I think that should happen in a different test.
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.
That said, do test nested printing, i.e., use a struct Inner
that has a mix of public
and private
fields as the fields of another struct Outer
, and try printing Outer
.
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.
Done!
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.
nit: since you're no longer using std::string
in this header file, you can probably delete this #include
statement now 🙂
test/Interop/Cxx/class/print.swift
Outdated
|
||
func printCxxStructPrivateAndPublicFields() { | ||
let s = HasPrivateAndPublicFields(24, 42, "Hello", "World") | ||
print(s) |
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.
Are there any other operations other than printing that relies on metadata? Perhaps we should test those 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.
Not sure, I'm not very familiar with metadata. Do you have any operations in mind?
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.
print
covers it pretty well if the type doesn't conform to any of the pretty-printing protocols. You might also test _forEachFieldWithKeyPath
.
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'll take a look at that, thanks!
f21499c
to
2b59c48
Compare
@swift-ci please smoke test |
2b59c48
to
eca94bf
Compare
@swift-ci please smoke test |
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.
This looks great, thanks for addressing my comments @susmonteiro ! There's just a few nits concerning the test setup, but those can be fixed in a follow-up if you'd like.
#ifndef TEST_INTEROP_CXX_CLASS_INPUTS_SIMPLE_STRUCTS_H | ||
#define TEST_INTEROP_CXX_CLASS_INPUTS_SIMPLE_STRUCTS_H | ||
|
||
#include <string> |
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.
nit: since you're no longer using std::string
in this header file, you can probably delete this #include
statement now 🙂
@@ -0,0 +1,40 @@ | |||
// RUN: %empty-directory(%t) | |||
// RUN: %target-build-swift %s -o %t/a.out -Xfrontend -enable-experimental-cxx-interop -I %S/Inputs |
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.
// RUN: %target-build-swift %s -o %t/a.out -Xfrontend -enable-experimental-cxx-interop -I %S/Inputs | |
// RUN: %target-build-swift %s -o %t/a.out -Xfrontend -cxx-interoperability-mode=default -I %S/Inputs |
@@ -0,0 +1,40 @@ | |||
// RUN: %empty-directory(%t) |
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.
nit: is there any reason not to simplify this to something like?
// RUN: %target-run-simple-swift(-cxx-interoperability-mode=default -Xfrontend -disable-availability-checking -I %S/Inputs) | %FileCheck %s
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.
No, there isn't. Thanks for the suggestion!
@@ -0,0 +1,90 @@ | |||
// RUN: %empty-directory(%t) |
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.
same question about using %target-run-simple-swift
here
@@ -0,0 +1,90 @@ | |||
// RUN: %empty-directory(%t) | |||
// RUN: %target-build-swift %s -o %t/a.out -Xfrontend -enable-experimental-cxx-interop -I %S/Inputs |
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.
nit:
// RUN: %target-build-swift %s -o %t/a.out -Xfrontend -enable-experimental-cxx-interop -I %S/Inputs | |
// RUN: %target-build-swift %s -o %t/a.out -Xfrontend -cxx-interoperability-mode=default -I %S/Inputs |
eca94bf
to
72b13b3
Compare
@swift-ci please smoke test |
@swift-ci please smoke test |
@swift-ci please smoke test windows platform |
…/metadata-private-fields" This reverts commit e805d93.
In #78467 and #78961, we stopped emitting metadata for private C++ fields. However, this created a mismatch between the fields emitted and the number of fields + their offsets in the StructDescriptor.
rdar://147263490