diff --git a/include/swift/AST/ASTMangler.h b/include/swift/AST/ASTMangler.h index 9bbd6b68ea687..de07d203299d3 100644 --- a/include/swift/AST/ASTMangler.h +++ b/include/swift/AST/ASTMangler.h @@ -384,8 +384,11 @@ class ASTMangler : public Mangler { static Optional getSpecialManglingContext(const ValueDecl *decl, bool useObjCProtocolNames); - static const clang::NamedDecl * - getClangDeclForMangling(const ValueDecl *decl); + static bool isCXXCFOptionsDefinition(const ValueDecl *decl); + static const clang::TypedefType * + getTypeDefForCXXCFOptionsDefinition(const ValueDecl *decl); + + static const clang::NamedDecl *getClangDeclForMangling(const ValueDecl *decl); void appendExistentialLayout( const ExistentialLayout &layout, GenericSignature sig, diff --git a/include/swift/AST/ClangModuleLoader.h b/include/swift/AST/ClangModuleLoader.h index 2ba1472d09b4c..c9113d97336c8 100644 --- a/include/swift/AST/ClangModuleLoader.h +++ b/include/swift/AST/ClangModuleLoader.h @@ -286,6 +286,9 @@ class ClangModuleLoader : public ModuleLoader { /// Determine the effective Clang context for the given Swift nominal type. virtual EffectiveClangContext getEffectiveClangContext( const NominalTypeDecl *nominal) = 0; + + virtual const clang::TypedefType * + getTypeDefForCXXCFOptionsDefinition(const clang::Decl *candidateDecl) = 0; }; /// Describes a C++ template instantiation error. diff --git a/include/swift/ClangImporter/ClangImporter.h b/include/swift/ClangImporter/ClangImporter.h index 441b7c82af468..6f962fc26a201 100644 --- a/include/swift/ClangImporter/ClangImporter.h +++ b/include/swift/ClangImporter/ClangImporter.h @@ -559,6 +559,9 @@ class ClangImporter final : public ClangModuleLoader { /// Enable the symbolic import experimental feature for the given callback. void withSymbolicFeatureEnabled(llvm::function_ref callback); + + const clang::TypedefType *getTypeDefForCXXCFOptionsDefinition( + const clang::Decl *candidateDecl) override; }; ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN, diff --git a/lib/AST/ASTMangler.cpp b/lib/AST/ASTMangler.cpp index 3ce20006c45fb..4fb11056b2273 100644 --- a/lib/AST/ASTMangler.cpp +++ b/lib/AST/ASTMangler.cpp @@ -484,12 +484,19 @@ void ASTMangler::beginManglingWithAutoDiffOriginalFunction( appendOperator(attr->Name); return; } + + auto beginManglingClangDecl = [&](const clang::NamedDecl *decl) { + beginManglingWithoutPrefix(); + appendOperator(decl->getName()); + }; + // For imported Clang declarations, use the Clang name in order to match how // DifferentiationMangler handles these. - auto clangDecl = getClangDeclForMangling(afd); - if (clangDecl) { - beginManglingWithoutPrefix(); - appendOperator(clangDecl->getName()); + if (auto clangDecl = getClangDeclForMangling(afd)) { + beginManglingClangDecl(clangDecl); + return; + } else if (auto typedefType = getTypeDefForCXXCFOptionsDefinition(afd)) { + beginManglingClangDecl(typedefType->getDecl()); return; } beginMangling(); @@ -2168,7 +2175,13 @@ ASTMangler::getSpecialManglingContext(const ValueDecl *decl, if (auto *clangDecl = cast_or_null(decl->getClangDecl())){ bool hasNameForLinkage; if (auto *tagDecl = dyn_cast(clangDecl)) - hasNameForLinkage = tagDecl->hasNameForLinkage(); + // Clang does not always populate the fields that determine if a tag + // decl has a linkage name. This is particularly the case for the + // C++ definition of CF_OPTIONS in the sdk. However, we use the + // name of the backing typedef as a linkage name, despite + // the enum itself not having one. + hasNameForLinkage = + tagDecl->hasNameForLinkage() || isCXXCFOptionsDefinition(decl); else hasNameForLinkage = !clangDecl->getDeclName().isEmpty(); if (hasNameForLinkage) { @@ -2508,11 +2521,26 @@ void ASTMangler::appendProtocolName(const ProtocolDecl *protocol, appendDeclName(protocol); } -const clang::NamedDecl *ASTMangler::getClangDeclForMangling(const ValueDecl *vd) { - auto namedDecl = dyn_cast_or_null(vd->getClangDecl()); +bool ASTMangler::isCXXCFOptionsDefinition(const ValueDecl *decl) { + return getTypeDefForCXXCFOptionsDefinition(decl); +} + +const clang::TypedefType * +ASTMangler::getTypeDefForCXXCFOptionsDefinition(const ValueDecl *decl) { + const clang::Decl *clangDecl = decl->getClangDecl(); + if (!clangDecl) + return nullptr; + + const auto &clangModuleLoader = decl->getASTContext().getClangModuleLoader(); + return clangModuleLoader->getTypeDefForCXXCFOptionsDefinition(clangDecl); +} + +const clang::NamedDecl * +ASTMangler::getClangDeclForMangling(const ValueDecl *vd) { + auto namedDecl = dyn_cast_or_null(vd->getClangDecl()); if (!namedDecl) return nullptr; - + // Use an anonymous enum's enclosing typedef for the mangled name, if // present. This matches C++'s rules for linkage names of tag declarations. if (namedDecl->getDeclName().isEmpty()) @@ -2574,8 +2602,20 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) { auto tryAppendClangName = [this, decl]() -> bool { auto *nominal = dyn_cast(decl); auto namedDecl = getClangDeclForMangling(decl); - if (!namedDecl) + if (!namedDecl) { + if (auto typedefType = getTypeDefForCXXCFOptionsDefinition(decl)) { + // To make sure the C++ definition of CF_OPTIONS mangles the + // same way as the Objective-C definition, we mangle using the + // name of the backing typedef, but pretend as if it was an enum. + // See CFAvailability.h to understand how the definitions differ + // in C++ and Objective-C + appendIdentifier(typedefType->getDecl()->getName()); + appendOperator("V"); + return true; + } + return false; + } // Mangle ObjC classes using their runtime names. auto interface = dyn_cast(namedDecl); diff --git a/lib/ClangImporter/ClangImporter.cpp b/lib/ClangImporter/ClangImporter.cpp index 4cff0fe032401..4dab5138ed1dd 100644 --- a/lib/ClangImporter/ClangImporter.cpp +++ b/lib/ClangImporter/ClangImporter.cpp @@ -6584,6 +6584,35 @@ void ClangImporter::withSymbolicFeatureEnabled( oldImportSymbolicCXXDecls.get()); } +const clang::TypedefType *ClangImporter::getTypeDefForCXXCFOptionsDefinition( + const clang::Decl *candidateDecl) { + + if (!Impl.SwiftContext.LangOpts.EnableCXXInterop) + return nullptr; + + auto enumDecl = dyn_cast(candidateDecl); + if (!enumDecl) + return nullptr; + + if (!enumDecl->getDeclName().isEmpty()) + return nullptr; + + if (auto typedefType = dyn_cast( + enumDecl->getIntegerType().getTypePtr())) { + if (auto enumExtensibilityAttr = + typedefType->getDecl()->getAttr(); + enumExtensibilityAttr && + enumExtensibilityAttr->getExtensibility() == + clang::EnumExtensibilityAttr::Open && + typedefType->getDecl()->hasAttr()) { + return Impl.isUnavailableInSwift(typedefType->getDecl()) ? typedefType + : nullptr; + } + } + + return nullptr; +} + bool importer::requiresCPlusPlus(const clang::Module *module) { // The libc++ modulemap doesn't currently declare the requirement. if (module->getTopLevelModuleName() == "std") diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index b22619ddceb6a..d63dcc0335ec1 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -1244,7 +1244,7 @@ namespace { // Otherwise, if this was imported from a Clang declaration, use that // declaration's name as the ABI name. } else if (auto clangDecl = - Mangle::ASTMangler::getClangDeclForMangling(Type)) { + Mangle::ASTMangler::getClangDeclForMangling(Type)) { // Class template specializations need to use their mangled name so // that each specialization gets its own metadata. A class template // specialization's Swift name will always be the mangled name, so just diff --git a/test/Interop/Cxx/enum/Inputs/CFAvailability.h b/test/Interop/Cxx/enum/Inputs/CFAvailability.h new file mode 100644 index 0000000000000..28d5bb60d5789 --- /dev/null +++ b/test/Interop/Cxx/enum/Inputs/CFAvailability.h @@ -0,0 +1,27 @@ +#if __has_attribute(enum_extensibility) +#define __CF_ENUM_ATTRIBUTES __attribute__((enum_extensibility(open))) +#define __CF_CLOSED_ENUM_ATTRIBUTES __attribute__((enum_extensibility(closed))) +#define __CF_OPTIONS_ATTRIBUTES \ + __attribute__((flag_enum, enum_extensibility(open))) +#else +#define __CF_ENUM_ATTRIBUTES +#define __CF_CLOSED_ENUM_ATTRIBUTES +#define __CF_OPTIONS_ATTRIBUTES +#endif + +#if (__cplusplus) +#define CF_OPTIONS(_type, _name) \ + _type __attribute__((availability(swift, unavailable))) _name; \ + enum __CF_OPTIONS_ATTRIBUTES : _name +#else +#define CF_OPTIONS(_type, _name) \ + enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; \ + enum _name : _type +#endif + +#define NS_OPTIONS(_type, _name) CF_OPTIONS(_type, _name) + +typedef NS_OPTIONS(int, StandardNSOption) { + StandardNSOption1, + StandardNSOption2 +}; diff --git a/test/Interop/Cxx/enum/Inputs/module.modulemap b/test/Interop/Cxx/enum/Inputs/module.modulemap index d5876a40485a9..38443317e21bc 100644 --- a/test/Interop/Cxx/enum/Inputs/module.modulemap +++ b/test/Interop/Cxx/enum/Inputs/module.modulemap @@ -32,3 +32,7 @@ module CenumsNSOptionsExternC [extern_c] { header "c-enums-NS_OPTIONS_without_extern_C.h" requires cplusplus } + +module CFAvailability { + header "CFAvailability.h" +} diff --git a/test/Interop/Cxx/enum/c-enums-NS_OPTIONS-consistent-linkage-name.swift b/test/Interop/Cxx/enum/c-enums-NS_OPTIONS-consistent-linkage-name.swift new file mode 100644 index 0000000000000..0d3328b3efa75 --- /dev/null +++ b/test/Interop/Cxx/enum/c-enums-NS_OPTIONS-consistent-linkage-name.swift @@ -0,0 +1,16 @@ +// RUN: %empty-directory(%t/cache) +// RUN: %target-swift-frontend %s -I %S/Inputs -c -enable-experimental-cxx-interop -o %t/object.o +// RUN: %llvm-nm %t/object.o > %t/results.txt +// RUN: %target-swift-frontend %s -I %S/Inputs -c -enable-objc-interop -o %t/object.o +// RUN: %llvm-nm %t/object.o >> %t/results.txt +// RUN: cat %t/results.txt | %FileCheck %s + +// REQUIRES: objc_interop + +import CFAvailability + +// Verify that this functions linkage name is the name with or without cxx interop enabled +public func useNSOption(foo param: StandardNSOption) {} + +// CHECK: [[FUNC_LINKAGE_NAME:\$s.*useNSOption.*$]] +// CHECK: [[FUNC_LINKAGE_NAME]]