Skip to content

Commit b5eb7b7

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 77feef5 commit b5eb7b7

File tree

6 files changed

+131
-15
lines changed

6 files changed

+131
-15
lines changed

include/swift/AST/ASTMangler.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,22 @@ class ASTMangler : public Mangler {
384384
static Optional<SpecialContext>
385385
getSpecialManglingContext(const ValueDecl *decl, bool useObjCProtocolNames);
386386

387-
static const clang::NamedDecl *
387+
struct ClangDeclForManglingContainer {
388+
const clang::NamedDecl *decl;
389+
std::function<void(const clang::NamedDecl *)> cleanup;
390+
391+
operator bool() const { return decl; }
392+
393+
ClangDeclForManglingContainer(
394+
const clang::NamedDecl *decl,
395+
std::function<void(const clang::NamedDecl *)> cleanup)
396+
: decl{decl}, cleanup{cleanup} {}
397+
ClangDeclForManglingContainer(const clang::NamedDecl *decl)
398+
: decl{decl}, cleanup{[](const clang::NamedDecl *) {}} {}
399+
~ClangDeclForManglingContainer() { cleanup(decl); }
400+
};
401+
402+
static const ClangDeclForManglingContainer
388403
getClangDeclForMangling(const ValueDecl *decl);
389404

390405
void appendExistentialLayout(

lib/AST/ASTMangler.cpp

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -486,10 +486,10 @@ void ASTMangler::beginManglingWithAutoDiffOriginalFunction(
486486
}
487487
// For imported Clang declarations, use the Clang name in order to match how
488488
// DifferentiationMangler handles these.
489-
auto clangDecl = getClangDeclForMangling(afd);
490-
if (clangDecl) {
489+
auto clangDeclContainer = getClangDeclForMangling(afd);
490+
if (clangDeclContainer) {
491491
beginManglingWithoutPrefix();
492-
appendOperator(clangDecl->getName());
492+
appendOperator(clangDeclContainer.decl->getName());
493493
return;
494494
}
495495
beginMangling();
@@ -2167,9 +2167,29 @@ ASTMangler::getSpecialManglingContext(const ValueDecl *decl,
21672167
if (isa<TypeDecl>(decl)) {
21682168
if (auto *clangDecl = cast_or_null<clang::NamedDecl>(decl->getClangDecl())){
21692169
bool hasNameForLinkage;
2170-
if (auto *tagDecl = dyn_cast<clang::TagDecl>(clangDecl))
2170+
if (auto *tagDecl = dyn_cast<clang::TagDecl>(clangDecl)) {
21712171
hasNameForLinkage = tagDecl->hasNameForLinkage();
2172-
else
2172+
// Clang does not always populate the fields that determine if a tag
2173+
// decl has a linkage name in the ways we expect. To support differing
2174+
// definitions of CF_OPTIONS, we check if the tag decls is anonymous and
2175+
// backed by an unavailable typedef.
2176+
if (!hasNameForLinkage) {
2177+
if (auto enumDecl = dyn_cast<clang::EnumDecl>(tagDecl);
2178+
decl->getASTContext().LangOpts.EnableCXXInterop &&
2179+
enumDecl->getDeclName().isEmpty()) {
2180+
if (auto typedefType = dyn_cast<clang::TypedefType>(
2181+
enumDecl->getIntegerType().getTypePtr())) {
2182+
for (auto *attr :
2183+
typedefType->getDecl()
2184+
->specific_attrs<clang::AvailabilityAttr>()) {
2185+
if (attr->getPlatform()->getName() == "swift") {
2186+
hasNameForLinkage = true;
2187+
}
2188+
}
2189+
}
2190+
}
2191+
}
2192+
} else
21732193
hasNameForLinkage = !clangDecl->getDeclName().isEmpty();
21742194
if (hasNameForLinkage) {
21752195
auto *clangDC = clangDecl->getDeclContext();
@@ -2506,17 +2526,47 @@ void ASTMangler::appendProtocolName(const ProtocolDecl *protocol,
25062526
appendDeclName(protocol);
25072527
}
25082528

2509-
const clang::NamedDecl *ASTMangler::getClangDeclForMangling(const ValueDecl *vd) {
2510-
auto namedDecl = dyn_cast_or_null<clang::NamedDecl>(vd->getClangDecl());
2529+
const ASTMangler::ClangDeclForManglingContainer
2530+
ASTMangler::getClangDeclForMangling(const ValueDecl *vd) {
2531+
auto namedDecl = dyn_cast_or_null<clang::NamedDecl>(vd->getClangDecl());
25112532
if (!namedDecl)
25122533
return nullptr;
2513-
2534+
25142535
// Use an anonymous enum's enclosing typedef for the mangled name, if
25152536
// present. This matches C++'s rules for linkage names of tag declarations.
25162537
if (namedDecl->getDeclName().isEmpty())
2517-
if (auto *tagDecl = dyn_cast<clang::TagDecl>(namedDecl))
2518-
if (auto *typedefDecl = tagDecl->getTypedefNameForAnonDecl())
2538+
if (auto *tagDecl = dyn_cast<clang::TagDecl>(namedDecl)) {
2539+
if (auto enumDecl = dyn_cast<clang::EnumDecl>(tagDecl)) {
2540+
// CF_OPTION derived types are different in Objective-C vs Objective-C++
2541+
// due different definitions in the SDK based on if __cplusplus is
2542+
// defined. The linkage name of these CF_OPTION derived types must be
2543+
// consistent regardless of if cxx interop is turned on or not,
2544+
// otherwise modules compiled with cxx interop cannot interoperate with
2545+
// those compiled without it.
2546+
if (auto typedefType = dyn_cast<clang::TypedefType>(
2547+
enumDecl->getIntegerType().getTypePtr());
2548+
vd->getASTContext().LangOpts.EnableCXXInterop) {
2549+
for (auto *attr : typedefType->getDecl()
2550+
->specific_attrs<clang::AvailabilityAttr>()) {
2551+
if (attr->getPlatform()->getName() == "swift") {
2552+
// Temporarily emulate the Objective-C definition of CF_OPTION
2553+
// for mangling, removing the temporary name attached to the
2554+
// anonymous enum once we are done with the value.
2555+
const_cast<clang::NamedDecl *>(namedDecl)->setDeclName(
2556+
typedefType->getDecl()->getDeclName());
2557+
return {namedDecl, [](const clang::NamedDecl *decl) {
2558+
const_cast<clang::NamedDecl *>(decl)->setDeclName(
2559+
clang::DeclarationName());
2560+
}};
2561+
}
2562+
}
2563+
}
2564+
}
2565+
2566+
if (auto *typedefDecl = tagDecl->getTypedefNameForAnonDecl()) {
25192567
namedDecl = typedefDecl;
2568+
}
2569+
}
25202570

25212571
if (namedDecl->getDeclName().isEmpty())
25222572
return nullptr;
@@ -2571,10 +2621,12 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) {
25712621
// have one.
25722622
auto tryAppendClangName = [this, decl]() -> bool {
25732623
auto *nominal = dyn_cast<NominalTypeDecl>(decl);
2574-
auto namedDecl = getClangDeclForMangling(decl);
2575-
if (!namedDecl)
2624+
auto namedDeclContainer = getClangDeclForMangling(decl);
2625+
if (!namedDeclContainer)
25762626
return false;
25772627

2628+
const clang::NamedDecl *namedDecl = namedDeclContainer.decl;
2629+
25782630
// Mangle ObjC classes using their runtime names.
25792631
auto interface = dyn_cast<clang::ObjCInterfaceDecl>(namedDecl);
25802632
auto protocol = dyn_cast<clang::ObjCProtocolDecl>(namedDecl);

lib/IRGen/GenMeta.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,8 +1225,10 @@ namespace {
12251225

12261226
// Otherwise, if this was imported from a Clang declaration, use that
12271227
// declaration's name as the ABI name.
1228-
} else if (auto clangDecl =
1229-
Mangle::ASTMangler::getClangDeclForMangling(Type)) {
1228+
} else if (auto clangDeclContainer =
1229+
Mangle::ASTMangler::getClangDeclForMangling(Type)) {
1230+
1231+
const clang::NamedDecl *clangDecl = clangDeclContainer.decl;
12301232
// Class template specializations need to use their mangled name so
12311233
// that each specialization gets its own metadata. A class template
12321234
// 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 SimpleNSOption {
37+
header "SimpleNSOption.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 SimpleNSOption
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)