[cxx-interop] Support nested structs

It is really involved to change how methods and classes are emitted into
the header so this patch introduces the impression of nested structs
through using statements and still emits the structs themselves as top
level structs. It emits them in their own namespace to avoid name
collisions. This patch also had to change some names to be fully
qualified to avoid some name lookup errors in case of nested structs.
Moreover, nesting level of 3 and above requires C++17 because it relies
on nested namespaces. Only nested structs are supported, not nested
classes.

Since this patch is already started to grow quite big, I decided to put
it out for reviews and plan to address some of the shortcomings in a
follow-up PR.

rdar://118793469
This commit is contained in:
Gabor Horvath
2024-07-24 10:30:51 +02:00
parent 3f916f504e
commit 94b466656e
45 changed files with 854 additions and 659 deletions

View File

@@ -31,6 +31,7 @@
#include "swift/Basic/Assertions.h"
#include "swift/Basic/SourceManager.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/Strings.h"
#include "clang/AST/Decl.h"
@@ -218,7 +219,10 @@ public:
return state.first == EmissionState::Defined;
}
bool require(const TypeDecl *D) {
bool require(const TypeDecl *D) { return requireTypes(D, declsToWrite); }
template <typename T>
bool requireTypes(const TypeDecl *D, T &types) {
if (addImport(D)) {
seenTypes[D] = { EmissionState::Defined, true };
return true;
@@ -229,7 +233,7 @@ public:
case EmissionState::NotYetDefined:
case EmissionState::DefinitionRequested:
state.first = EmissionState::DefinitionRequested;
declsToWrite.push_back(D);
types.push_back(D);
return false;
case EmissionState::Defined:
return true;
@@ -364,7 +368,7 @@ public:
}
bool hadAnyDelayedMembers = false;
SmallVector<ValueDecl *, 4> nestedTypes;
SmallVector<const ValueDecl *, 4> nestedTypes;
for (auto member : members) {
PrettyStackTraceDecl loopEntry("printing for member", member);
auto VD = dyn_cast<ValueDecl>(member);
@@ -372,15 +376,23 @@ public:
continue;
// Catch nested types and emit their definitions /after/ this class.
if (isa<TypeDecl>(VD)) {
// Don't emit nested types that are just implicitly @objc.
// You should have to opt into this, since they are even less
// namespaced than usual.
if (std::any_of(VD->getAttrs().begin(), VD->getAttrs().end(),
[](const DeclAttribute *attr) {
return isa<ObjCAttr>(attr) && !attr->isImplicit();
})) {
nestedTypes.push_back(VD);
if (const auto *TD = dyn_cast<TypeDecl>(VD)) {
if (outputLangMode == OutputLanguageMode::Cxx) {
if (!isa<TypeAliasDecl>(TD) && !isStringNestedType(VD, "UTF8View") &&
!isStringNestedType(VD, "Index")) {
forwardDeclareType(TD);
requireTypes(TD, nestedTypes);
}
} else {
// Don't emit nested types that are just implicitly @objc.
// You should have to opt into this, since they are even less
// namespaced than usual.
if (std::any_of(VD->getAttrs().begin(), VD->getAttrs().end(),
[](const DeclAttribute *attr) {
return isa<ObjCAttr>(attr) && !attr->isImplicit();
})) {
nestedTypes.push_back(VD);
}
}
continue;
}
@@ -1060,11 +1072,14 @@ EmittedClangHeaderDependencyInfo swift::printModuleContentsAsCxx(
os << "}\n";
}
os << "#pragma clang diagnostic push\n";
os << "#pragma clang diagnostic ignored \"-Wreserved-identifier\"\n";
// Construct a C++ namespace for the module.
ClangSyntaxPrinter(os).printNamespace(
[&](raw_ostream &os) { ClangSyntaxPrinter(os).printBaseName(&M); },
[&](raw_ostream &os) { os << moduleOS.str(); },
ClangSyntaxPrinter::NamespaceTrivia::AttributeSwiftPrivate, &M);
os << "#pragma clang diagnostic pop\n";
if (M.isStdlibModule()) {
os << "#pragma clang diagnostic pop\n";