[cxx-interop] Support nested classes in reverse interop

Turns out we already had most of the building blocks given we already
support nested structs.

rdar://143343490
This commit is contained in:
Gabor Horvath
2025-01-30 12:52:23 +00:00
parent aff0f455cf
commit 8603dfe53a
7 changed files with 68 additions and 14 deletions

View File

@@ -84,7 +84,6 @@ enum RepresentationError {
UnrepresentableEnumCaseTuple,
UnrepresentableProtocol,
UnrepresentableMoveOnly,
UnrepresentableNested,
UnrepresentableMacro,
UnrepresentableZeroSizedValueType,
};

View File

@@ -249,11 +249,6 @@ swift::cxx_translation::getDeclRepresentation(
return {Unsupported, UnrepresentableGeneric};
genericSignature = typeDecl->getGenericSignature();
}
// Nested classes are not yet supported.
if (isa<ClassDecl>(VD) && !typeDecl->hasClangNode() &&
isa_and_nonnull<NominalTypeDecl>(
typeDecl->getDeclContext()->getAsDecl()))
return {Unsupported, UnrepresentableNested};
if (!isa<ClassDecl>(typeDecl) && isZeroSized && (*isZeroSized)(typeDecl))
return {Unsupported, UnrepresentableZeroSizedValueType};
}
@@ -392,8 +387,6 @@ swift::cxx_translation::diagnoseRepresenationError(RepresentationError error,
return Diagnostic(diag::expose_protocol_to_cxx_unsupported, vd);
case UnrepresentableMoveOnly:
return Diagnostic(diag::expose_move_only_to_cxx, vd);
case UnrepresentableNested:
return Diagnostic(diag::expose_nested_type_to_cxx, vd);
case UnrepresentableMacro:
return Diagnostic(diag::expose_macro_to_cxx, vd);
case UnrepresentableZeroSizedValueType:

View File

@@ -260,9 +260,6 @@ private:
auto VD = dyn_cast<ValueDecl>(member);
if (!VD || !shouldInclude(VD))
continue;
// TODO: support nested classes.
if (isa<ClassDecl>(member))
continue;
if (const auto *TD = dyn_cast<NominalTypeDecl>(member))
printUsingForNestedType(TD, TD->getModuleContext());
}

View File

@@ -116,9 +116,12 @@ void ClangClassTypePrinter::printClassTypeDecl(
void ClangClassTypePrinter::printClassTypeReturnScaffold(
raw_ostream &os, const ClassDecl *type, const ModuleDecl *moduleContext,
llvm::function_ref<void(void)> bodyPrinter) {
ClangSyntaxPrinter printer(type->getASTContext(), os);
os << " return ";
ClangSyntaxPrinter(type->getASTContext(), os).printModuleNamespaceQualifiersIfNeeded(
type->getModuleContext(), moduleContext);
printer.printModuleNamespaceQualifiersIfNeeded(type->getModuleContext(),
moduleContext);
if (!printer.printNestedTypeNamespaceQualifiers(type))
os << "::";
os << cxx_synthesis::getCxxImplNamespaceName() << "::";
ClangValueTypePrinter::printCxxImplClassName(os, type);
os << "::makeRetained(";

View File

@@ -348,7 +348,8 @@ public:
if (typeUseKind == FunctionSignatureTypeUse::ParamType && !isInOutParam)
os << "const ";
printOptional(optionalKind, [&]() {
ClangSyntaxPrinter(CT->getASTContext(), os).printBaseName(CT->getDecl());
ClangSyntaxPrinter(CT->getASTContext(), os)
.printPrimaryCxxTypeName(cd, moduleContext);
});
if (typeUseKind == FunctionSignatureTypeUse::ParamType)
os << "&";

View File

@@ -0,0 +1,26 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %S/nested-classes-in-cxx.swift -enable-library-evolution -typecheck -module-name Classes -clang-header-expose-decls=all-public -emit-clang-header-path %t/classes.h
// RUN: %target-interop-build-clangxx -std=c++17 -c %s -I %t -o %t/swift-classes-execution.o
// RUN: %target-interop-build-swift %S/nested-classes-in-cxx.swift -enable-library-evolution -o %t/swift-classes-execution -Xlinker %t/swift-classes-execution.o -module-name Classes -Xfrontend -entry-point-function-name -Xfrontend swiftMain
// RUN: %target-codesign %t/swift-classes-execution
// RUN: %target-run %t/swift-classes-execution
// REQUIRES: executable_test
#include "classes.h"
#include <cassert>
int main() {
using namespace Classes;
auto x = makeRecordConfig();
RecordConfig::File::Gate y = x.getGate();
assert(y.getProp() == 80);
assert(y.computeValue() == 160);
RecordConfig::AudioFormat z = x.getFile().getFormat();
assert(z == RecordConfig::AudioFormat::ALAC);
RecordConfig::File::Gate g = RecordConfig::File::Gate::init();
}

View File

@@ -0,0 +1,35 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -enable-library-evolution -typecheck -module-name Classes -clang-header-expose-decls=all-public -emit-clang-header-path %t/classes.h
// RUN: %check-interop-cxx-header-in-clang(%t/classes.h -DSWIFT_CXX_INTEROP_HIDE_STL_OVERLAY -std=c++17)
public class RecordConfig {
public enum AudioFormat {
case PCM, ALAC, AAC
}
public struct Directory {
public var path: String?
}
public class File {
public var format: AudioFormat = .ALAC
public class Gate {
public init() {}
public var prop: Int32 = 80
public func computeValue() -> Int32 {
return prop * 2
}
}
}
public var directory = Directory()
public var file = File()
public var gate = File.Gate()
}
public func makeRecordConfig() -> RecordConfig {
return RecordConfig()
}