Skip to content

Commit 6a6b4f6

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 cad2c7b commit 6a6b4f6

File tree

9 files changed

+143
-15
lines changed

9 files changed

+143
-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(

include/swift/AST/ClangModuleLoader.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,8 @@ 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* getTypeDefForCXXCFOptionsDefinition(const clang::Decl* candidateDecl) = 0;
289291
};
290292

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

include/swift/ClangImporter/ClangImporter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,8 @@ class ClangImporter final : public ClangModuleLoader {
558558

559559
/// Enable the symbolic import experimental feature for the given callback.
560560
void withSymbolicFeatureEnabled(llvm::function_ref<void(void)> callback);
561+
562+
const clang::TypedefType* getTypeDefForCXXCFOptionsDefinition(const clang::Decl* candidateDecl) override;
561563
};
562564

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

lib/AST/ASTMangler.cpp

Lines changed: 46 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,19 @@ 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+
const auto& clangImporter = decl->getASTContext().getClangModuleLoader();
2178+
if (clangImporter->getTypeDefForCXXCFOptionsDefinition(tagDecl)) {
2179+
hasNameForLinkage = true;
2180+
}
2181+
}
2182+
} else
21732183
hasNameForLinkage = !clangDecl->getDeclName().isEmpty();
21742184
if (hasNameForLinkage) {
21752185
auto *clangDC = clangDecl->getDeclContext();
@@ -2506,17 +2516,39 @@ void ASTMangler::appendProtocolName(const ProtocolDecl *protocol,
25062516
appendDeclName(protocol);
25072517
}
25082518

2509-
const clang::NamedDecl *ASTMangler::getClangDeclForMangling(const ValueDecl *vd) {
2510-
auto namedDecl = dyn_cast_or_null<clang::NamedDecl>(vd->getClangDecl());
2519+
const ASTMangler::ClangDeclForManglingContainer
2520+
ASTMangler::getClangDeclForMangling(const ValueDecl *vd) {
2521+
auto namedDecl = dyn_cast_or_null<clang::NamedDecl>(vd->getClangDecl());
25112522
if (!namedDecl)
25122523
return nullptr;
2513-
2524+
25142525
// Use an anonymous enum's enclosing typedef for the mangled name, if
25152526
// present. This matches C++'s rules for linkage names of tag declarations.
25162527
if (namedDecl->getDeclName().isEmpty())
2517-
if (auto *tagDecl = dyn_cast<clang::TagDecl>(namedDecl))
2518-
if (auto *typedefDecl = tagDecl->getTypedefNameForAnonDecl())
2528+
if (auto *tagDecl = dyn_cast<clang::TagDecl>(namedDecl)) {
2529+
// CF_OPTION derived types are different in Objective-C vs Objective-C++
2530+
// due different definitions in the SDK based on if __cplusplus is
2531+
// defined. The linkage name of these CF_OPTION derived types must be
2532+
// consistent regardless of if cxx interop is turned on or not,
2533+
// otherwise modules compiled with cxx interop cannot interoperate with
2534+
// those compiled without it.
2535+
const auto& clangModuleLoader = vd->getASTContext().getClangModuleLoader();
2536+
if (auto typedefType = clangModuleLoader->getTypeDefForCXXCFOptionsDefinition(tagDecl)) {
2537+
// Temporarily emulate the Objective-C definition of CF_OPTION
2538+
// for mangling, removing the temporary name attached to the
2539+
// anonymous enum once we are done with the value.
2540+
const_cast<clang::NamedDecl *>(namedDecl)->setDeclName(
2541+
typedefType->getDecl()->getDeclName());
2542+
return {namedDecl, [](const clang::NamedDecl *decl) {
2543+
const_cast<clang::NamedDecl *>(decl)->setDeclName(
2544+
clang::DeclarationName());
2545+
}};
2546+
}
2547+
2548+
if (auto *typedefDecl = tagDecl->getTypedefNameForAnonDecl()) {
25192549
namedDecl = typedefDecl;
2550+
}
2551+
}
25202552

25212553
if (namedDecl->getDeclName().isEmpty())
25222554
return nullptr;
@@ -2571,10 +2603,12 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) {
25712603
// have one.
25722604
auto tryAppendClangName = [this, decl]() -> bool {
25732605
auto *nominal = dyn_cast<NominalTypeDecl>(decl);
2574-
auto namedDecl = getClangDeclForMangling(decl);
2575-
if (!namedDecl)
2606+
auto namedDeclContainer = getClangDeclForMangling(decl);
2607+
if (!namedDeclContainer)
25762608
return false;
25772609

2610+
const clang::NamedDecl *namedDecl = namedDeclContainer.decl;
2611+
25782612
// Mangle ObjC classes using their runtime names.
25792613
auto interface = dyn_cast<clang::ObjCInterfaceDecl>(namedDecl);
25802614
auto protocol = dyn_cast<clang::ObjCProtocolDecl>(namedDecl);

lib/ClangImporter/ClangImporter.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6580,6 +6580,32 @@ void ClangImporter::withSymbolicFeatureEnabled(
65806580
oldImportSymbolicCXXDecls.get());
65816581
}
65826582

6583+
const clang::TypedefType* ClangImporter::getTypeDefForCXXCFOptionsDefinition(const clang::Decl* candidateDecl) {
6584+
6585+
if (!Impl.SwiftContext.LangOpts.EnableCXXInterop)
6586+
return nullptr;
6587+
6588+
auto enumDecl = dyn_cast<clang::EnumDecl>(candidateDecl);
6589+
if (!enumDecl)
6590+
return nullptr;
6591+
6592+
if (!enumDecl->getDeclName().isEmpty())
6593+
return nullptr;
6594+
6595+
clang::SourceManager& SM = getClangASTContext().getSourceManager();
6596+
clang::SourceLocation SpellingLoc = SM.getSpellingLoc(enumDecl->getLocation());
6597+
clang::PresumedLoc PLoc = SM.getPresumedLoc(SpellingLoc);
6598+
if (PLoc.isValid() && !strstr(PLoc.getFilename(), "CFAvailability"))
6599+
return nullptr;
6600+
6601+
if (auto typedefType = dyn_cast<clang::TypedefType>(
6602+
enumDecl->getIntegerType().getTypePtr())) {
6603+
return Impl.isUnavailableInSwift(typedefType->getDecl()) ? typedefType : nullptr;
6604+
}
6605+
6606+
return nullptr;
6607+
}
6608+
65836609
bool importer::requiresCPlusPlus(const clang::Module *module) {
65846610
// The libc++ modulemap doesn't currently declare the requirement.
65856611
if (module->getTopLevelModuleName() == "std")

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 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)