-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[cxx-interop] Import default public ctor of C++ foreign ref types as Swift Initializer #79986
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
c225a0a
to
bc612bf
Compare
This comment was marked as resolved.
This comment was marked as resolved.
52235c1
to
6c9b82a
Compare
6c9b82a
to
2fcb83e
Compare
2fcb83e
to
2dcf154
Compare
695d2b2
to
3f507ef
Compare
lib/ClangImporter/ImportDecl.cpp
Outdated
if (auto *cxxRecordDecl = const_cast<clang::CXXRecordDecl *>( | ||
dyn_cast<clang::CXXRecordDecl>(decl))) { | ||
bool hasUserProvidedStaticFactory = false; | ||
for (const auto *method : cxxRecordDecl->methods()) { |
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.
Do we have a non-empty ctors
array for foreign reference types? Actually, I wonder if it would be better to not even try to import the constructors above for foreign reference types. If we skip the constructors in the for (auto m : decl->decls()) {
loop for foreign reference types the ctors
array will only have an element if one of the static members is annotated with init
. This way we can both skip some redundant work (not importing constructors we would not use) and simplify the code (do not need another pass through all the methods).
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
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.
If we skip the constructors in the for (auto m : decl->decls()) { loop for foreign reference types the ctors array will only have an element if one of the static members is annotated with init
I am not sure if that is the best way to handle this. I checked that the ctors array is same whether or not the FRT has user provided swift_name("init()"))
method. Annotated user provided factories are not added to ctors anyway so we may have to think of some other way to factor out whether user provided factory exist or not for cxxRecordDecl
This way we can both skip some redundant work (not importing constructors we would not use) and simplify the code (do not need another pass through all the methods).
If this is not a big concern. can we optimise for this in a follow up patch?
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 OK doing a different strategy for checking if there is a user annotated factory and importing ctors for foreign references more lazily in a follow-up patch.
@@ -464,6 +464,10 @@ EXPERIMENTAL_FEATURE(AssumeResilientCxxTypes, true) | |||
/// Import inherited non-public members when importing C++ classes. | |||
EXPERIMENTAL_FEATURE(ImportNonPublicCxxMembers, true) | |||
|
|||
/// Synthesize static factory methods for C++ foreign reference types and import | |||
/// them as Swift initializers. | |||
EXPERIMENTAL_FEATURE(CXXForeignReferenceTypeInitializers, true) |
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.
Now that all the major concerns are addressed in this PR, I would even be OK to not have the feature flag and have this feature on by default for default constructors.
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.
As per some discussions offline, we might want to invert this flag so users can opt out of this feature if it causes any trouble.
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.
we might want to invert this flag so users can opt out of this feature if it causes any trouble.
I seem like we can use the same feature flag to either enable or disable a feature.
Disable: -disable-experimental-feature CXXForeignReferenceTypeInitializers
Enable: -enable-experimental-feature CXXForeignReferenceTypeInitializers
I tested this at least for the features which are marked as EXPERIMENTAL_FEATURE(<feature-name>, true)
I am not sure what would be the semantics of EXPERIMENTAL_FEATURE(<feature-name>, false)
I am not sure what is the best thing to do to call this feature as ready. My inclination is towards completely removing the feature flag if it feels stable enough and we are happy with the test coverage.
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.
My inclination is towards completely removing the feature flag if it feels stable enough and we are happy with the test coverage.
I think the compilation error with operator new and immortal references is a blocker to turn this on by default. Until those are resolved there will be user code that is broken by this patch. I.e., it was compiling fine before but stopped compiling after.
ctor->getAccess() != clang::AS_private && | ||
ctor->getAccess() != clang::AS_protected) { |
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.
Could this be ctor->getAccess() == clang::AS_public
?
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'm actually wondering, should we even need to check this? I think there's utility to importing non-public static factories as Swift-private initializers. We just need to make sure we set the synthesized access specifiers appropriately (rather than simply assuming and hard-coding clang::AS_public
/AccessLevel::Public
).
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. Initially, I was thinking of declaring a ctor private as a way to opt out of this feature if the users run into some trouble. But in case we invert the experimental flag to be an opt out, we might not need to check the access.
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.
Could this be ctor->getAccess() == clang::AS_public?
I am not sure what to do for clang::AS_none
. @j-hui told me that AS_none
is for global 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.
We just need to make sure we set the synthesized access specifiers appropriately (rather than simply assuming and hard-coding clang::AS_public/AccessLevel::Public).
Any thoughts about what to do when operator new is private/protected (for both overloaded and global operator new) ?
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 there's utility to importing non-public static factories as Swift-private initializers.
I am not sure if we import private or public static factories which call into private ctor in case of foreign reference types. Maybe we should keep this patch about public contractors, public new operator and come back to the other access specifier problem later in a follow up patch. (also address the clang::AS_none
aka global operator new peculiarities).
5c3ddc9
to
06bf551
Compare
3b33855
to
38987fb
Compare
@swift-ci please smoke test |
lib/ClangImporter/ImportDecl.cpp
Outdated
llvm::any_of(method->template specific_attrs< | ||
clang::SwiftNameAttr>(), | ||
[](const auto *attr) { | ||
return attr->getName().str().find( |
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 we should thighten this condition here. This would also trigger for something like "myfunc(init:foo:)", so something that is not imported as an initializer.
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.
Changing the check to use .startswith("init(")
. But we may re-visit this comment in a follow up patch if we can come up with an ever better check 🤔
@swift-ci please smoke test macos |
…Swift Initializer rdar://147529406
38987fb
to
7191d19
Compare
@swift-ci please smoke test |
@swift-ci please test macOS |
Enabling auto-merge on this patch with these points in mind:
All comments which are not hidden or marked as resolved could be revisited either for refactoring, stress testing or other improvements later. All resolved/hidden comments have been addressed. rdar://148286196 |
…pes as Swift Initializer (#80449) Extends PR #79986 by adding support for calling parameterized C++ initializers from Swift. This patch synthesizes static factory methods corresponding to C++ parameterized constructors, allowing Swift to call them as Swift initializers (e.g., init(_:), init(_:_:), etc.). This patch also aded tests and logic to make sure that we emit no additional diagnostics when a C++ foreign ref type is just referred from Swift and its initializer is not explicitly called. rdar://148285251
…pes as Swift Initializer (#80449) Extends PR #79986 by adding support for calling parameterized C++ initializers from Swift. This patch synthesizes static factory methods corresponding to C++ parameterized constructors, allowing Swift to call them as Swift initializers (e.g., init(_:), init(_:_:), etc.). This patch also aded tests and logic to make sure that we emit no additional diagnostics when a C++ foreign ref type is just referred from Swift and its initializer is not explicitly called. rdar://148285251
Building on top of PR #79288, this update synthesizes a static factory method using the default new operator, with a call to the default constructor expression for C++ foreign reference types, and imports them as Swift initializers.
rdar://147529406