-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[cxx-interop] Re-implement namespaces using enums + extensions. #35085
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
// This way, if we have multiple modules that all open the same namespace, | ||
// we won't import multiple enums with the same name in swift. | ||
if (extensionDC->getContextKind() == DeclContextKind::FileUnit) | ||
dc = Impl.ImportedHeaderUnit; |
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.
Originally, I created a separate __Cxx
module for this, but I think ImportedHeaderUnit
(__ObjC
) works fine, so there's really no reason to add another one. Once we decide how we want to handle apps that use both Objective-C and C++ interop, we might conditionally name this __Cxx
instead of __ObjC
.
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 the only question is, whether it is possible to have a conflict between any existing entity in __ObjC
and these new namespace-enums. I think a conflict is only possible if someone had an actual C enum in the global namespace, and a top-level namespace of the same name; however that is not allowed in C++.
So using __ObjC
makes sense. I think even when we enable C++ interop by default, the module can continue to be called __ObjC
, because why break ABI.
header "namespaces.h" | ||
} | ||
|
||
module NamespaceDefs { |
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.
What does 'defs' mean and why does the first module not have it in the name? I'd suggest to name the two modules Namespaces1
and Namespaces2
, they seem to be siblings in the sense that one is not more important than the other.
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.
Yeah, I fixed this. I meant to include the one header in the other so that we could test with two headers (because then the decls sometimes become unlinked, i.e., redecls isn't all the decls). Do the names make sense now?
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 I see. Could you add a comment to namespace-defs.h that explains that the only purpose of that header is to define APIs that are forward declared in namespaces.h?
95998c9
to
44ffb2f
Compare
ba9a7bd
to
6f5c6e3
Compare
@swift-ci please test Windwos. |
@swift-ci please test Windows. |
@swift-ci please test Windows platform. |
@swift-ci test Windows |
6f5c6e3
to
e578027
Compare
@swift-ci test Windows |
lib/ClangImporter/ImportDecl.cpp
Outdated
// This will be re-set as the enum after we return from | ||
// "VisitNamespaceDecl." |
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 will be re-set as the enum after we return from | |
// "VisitNamespaceDecl." | |
// This will be reset to the EnumDecl after we return from | |
// VisitNamespaceDecl. |
But by whom?
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.
A call to importDecl
will "cache" the imported decl (the result from the visitor). We set Impl.ImportedDecls[redecl]
here so that subdecls will have the correct decl context, but once this function returns an enum, importDecl
's implementation will "cache" or reset Impl.ImportedDecls[decl]
with the imported enum.
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! Please add this information to the comment.
"This will be reset" -> "importDecl
will reset the imported decl to the EnumDecl..."
e578027
to
929ca8e
Compare
@gribozavr Thanks for the review! I think I addressed all you're comments. Let me know what you think. |
@swift-ci please smoke test. |
} // namespace ParentWithChildWithFunctions | ||
|
||
namespace ParentWithChildWithFunctions { | ||
inline void ChildWithFunctions::forwardDeclared(){}; |
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.
inline void ChildWithFunctions::forwardDeclared(){}; | |
inline void ChildWithFunctions::forwardDeclared() {} |
|
||
namespace SecondWithSameName { | ||
inline int hasSameName() { return 2; } | ||
} // namespace SecondWithSameName |
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 you group this test together with UniqueOuterName1
? It is essentially a very similar idea, but with less nesting.
inline int hasSameName() { return 2; } | ||
} // namespace SecondWithSameName | ||
|
||
#endif // TEST_INTEROP_CXX_NAMESPACE_INPUTS_NAMESPACE_H |
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 sorry, this is going to be a big request, but I hope you agree that it is going to be worth it.
I'm getting a feeling that this test header is hard to understand because it tries to test many combinations of many complex features, and there is no visible structure in this header that makes it obvious that we are testing all necessary combinations. Also, naming is a bit ad-hoc, which makes it also harder to understand what is covered and what isn't.
I'd suggest to maybe restructure it into sections (or separate headers!) based on the features we're testing.
For example, the first section (or the first header) would test free functions in namespaces. Define a namespace structure:
namespace FunctionsNS1 {/* here */}
namespace FunctionsNS2 {/* here */}
namespace FunctionsNS1 {
namespace FunctionsNS2 {/* here */}
}
namespace FunctionsNS2 {
namespace FunctionsNS1 {/* here */}
}
Now, inside every point marked "here" insert the same repertoire of functions: defined in .cc file, defined inline, forward declared and defined as out of line later, marked static, etc. Use repeated function names so that we can test that the importer does not confuse any two of them. Have each function return a unique const char *
with its fully qualified name so that execution tests can easily validate that they are calling the right function. Call every function in the irgen test and check its mangled name. Call each function in the execution test and check the string that it returns. Check the complete imported API in the module interface test.
Repeat similarly for function templates, classes, class templates, typedefs.
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.
@gribozavr you were correct on both accounts: this was a big overhaul of the tests and it was well worth it.
I ran into several problems while adding these tests, luckily none were related to namespaces themselves, per se. Anyway, I'm going to try to remove the reliance on #34993 to make the diff easier to read.
Let me know what you think. I really hope the tests are easier to read now :)
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 probably know this already but just in case...) To make the diff easier to read, you can click on the commit where all this is implemented, then the other two commits (which are both separate PRs) won't distract from what's actually being implemented here.
929ca8e
to
76de0a8
Compare
76de0a8
to
45fc817
Compare
45fc817
to
884efc0
Compare
Sounds good. I'll make this a separate change. |
884efc0
to
4c3b4d5
Compare
@swift-ci please smoke test. |
4c3b4d5
to
97f844a
Compare
@swift-ci please smoke test Windows. |
97f844a
to
d5707c8
Compare
@swift-ci please smoke test Windows. |
d5707c8
to
ba695b6
Compare
@swift-ci please smoke test Windows. |
1 similar comment
@swift-ci please smoke test Windows. |
ba695b6
to
7a89d9e
Compare
@@ -2,6 +2,9 @@ | |||
// RUN: %target-swiftxx-frontend -emit-module -o %t/SwiftClassTemplateInNamespaceModule.swiftmodule %S/Inputs/SwiftClassTemplateInNamespaceModule.swift -I %S/Inputs -enable-library-evolution -swift-version 5 | |||
// RUN: %target-swift-ide-test -print-module -module-to-print=SwiftClassTemplateInNamespaceModule -I %t/ -source-filename=x -enable-cxx-interop | %FileCheck %s | |||
|
|||
// We need to be able to properly serialize the __ObjC module holding the "Space" enum. | |||
// REQUIRES: SR-XXXXX |
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 test is failing because we can't find the enum decl because it's in the __ObjC
module which isn't getting serialized. I think the best solution is to serialize that module as well and save it along side whatever the Swift module is that we're saving (SwiftClassTemplateInNamespaceModule
in this case). But that's going to be a big/involved change, so I'd rather get this in first, and figure that out later.
If we agree that this is the best path forward, I'll create an SR and update the comment.
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.
The contents of the __ObjC
module should be generated by the importer, so I'm not yet convinced that serializing it is the right approach. I'd suggest to update the comment to say that there is a bug, and put the details into jira.
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.
OK good to know. I'll have to spend some more time thinking about this, then :) Here's the bug I filed: SR-14211.
@swift-ci please smoke test. |
@swift-ci please test Windows platform. |
4 similar comments
@swift-ci please test Windows platform. |
@swift-ci please test Windows platform. |
@swift-ci please test Windows platform. |
@swift-ci please test Windows platform. |
@gribozavr friendly ping. |
The tests are passing on macOS and the windows tests are just a matter of updating the regex (I'll have to fire up the VM for that, though, so I'm waiting for this to get approved first). |
@gribozavr, thank you so much for all you're help/guidance with this patch! |
1ad2c83
to
4c5cbbc
Compare
C++ namespaces are module-independent, but enums are owned by their module's in Swift. So, to prevent declaring two enums with the same name, this patch implements a new approach to namespaces: enums with extensions. Here's an example: ``` // Module A namespace N { void test1(); } // Module B namespace N { void test2(); } // __ObjC module enum N { } // Swift module A extension N { func test1() } // Swift module B extension N { func test1() } ``` Thanks to @gribozavr for the great idea.
4c5cbbc
to
bd96959
Compare
@swift-ci please test. |
C++ namespaces are module-independent, but enums are owned by their module's in Swift. So, to prevent declaring two enums with the same name, this patch implements a new approach to namespaces: enums with extensions.
Here's an example:
Thanks to @gribozavr for the great idea.