diff --git a/lib/AST/SwiftNameTranslation.cpp b/lib/AST/SwiftNameTranslation.cpp index ed9975acb0339..9a9605078a866 100644 --- a/lib/AST/SwiftNameTranslation.cpp +++ b/lib/AST/SwiftNameTranslation.cpp @@ -331,7 +331,9 @@ swift::cxx_translation::getDeclRepresentation( return {Unsupported, UnrepresentableGeneric}; genericSignature = typeDecl->getGenericSignature(); } - if (!isa(typeDecl) && isZeroSized && (*isZeroSized)(typeDecl)) + if (!isa(typeDecl) && isZeroSized && (*isZeroSized)(typeDecl) && + // Empty enums are exported as namespaces. + !isa(typeDecl)) return {Unsupported, UnrepresentableZeroSizedValueType}; } if (const auto *varDecl = dyn_cast(VD)) { diff --git a/lib/PrintAsClang/DeclAndTypePrinter.cpp b/lib/PrintAsClang/DeclAndTypePrinter.cpp index c61e2be56328b..d31a918befddf 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.cpp +++ b/lib/PrintAsClang/DeclAndTypePrinter.cpp @@ -243,6 +243,9 @@ class DeclAndTypePrinter::Implementation const ModuleDecl *moduleContext) { if (TD->isImplicit() || TD->isSynthesized()) return; + // Empty enums are exported as namespaces. + if (owningPrinter.isEmptyEnum(TD)) + return; os << " using "; ClangSyntaxPrinter(getASTContext(), os).printBaseName(TD); os << "="; @@ -490,10 +493,31 @@ class DeclAndTypePrinter::Implementation void visitEnumDeclCxx(EnumDecl *ED) { assert(owningPrinter.outputLang == OutputLanguageMode::Cxx); + ClangSyntaxPrinter syntaxPrinter(ED->getASTContext(), os); + + if (owningPrinter.isEmptyEnum(ED)) { + syntaxPrinter.printParentNamespaceForNestedTypes( + ED, [&](raw_ostream &os) { + syntaxPrinter.printNamespace( + cxx_translation::getNameForCxx(ED), + [ED, this](llvm::raw_ostream &) { + printMembers(ED->getAllMembers()); + for (const auto *ext : + owningPrinter.interopContext.getExtensionsForNominalType( + ED)) { + if (!cxx_translation::isExposableToCxx( + ext->getGenericSignature())) + continue; + + printMembers(ext->getAllMembers()); + } + }); + }); + return; + } ClangValueTypePrinter valueTypePrinter(os, owningPrinter.prologueOS, owningPrinter.interopContext); - ClangSyntaxPrinter syntaxPrinter(ED->getASTContext(), os); DeclAndTypeClangFunctionPrinter clangFuncPrinter( os, owningPrinter.prologueOS, owningPrinter.typeMapping, owningPrinter.interopContext, owningPrinter); @@ -1890,7 +1914,9 @@ class DeclAndTypePrinter::Implementation void visitFuncDecl(FuncDecl *FD) { if (outputLang == OutputLanguageMode::Cxx) { if (FD->getDeclContext()->isTypeContext()) - return printAbstractFunctionAsMethod(FD, FD->isStatic()); + return printAbstractFunctionAsMethod( + FD, FD->isStatic() && !owningPrinter.isEmptyEnum( + FD->getDeclContext()->getAsDecl())); // Emit the underlying C signature that matches the Swift ABI // in the generated C++ implementation prologue for the module. @@ -3080,6 +3106,10 @@ bool DeclAndTypePrinter::isZeroSized(const NominalTypeDecl *decl) { return false; } +bool DeclAndTypePrinter::isEmptyEnum(const Decl *decl) { + return isa(decl) && isZeroSized(cast(decl)); +} + bool DeclAndTypePrinter::isVisible(const ValueDecl *vd) const { return outputLang == OutputLanguageMode::Cxx ? cxx_translation::isVisibleToCxx(vd, minRequiredAccess) diff --git a/lib/PrintAsClang/DeclAndTypePrinter.h b/lib/PrintAsClang/DeclAndTypePrinter.h index 55c7a57344a41..b1130ef36b9fd 100644 --- a/lib/PrintAsClang/DeclAndTypePrinter.h +++ b/lib/PrintAsClang/DeclAndTypePrinter.h @@ -119,6 +119,7 @@ class DeclAndTypePrinter { bool shouldInclude(const ValueDecl *VD); bool isZeroSized(const NominalTypeDecl *decl); + bool isEmptyEnum(const Decl *decl); /// Returns true if \p vd is visible given the current access level and thus /// can be included in the generated header. diff --git a/lib/PrintAsClang/ModuleContentsWriter.cpp b/lib/PrintAsClang/ModuleContentsWriter.cpp index 028dc7c028fdc..a2f704aabd3fd 100644 --- a/lib/PrintAsClang/ModuleContentsWriter.cpp +++ b/lib/PrintAsClang/ModuleContentsWriter.cpp @@ -582,6 +582,10 @@ class ModuleWriter { } void forwardDeclareCxxValueTypeIfNeeded(const NominalTypeDecl *NTD) { + // Zero sized enums are exported as namespaces, no forward declaration is + // required. + if (printer.isZeroSized(NTD) && isa(NTD)) + return; forwardDeclare(NTD, [&]() { ClangValueTypePrinter::forwardDeclType(os, NTD, printer); }); diff --git a/lib/PrintAsClang/PrintClangFunction.cpp b/lib/PrintAsClang/PrintClangFunction.cpp index 1bd546494efbf..820798fe0758c 100644 --- a/lib/PrintAsClang/PrintClangFunction.cpp +++ b/lib/PrintAsClang/PrintClangFunction.cpp @@ -1625,7 +1625,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxMethod( bool isMutating = isa(FD) ? cast(FD)->isMutating() : false; modifiers.isConst = !isa(typeDeclContext) && !isMutating && - !isConstructor && !isStatic; + !isConstructor && !isStatic && + !declAndTypePrinter.isEmptyEnum(typeDeclContext); modifiers.hasSymbolUSR = !isDefinition; auto result = printFunctionSignature( FD, signature, cxx_translation::getNameForCxx(FD), resultTy, diff --git a/test/Interop/SwiftToCxx/structs/nested-structs-in-cxx-execution.cpp b/test/Interop/SwiftToCxx/structs/nested-structs-in-cxx-execution.cpp index ab7b07ad0472d..752e30bdff0a6 100644 --- a/test/Interop/SwiftToCxx/structs/nested-structs-in-cxx-execution.cpp +++ b/test/Interop/SwiftToCxx/structs/nested-structs-in-cxx-execution.cpp @@ -21,4 +21,7 @@ int main() { auto xx = makeAudioFileType(); AudioFileType::SubType yy = xx.getCAF(); + auto xxx = Empty::getInt(); + Empty::NestedInEmpty *ptr = nullptr; + auto xxxxx = AuxConfig::Empty::getInt(); } diff --git a/test/Interop/SwiftToCxx/structs/nested-structs-in-cxx.swift b/test/Interop/SwiftToCxx/structs/nested-structs-in-cxx.swift index 0b6c24cc577d1..cdbee26628c90 100644 --- a/test/Interop/SwiftToCxx/structs/nested-structs-in-cxx.swift +++ b/test/Interop/SwiftToCxx/structs/nested-structs-in-cxx.swift @@ -53,6 +53,10 @@ public class AuxConfig { } public var directory = AuxDirectory() + + public enum Empty { + public static func getInt() -> Int { 55 } + } } public func makeRecordConfig() -> RecordConfig { @@ -85,6 +89,13 @@ extension AudioFileType { } } +public enum Empty { + public static func getInt() -> Int { 42 } + + public struct NestedInEmpty { + public var x : Int + } +} public func getFiles() -> [RecordConfig.File] { [] diff --git a/test/Interop/SwiftToCxx/structs/zero-sized-struct-in-cxx.swift b/test/Interop/SwiftToCxx/structs/zero-sized-struct-in-cxx.swift index c301f11a559c3..2df99e0dafbd2 100644 --- a/test/Interop/SwiftToCxx/structs/zero-sized-struct-in-cxx.swift +++ b/test/Interop/SwiftToCxx/structs/zero-sized-struct-in-cxx.swift @@ -35,11 +35,11 @@ public func f() -> ZeroSizedStruct { public func g(x: ZeroSizedStruct) { } -// CHECK: class ZeroSizedEnum { } SWIFT_UNAVAILABLE_MSG("'ZeroSizedEnum' is a zero sized value type, it cannot be exposed to C++ yet"); +// CHECK: namespace ZeroSizedEnum -// CHECK: class ZeroSizedEnum2 { } SWIFT_UNAVAILABLE_MSG("'ZeroSizedEnum2' is a zero sized value type, it cannot be exposed to C++ yet"); +// CHECK: namespace ZeroSizedEnum2 -// CHECK: class ZeroSizedEnum3 { } SWIFT_UNAVAILABLE_MSG("'ZeroSizedEnum3' is a zero sized value type, it cannot be exposed to C++ yet"); +// CHECK: class ZeroSizedEnum3 { } SWIFT_UNAVAILABLE_MSG("enum 'ZeroSizedEnum3' can not yet be represented in C++ as one of its cases has multiple associated values"); // CHECK: class ZeroSizedStruct { } SWIFT_UNAVAILABLE_MSG("'ZeroSizedStruct' is a zero sized value type, it cannot be exposed to C++ yet");