Skip to content

Commit 709321b

Browse files
committed
Fix ASTMangler mangling NS_OPTION differently in C++ mode
CF_OPTIONS is defined differently in the SDK based on a __cplusplus preprocessor branch. As a result, declarations referencing CF_OPTIONS are mangled differently depending on if C++ interop is enabled. This meant a module compiled with cxx interop on could not be linked with a module compiled without and vice versa. This patch modifies the mangler such that the mangled names are consistent. This is achieved by feeding the mangler a modified AST node that looks like the Objective-C definition of CF_OPTIONS, even when we have cxx interop enabled.
1 parent 8d7c115 commit 709321b

File tree

9 files changed

+137
-12
lines changed

9 files changed

+137
-12
lines changed

include/swift/AST/ASTMangler.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,11 @@ class ASTMangler : public Mangler {
384384
static Optional<SpecialContext>
385385
getSpecialManglingContext(const ValueDecl *decl, bool useObjCProtocolNames);
386386

387-
static const clang::NamedDecl *
388-
getClangDeclForMangling(const ValueDecl *decl);
387+
static bool isCXXCFOptionsDefinition(const ValueDecl *decl);
388+
static const clang::TypedefType *
389+
getTypeDefForCXXCFOptionsDefinition(const ValueDecl *decl);
390+
391+
static const clang::NamedDecl *getClangDeclForMangling(const ValueDecl *decl);
389392

390393
void appendExistentialLayout(
391394
const ExistentialLayout &layout, GenericSignature sig,

include/swift/AST/ClangModuleLoader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,9 @@ class ClangModuleLoader : public ModuleLoader {
286286
/// Determine the effective Clang context for the given Swift nominal type.
287287
virtual EffectiveClangContext getEffectiveClangContext(
288288
const NominalTypeDecl *nominal) = 0;
289+
290+
virtual const clang::TypedefType *
291+
getTypeDefForCXXCFOptionsDefinition(const clang::Decl *candidateDecl) = 0;
289292
};
290293

291294
/// Describes a C++ template instantiation error.

include/swift/ClangImporter/ClangImporter.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,9 @@ class ClangImporter final : public ClangModuleLoader {
559559

560560
/// Enable the symbolic import experimental feature for the given callback.
561561
void withSymbolicFeatureEnabled(llvm::function_ref<void(void)> callback);
562+
563+
const clang::TypedefType *getTypeDefForCXXCFOptionsDefinition(
564+
const clang::Decl *candidateDecl) override;
562565
};
563566

564567
ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN,

lib/AST/ASTMangler.cpp

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -484,12 +484,19 @@ void ASTMangler::beginManglingWithAutoDiffOriginalFunction(
484484
appendOperator(attr->Name);
485485
return;
486486
}
487+
488+
auto beginManglingClangDecl = [&](const clang::NamedDecl *decl) {
489+
beginManglingWithoutPrefix();
490+
appendOperator(decl->getName());
491+
};
492+
487493
// For imported Clang declarations, use the Clang name in order to match how
488494
// DifferentiationMangler handles these.
489-
auto clangDecl = getClangDeclForMangling(afd);
490-
if (clangDecl) {
491-
beginManglingWithoutPrefix();
492-
appendOperator(clangDecl->getName());
495+
if (auto clangDecl = getClangDeclForMangling(afd)) {
496+
beginManglingClangDecl(clangDecl);
497+
return;
498+
} else if (auto typedefType = getTypeDefForCXXCFOptionsDefinition(afd)) {
499+
beginManglingClangDecl(typedefType->getDecl());
493500
return;
494501
}
495502
beginMangling();
@@ -2168,7 +2175,13 @@ ASTMangler::getSpecialManglingContext(const ValueDecl *decl,
21682175
if (auto *clangDecl = cast_or_null<clang::NamedDecl>(decl->getClangDecl())){
21692176
bool hasNameForLinkage;
21702177
if (auto *tagDecl = dyn_cast<clang::TagDecl>(clangDecl))
2171-
hasNameForLinkage = tagDecl->hasNameForLinkage();
2178+
// Clang does not always populate the fields that determine if a tag
2179+
// decl has a linkage name. This is particularly the case for the
2180+
// C++ definition of CF_OPTIONS in the sdk. However, we use the
2181+
// name of the backing typedef as a linkage name, despite
2182+
// the enum itself not having one.
2183+
hasNameForLinkage =
2184+
tagDecl->hasNameForLinkage() || isCXXCFOptionsDefinition(decl);
21722185
else
21732186
hasNameForLinkage = !clangDecl->getDeclName().isEmpty();
21742187
if (hasNameForLinkage) {
@@ -2508,11 +2521,26 @@ void ASTMangler::appendProtocolName(const ProtocolDecl *protocol,
25082521
appendDeclName(protocol);
25092522
}
25102523

2511-
const clang::NamedDecl *ASTMangler::getClangDeclForMangling(const ValueDecl *vd) {
2512-
auto namedDecl = dyn_cast_or_null<clang::NamedDecl>(vd->getClangDecl());
2524+
bool ASTMangler::isCXXCFOptionsDefinition(const ValueDecl *decl) {
2525+
return getTypeDefForCXXCFOptionsDefinition(decl);
2526+
}
2527+
2528+
const clang::TypedefType *
2529+
ASTMangler::getTypeDefForCXXCFOptionsDefinition(const ValueDecl *decl) {
2530+
const clang::Decl *clangDecl = decl->getClangDecl();
2531+
if (!clangDecl)
2532+
return nullptr;
2533+
2534+
const auto &clangModuleLoader = decl->getASTContext().getClangModuleLoader();
2535+
return clangModuleLoader->getTypeDefForCXXCFOptionsDefinition(clangDecl);
2536+
}
2537+
2538+
const clang::NamedDecl *
2539+
ASTMangler::getClangDeclForMangling(const ValueDecl *vd) {
2540+
auto namedDecl = dyn_cast_or_null<clang::NamedDecl>(vd->getClangDecl());
25132541
if (!namedDecl)
25142542
return nullptr;
2515-
2543+
25162544
// Use an anonymous enum's enclosing typedef for the mangled name, if
25172545
// present. This matches C++'s rules for linkage names of tag declarations.
25182546
if (namedDecl->getDeclName().isEmpty())
@@ -2574,8 +2602,20 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) {
25742602
auto tryAppendClangName = [this, decl]() -> bool {
25752603
auto *nominal = dyn_cast<NominalTypeDecl>(decl);
25762604
auto namedDecl = getClangDeclForMangling(decl);
2577-
if (!namedDecl)
2605+
if (!namedDecl) {
2606+
if (auto typedefType = getTypeDefForCXXCFOptionsDefinition(decl)) {
2607+
// To make sure the C++ definition of CF_OPTIONS mangles the
2608+
// same way as the Objective-C definition, we mangle using the
2609+
// name of the backing typedef, but pretend as if it was an enum.
2610+
// See CFAvailability.h to understand how the definitions differ
2611+
// in C++ and Objective-C
2612+
appendIdentifier(typedefType->getDecl()->getName());
2613+
appendOperator("V");
2614+
return true;
2615+
}
2616+
25782617
return false;
2618+
}
25792619

25802620
// Mangle ObjC classes using their runtime names.
25812621
auto interface = dyn_cast<clang::ObjCInterfaceDecl>(namedDecl);

lib/ClangImporter/ClangImporter.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6584,6 +6584,35 @@ void ClangImporter::withSymbolicFeatureEnabled(
65846584
oldImportSymbolicCXXDecls.get());
65856585
}
65866586

6587+
const clang::TypedefType *ClangImporter::getTypeDefForCXXCFOptionsDefinition(
6588+
const clang::Decl *candidateDecl) {
6589+
6590+
if (!Impl.SwiftContext.LangOpts.EnableCXXInterop)
6591+
return nullptr;
6592+
6593+
auto enumDecl = dyn_cast<clang::EnumDecl>(candidateDecl);
6594+
if (!enumDecl)
6595+
return nullptr;
6596+
6597+
if (!enumDecl->getDeclName().isEmpty())
6598+
return nullptr;
6599+
6600+
if (auto typedefType = dyn_cast<clang::TypedefType>(
6601+
enumDecl->getIntegerType().getTypePtr())) {
6602+
if (auto enumExtensibilityAttr =
6603+
typedefType->getDecl()->getAttr<clang::EnumExtensibilityAttr>();
6604+
enumExtensibilityAttr &&
6605+
enumExtensibilityAttr->getExtensibility() ==
6606+
clang::EnumExtensibilityAttr::Open &&
6607+
typedefType->getDecl()->hasAttr<clang::FlagEnumAttr>()) {
6608+
return Impl.isUnavailableInSwift(typedefType->getDecl()) ? typedefType
6609+
: nullptr;
6610+
}
6611+
}
6612+
6613+
return nullptr;
6614+
}
6615+
65876616
bool importer::requiresCPlusPlus(const clang::Module *module) {
65886617
// The libc++ modulemap doesn't currently declare the requirement.
65896618
if (module->getTopLevelModuleName() == "std")

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1244,7 +1244,7 @@ namespace {
12441244
// Otherwise, if this was imported from a Clang declaration, use that
12451245
// declaration's name as the ABI name.
12461246
} else if (auto clangDecl =
1247-
Mangle::ASTMangler::getClangDeclForMangling(Type)) {
1247+
Mangle::ASTMangler::getClangDeclForMangling(Type)) {
12481248
// Class template specializations need to use their mangled name so
12491249
// that each specialization gets its own metadata. A class template
12501250
// specialization's Swift name will always be the mangled name, so just
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#if __has_attribute(enum_extensibility)
2+
#define __CF_ENUM_ATTRIBUTES __attribute__((enum_extensibility(open)))
3+
#define __CF_CLOSED_ENUM_ATTRIBUTES __attribute__((enum_extensibility(closed)))
4+
#define __CF_OPTIONS_ATTRIBUTES \
5+
__attribute__((flag_enum, enum_extensibility(open)))
6+
#else
7+
#define __CF_ENUM_ATTRIBUTES
8+
#define __CF_CLOSED_ENUM_ATTRIBUTES
9+
#define __CF_OPTIONS_ATTRIBUTES
10+
#endif
11+
12+
#if (__cplusplus)
13+
#define CF_OPTIONS(_type, _name) \
14+
_type __attribute__((availability(swift, unavailable))) _name; \
15+
enum __CF_OPTIONS_ATTRIBUTES : _name
16+
#else
17+
#define CF_OPTIONS(_type, _name) \
18+
enum __CF_OPTIONS_ATTRIBUTES _name : _type _name; \
19+
enum _name : _type
20+
#endif
21+
22+
#define NS_OPTIONS(_type, _name) CF_OPTIONS(_type, _name)
23+
24+
typedef NS_OPTIONS(int, StandardNSOption) {
25+
StandardNSOption1,
26+
StandardNSOption2
27+
};

test/Interop/Cxx/enum/Inputs/module.modulemap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,7 @@ module CenumsNSOptionsExternC [extern_c] {
3232
header "c-enums-NS_OPTIONS_without_extern_C.h"
3333
requires cplusplus
3434
}
35+
36+
module CFAvailability {
37+
header "CFAvailability.h"
38+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %empty-directory(%t/cache)
2+
// RUN: %target-swift-frontend %s -I %S/Inputs -c -enable-experimental-cxx-interop -o %t/object.o
3+
// RUN: %llvm-nm %t/object.o > %t/results.txt
4+
// RUN: %target-swift-frontend %s -I %S/Inputs -c -enable-objc-interop -o %t/object.o
5+
// RUN: %llvm-nm %t/object.o >> %t/results.txt
6+
// RUN: cat %t/results.txt | %FileCheck %s
7+
8+
// REQUIRES: objc_interop
9+
10+
import CFAvailability
11+
12+
// Verify that this functions linkage name is the name with or without cxx interop enabled
13+
public func useNSOption(foo param: StandardNSOption) {}
14+
15+
// CHECK: [[FUNC_LINKAGE_NAME:\$s.*useNSOption.*$]]
16+
// CHECK: [[FUNC_LINKAGE_NAME]]

0 commit comments

Comments
 (0)