Skip to content

[cxx-interop] Support nested structs #75477

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

Merged
merged 1 commit into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/AST/SwiftNameTranslation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,8 @@ swift::cxx_translation::getDeclRepresentation(const ValueDecl *VD) {
return {Unsupported, UnrepresentableGeneric};
genericSignature = typeDecl->getGenericSignature();
}
// Nested types are not yet supported.
if (!typeDecl->hasClangNode() &&
// Nested classes are not yet supported.
if (isa<ClassDecl>(VD) && !typeDecl->hasClangNode() &&
isa_and_nonnull<NominalTypeDecl>(
typeDecl->getDeclContext()->getAsDecl()))
return {Unsupported, UnrepresentableNested};
Expand Down
41 changes: 38 additions & 3 deletions lib/PrintAsClang/ClangSyntaxPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
#include "swift/AST/TypeCheckRequests.h"
#include "swift/Basic/Assertions.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/NestedNameSpecifier.h"
#include "llvm/Support/Casting.h"

using namespace swift;
using namespace cxx_synthesis;
Expand Down Expand Up @@ -79,7 +81,6 @@ void ClangSyntaxPrinter::printModuleNamespaceQualifiersIfNeeded(

bool ClangSyntaxPrinter::printNominalTypeOutsideMemberDeclTemplateSpecifiers(
const NominalTypeDecl *typeDecl) {
// FIXME: Full qualifiers for nested types?
if (!typeDecl->isGeneric())
return true;
printGenericSignature(
Expand Down Expand Up @@ -126,6 +127,26 @@ void ClangSyntaxPrinter::printClangTypeReference(const clang::Decl *typeDecl) {
}
}

bool ClangSyntaxPrinter::printNestedTypeNamespaceQualifiers(
const ValueDecl *D) const {
bool first = true;
while (auto parent = dyn_cast_or_null<NominalTypeDecl>(
D->getDeclContext()->getAsDecl())) {
// C++ namespaces are imported as enums.
if (parent->hasClangNode() &&
isa<clang::NamespaceDecl>(parent->getClangNode().getAsDecl()))
break;
if (!first)
os << "::";
first = false;
os << "__";
printBaseName(parent);
os << "Nested";
D = parent;
}
return first;
}

void ClangSyntaxPrinter::printNominalTypeReference(
const NominalTypeDecl *typeDecl, const ModuleDecl *moduleContext) {
if (typeDecl->hasClangNode()) {
Expand All @@ -134,7 +155,8 @@ void ClangSyntaxPrinter::printNominalTypeReference(
}
printModuleNamespaceQualifiersIfNeeded(typeDecl->getModuleContext(),
moduleContext);
// FIXME: Full qualifiers for nested types?
if (!printNestedTypeNamespaceQualifiers(typeDecl))
os << "::";
ClangSyntaxPrinter(os).printBaseName(typeDecl);
if (typeDecl->isGeneric())
printGenericSignatureParams(
Expand Down Expand Up @@ -180,6 +202,18 @@ void ClangSyntaxPrinter::printNamespace(
printNamespace([&](raw_ostream &os) { os << name; }, bodyPrinter, trivia);
}

void ClangSyntaxPrinter::printParentNamespaceForNestedTypes(
const ValueDecl *D, llvm::function_ref<void(raw_ostream &OS)> bodyPrinter,
NamespaceTrivia trivia) const {
if (!isa_and_nonnull<NominalTypeDecl>(D->getDeclContext()->getAsDecl())) {
bodyPrinter(os);
return;
}
printNamespace(
[=](raw_ostream &os) { printNestedTypeNamespaceQualifiers(D); },
bodyPrinter, trivia);
}

void ClangSyntaxPrinter::printExternC(
llvm::function_ref<void(raw_ostream &OS)> bodyPrinter) const {
os << "#ifdef __cplusplus\n";
Expand Down Expand Up @@ -383,7 +417,8 @@ void ClangSyntaxPrinter::printPrimaryCxxTypeName(
const NominalTypeDecl *type, const ModuleDecl *moduleContext) {
printModuleNamespaceQualifiersIfNeeded(type->getModuleContext(),
moduleContext);
// FIXME: Print class qualifiers for nested class references.
if (!printNestedTypeNamespaceQualifiers(type))
os << "::";
printBaseName(type);
}

Expand Down
10 changes: 10 additions & 0 deletions lib/PrintAsClang/ClangSyntaxPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ class ClangSyntaxPrinter {
bool printNominalTypeOutsideMemberDeclInnerStaticAssert(
const NominalTypeDecl *typeDecl);

// Returns true when no qualifiers were printed.
bool printNestedTypeNamespaceQualifiers(const ValueDecl *D) const;

/// Print out the C++ class access qualifier for the given Swift type
/// declaration.
///
Expand Down Expand Up @@ -135,6 +138,13 @@ class ClangSyntaxPrinter {
llvm::function_ref<void(raw_ostream &OS)> bodyPrinter,
NamespaceTrivia trivia = NamespaceTrivia::None) const;

/// Prints the C++ namespaces of the outer types for a nested type.
/// E.g., for struct Foo { struct Bar {...} } it will print
/// namespace __FooNested { ..body.. } // namespace __FooNested
void printParentNamespaceForNestedTypes(
const ValueDecl *D, llvm::function_ref<void(raw_ostream &OS)> bodyPrinter,
NamespaceTrivia trivia = NamespaceTrivia::None) const;

/// Print an extern C block with given body.
void
printExternC(llvm::function_ref<void(raw_ostream &OS)> bodyPrinter) const;
Expand Down
33 changes: 28 additions & 5 deletions lib/PrintAsClang/DeclAndTypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "DeclAndTypePrinter.h"
#include "ClangSyntaxPrinter.h"
#include "OutputLanguageMode.h"
#include "PrimitiveTypeMapping.h"
#include "PrintClangClassType.h"
#include "PrintClangFunction.h"
Expand Down Expand Up @@ -46,6 +47,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/raw_ostream.h"

using namespace swift;
Expand Down Expand Up @@ -234,14 +236,33 @@ class DeclAndTypePrinter::Implementation
os << ">";
}

void printUsingForNestedType(const NominalTypeDecl *TD,
const ModuleDecl *moduleContext) {
if (TD->isImplicit() || TD->isSynthesized())
return;
os << " using ";
ClangSyntaxPrinter(os).printBaseName(TD);
os << "=";
ClangSyntaxPrinter(os).printNominalTypeReference(TD, moduleContext);
os << ";\n";
}

/// Prints the members of a class, extension, or protocol.
template <bool AllowDelayed = false, typename R>
void printMembers(R &&members) {
CxxEmissionScopeRAII cxxScopeRAII(owningPrinter);
// FIXME: Actually track emitted members in nested
// lexical scopes.
// FIXME: Emit unavailable C++ decls for not emitted
// nested members.
// Using statements for nested types.
if (outputLang == OutputLanguageMode::Cxx) {
for (const Decl *member : members) {
if (member->getModuleContext()->isStdlibModule())
break;
// TODO: support nested classes.
if (isa<ClassDecl>(member))
continue;
if (const auto *TD = dyn_cast<NominalTypeDecl>(member))
printUsingForNestedType(TD, TD->getModuleContext());
}
}
bool protocolMembersOptional = false;
for (const Decl *member : members) {
auto VD = dyn_cast<ValueDecl>(member);
Expand Down Expand Up @@ -2796,14 +2817,16 @@ static bool isAsyncAlternativeOfOtherDecl(const ValueDecl *VD) {
return false;
}

static bool isStringNestedType(const ValueDecl *VD, StringRef Typename) {
namespace swift {
bool isStringNestedType(const ValueDecl *VD, StringRef Typename) {
auto ctx = VD->getDeclContext();
return VD->hasName() && VD->getName().isSimpleName() &&
VD->getBaseIdentifier().str() == Typename &&
isa<ExtensionDecl>(ctx->getAsDecl()) &&
cast<ExtensionDecl>(ctx->getAsDecl())->getExtendedNominal() ==
VD->getASTContext().getStringDecl();
}
} // namespace swift

static bool hasExposeAttr(const ValueDecl *VD) {
if (isa<NominalTypeDecl>(VD) && VD->getModuleContext()->isStdlibModule()) {
Expand Down
2 changes: 2 additions & 0 deletions lib/PrintAsClang/DeclAndTypePrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ class DeclAndTypePrinter {
getObjectTypeAndOptionality(const ValueDecl *D, Type ty);
};

bool isStringNestedType(const ValueDecl *VD, StringRef Typename);

} // end namespace swift

#endif
39 changes: 27 additions & 12 deletions lib/PrintAsClang/ModuleContentsWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -218,7 +219,10 @@ class ModuleWriter {
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;
Expand All @@ -229,7 +233,7 @@ class ModuleWriter {
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;
Expand Down Expand Up @@ -364,23 +368,31 @@ class ModuleWriter {
}

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);
if (!VD || !printer.shouldInclude(VD))
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;
}
Expand Down Expand Up @@ -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";
Expand Down
Loading