Skip to content

Fix ASTMangler mangling NS_OPTION differently in C++ mode #64043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions include/swift/AST/ASTMangler.h
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,11 @@ class ASTMangler : public Mangler {
static Optional<SpecialContext>
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,
Expand Down
3 changes: 3 additions & 0 deletions include/swift/AST/ClangModuleLoader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions include/swift/ClangImporter/ClangImporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,9 @@ class ClangImporter final : public ClangModuleLoader {

/// Enable the symbolic import experimental feature for the given callback.
void withSymbolicFeatureEnabled(llvm::function_ref<void(void)> callback);

const clang::TypedefType *getTypeDefForCXXCFOptionsDefinition(
const clang::Decl *candidateDecl) override;
};

ImportDecl *createImportDecl(ASTContext &Ctx, DeclContext *DC, ClangNode ClangN,
Expand Down
58 changes: 49 additions & 9 deletions lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -2168,7 +2175,13 @@ ASTMangler::getSpecialManglingContext(const ValueDecl *decl,
if (auto *clangDecl = cast_or_null<clang::NamedDecl>(decl->getClangDecl())){
bool hasNameForLinkage;
if (auto *tagDecl = dyn_cast<clang::TagDecl>(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) {
Expand Down Expand Up @@ -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<clang::NamedDecl>(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<clang::NamedDecl>(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())
Expand Down Expand Up @@ -2574,8 +2602,20 @@ void ASTMangler::appendAnyGenericType(const GenericTypeDecl *decl) {
auto tryAppendClangName = [this, decl]() -> bool {
auto *nominal = dyn_cast<NominalTypeDecl>(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<clang::ObjCInterfaceDecl>(namedDecl);
Expand Down
29 changes: 29 additions & 0 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<clang::EnumDecl>(candidateDecl);
if (!enumDecl)
return nullptr;

if (!enumDecl->getDeclName().isEmpty())
return nullptr;

if (auto typedefType = dyn_cast<clang::TypedefType>(
enumDecl->getIntegerType().getTypePtr())) {
if (auto enumExtensibilityAttr =
typedefType->getDecl()->getAttr<clang::EnumExtensibilityAttr>();
enumExtensibilityAttr &&
enumExtensibilityAttr->getExtensibility() ==
clang::EnumExtensibilityAttr::Open &&
typedefType->getDecl()->hasAttr<clang::FlagEnumAttr>()) {
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")
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenMeta.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions test/Interop/Cxx/enum/Inputs/CFAvailability.h
Original file line number Diff line number Diff line change
@@ -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
};
4 changes: 4 additions & 0 deletions test/Interop/Cxx/enum/Inputs/module.modulemap
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ module CenumsNSOptionsExternC [extern_c] {
header "c-enums-NS_OPTIONS_without_extern_C.h"
requires cplusplus
}

module CFAvailability {
header "CFAvailability.h"
}
Original file line number Diff line number Diff line change
@@ -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]]