Skip to content

Commit

Permalink
Clang importer: introduce "importFullName" to centralize name-mapping…
Browse files Browse the repository at this point in the history
… logic.

The Swift lookup tables are the primary client and test vehicle right
now. This change adds the capability to use the swift_name attribute
to rename C functions when they are imported into Swift, as well as
handling the swift_private attribute more uniformly.

There are a few obvious places where I've applied this API to
eliminate redundancy. Expect it to broaden as the API fills out more.
  • Loading branch information
DougGregor committed Dec 3, 2015
1 parent c2bf16c commit a861a73
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 55 deletions.
204 changes: 202 additions & 2 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,8 +743,8 @@ bool ClangImporter::Implementation::importHeader(

if (UseSwiftLookupTables) {
if (auto named = dyn_cast<clang::NamedDecl>(D)) {
Identifier name = importName(named);
if (!name.empty())
bool hasCustomName;
if (DeclName name = importFullName(named, hasCustomName))
BridgingHeaderLookupTable.addEntry(name, named);
}
}
Expand Down Expand Up @@ -1288,6 +1288,206 @@ ClangImporter::Implementation::exportName(Identifier name) {
return ident;
}

/// Parse a stringified Swift DeclName, e.g. "init(frame:)".
static DeclName parseDeclName(ASTContext &ctx, StringRef Name) {
if (Name.back() != ')') {
if (Lexer::isIdentifier(Name) && Name != "_")
return ctx.getIdentifier(Name);

return {};
}

StringRef BaseName, Parameters;
std::tie(BaseName, Parameters) = Name.split('(');
if (!Lexer::isIdentifier(BaseName) || BaseName == "_")
return {};

if (Parameters.empty())
return {};
Parameters = Parameters.drop_back(); // ')'

Identifier BaseID = ctx.getIdentifier(BaseName);
if (Parameters.empty())
return DeclName(ctx, BaseID, {});

if (Parameters.back() != ':')
return {};

SmallVector<Identifier, 4> ParamIDs;
do {
StringRef NextParam;
std::tie(NextParam, Parameters) = Parameters.split(':');

if (!Lexer::isIdentifier(NextParam))
return {};
Identifier NextParamID;
if (NextParam != "_")
NextParamID = ctx.getIdentifier(NextParam);
ParamIDs.push_back(NextParamID);
} while (!Parameters.empty());

return DeclName(ctx, BaseID, ParamIDs);
}

DeclName ClangImporter::Implementation::importFullName(
const clang::NamedDecl *D,
bool &hasCustomName) {
// If we have a swift_name attribute, use that.
if (auto *nameAttr = D->getAttr<clang::SwiftNameAttr>()) {
hasCustomName = true;
return parseDeclName(SwiftContext, nameAttr->getName());
}

// We don't have a customized name.
hasCustomName = false;

// For empty names, there is nothing to do.
if (D->getDeclName().isEmpty()) return { };

/// Whether the result is a function name.
bool isFunction = false;
StringRef baseName;
SmallVector<StringRef, 4> argumentNames;
switch (D->getDeclName().getNameKind()) {
case clang::DeclarationName::CXXConstructorName:
case clang::DeclarationName::CXXConversionFunctionName:
case clang::DeclarationName::CXXDestructorName:
case clang::DeclarationName::CXXLiteralOperatorName:
case clang::DeclarationName::CXXOperatorName:
case clang::DeclarationName::CXXUsingDirective:
// Handling these is part of C++ interoperability.
return { };

case clang::DeclarationName::Identifier:
// Map the identifier.
baseName = D->getDeclName().getAsIdentifierInfo()->getName();
break;

case clang::DeclarationName::ObjCMultiArgSelector:
case clang::DeclarationName::ObjCOneArgSelector:
case clang::DeclarationName::ObjCZeroArgSelector: {
// Map the Objective-C selector directly.
auto selector = D->getDeclName().getObjCSelector();
baseName = selector.getNameForSlot(0);
for (unsigned index = 0, numArgs = selector.getNumArgs(); index != numArgs;
++index) {
if (index == 0)
argumentNames.push_back("");
else
argumentNames.push_back(selector.getNameForSlot(index));
}

isFunction = true;
break;
}
}

// Local function to determine whether the given declaration is subject to
// a swift_private attribute.
auto hasSwiftPrivate = [this](const clang::NamedDecl *D) {
if (D->hasAttr<clang::SwiftPrivateAttr>())
return true;

// Enum constants that are not imported as members should be considered
// private if the parent enum is marked private.
if (auto *ECD = dyn_cast<clang::EnumConstantDecl>(D)) {
auto *ED = cast<clang::EnumDecl>(ECD->getDeclContext());
switch (classifyEnum(ED)) {
case EnumKind::Constants:
case EnumKind::Unknown:
if (ED->hasAttr<clang::SwiftPrivateAttr>())
return true;
if (auto *enumTypedef = ED->getTypedefNameForAnonDecl())
if (enumTypedef->hasAttr<clang::SwiftPrivateAttr>())
return true;
break;

case EnumKind::Enum:
case EnumKind::Options:
break;
}
}

return false;
};

// Omit needless words.
StringScratchSpace omitNeedlessWordsScratch;
if (OmitNeedlessWords) {
// Objective-C properties.
if (auto objcProperty = dyn_cast<clang::ObjCPropertyDecl>(D)) {
auto contextType = getClangDeclContextType(D->getDeclContext());
if (!contextType.isNull()) {
auto contextTypeName = getClangTypeNameForOmission(contextType);
auto propertyTypeName = getClangTypeNameForOmission(
objcProperty->getType());
// Find the property names.
const InheritedNameSet *allPropertyNames = nullptr;
if (!contextType.isNull()) {
if (auto objcPtrType = contextType->getAsObjCInterfacePointerType())
if (auto objcClassDecl = objcPtrType->getInterfaceDecl())
allPropertyNames = SwiftContext.getAllPropertyNames(
objcClassDecl,
/*forInstance=*/true);
}

(void)omitNeedlessWords(baseName, { }, "", propertyTypeName,
contextTypeName, { }, /*returnsSelf=*/false,
/*isProperty=*/true, allPropertyNames,
omitNeedlessWordsScratch);
}
}
}

// If this declaration has the swift_private attribute, prepend "__" to the
// appropriate place.
SmallString<16> swiftPrivateScratch;
if (hasSwiftPrivate(D)) {
swiftPrivateScratch = "__";

if (baseName == "init") {
// For initializers, prepend "__" to the first argument name.
if (argumentNames.empty()) {
// swift_private cannot actually do anything here. Just drop the
// declaration.
// FIXME: Diagnose this?
return { };
}

swiftPrivateScratch += argumentNames[0];
argumentNames[0] = swiftPrivateScratch;
} else {
// For all other entities, prepend "__" to the base name.
swiftPrivateScratch += baseName;
baseName = swiftPrivateScratch;
}
}

// We cannot import when the base name is not an identifier.
if (!Lexer::isIdentifier(baseName))
return { };

// Get the identifier for the base name.
Identifier baseNameId = SwiftContext.getIdentifier(baseName);

// If we have a non-function name, just return the base name.
if (!isFunction) return baseNameId;

// Convert the argument names.
SmallVector<Identifier, 4> argumentNameIds;
for (auto argName : argumentNames) {
if (argumentNames.empty() || !Lexer::isIdentifier(argName)) {
argumentNameIds.push_back(Identifier());
continue;
}

argumentNameIds.push_back(SwiftContext.getIdentifier(argName));
}

// Build the result.
return DeclName(SwiftContext, baseNameId, argumentNameIds);
}

Identifier
ClangImporter::Implementation::importDeclName(clang::DeclarationName name,
StringRef removePrefix) {
Expand Down
57 changes: 4 additions & 53 deletions lib/ClangImporter/ImportDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2715,18 +2715,8 @@ namespace {
return nullptr;

// Determine the name of the function.
DeclName name;
bool hasCustomName;
if (auto *customNameAttr = decl->getAttr<clang::SwiftNameAttr>()) {
name = parseDeclName(customNameAttr->getName());
hasCustomName = true;
}

if (!name) {
name = Impl.importName(decl);
hasCustomName = false;
}

DeclName name = Impl.importFullName(decl, hasCustomName);
if (!name)
return nullptr;

Expand Down Expand Up @@ -2998,43 +2988,6 @@ namespace {
return result;
}

/// Parse a stringified Swift DeclName, e.g. "init(frame:)".
DeclName parseDeclName(StringRef Name) {
if (Name.back() != ')')
return {};

StringRef BaseName, Parameters;
std::tie(BaseName, Parameters) = Name.split('(');
if (!Lexer::isIdentifier(BaseName) || BaseName == "_")
return {};

if (Parameters.empty())
return {};
Parameters = Parameters.drop_back(); // ')'

Identifier BaseID = Impl.SwiftContext.getIdentifier(BaseName);
if (Parameters.empty())
return DeclName(Impl.SwiftContext, BaseID, {});

if (Parameters.back() != ':')
return {};

SmallVector<Identifier, 4> ParamIDs;
do {
StringRef NextParam;
std::tie(NextParam, Parameters) = Parameters.split(':');

if (!Lexer::isIdentifier(NextParam))
return {};
Identifier NextParamID;
if (NextParam != "_")
NextParamID = Impl.SwiftContext.getIdentifier(NextParam);
ParamIDs.push_back(NextParamID);
} while (!Parameters.empty());

return DeclName(Impl.SwiftContext, BaseID, ParamIDs);
}

/// If the given method is a factory method, import it as a constructor
Optional<ConstructorDecl *>
importFactoryMethodAsConstructor(Decl *member,
Expand All @@ -3056,9 +3009,8 @@ namespace {
// Check whether we're allowed to try.
switch (Impl.getFactoryAsInit(objcClass, decl)) {
case FactoryAsInitKind::AsInitializer:
if (auto *customNameAttr = decl->getAttr<clang::SwiftNameAttr>()) {
initName = parseDeclName(customNameAttr->getName());
hasCustomName = true;
if (decl->hasAttr<clang::SwiftNameAttr>()) {
initName = Impl.importFullName(decl, hasCustomName);
break;
}
// FIXME: We probably should stop using this codepath. It won't ever
Expand Down Expand Up @@ -3170,8 +3122,7 @@ namespace {
bool hasCustomName;
if (auto *customNameAttr = decl->getAttr<clang::SwiftNameAttr>()) {
if (!customNameAttr->getName().startswith("init(")) {
name = parseDeclName(customNameAttr->getName());
hasCustomName = true;
name = Impl.importFullName(decl, hasCustomName);
}
}
if (!name) {
Expand Down
7 changes: 7 additions & 0 deletions lib/ClangImporter/ImporterImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,13 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
/// \brief Converts the given Swift identifier for Clang.
clang::DeclarationName exportName(Identifier name);

/// Imports the full name of the given Clang declaration into Swift.
///
/// Note that this may result in a name very different from the Clang name,
/// so it should not be used when referencing Clang symbols.
DeclName importFullName(const clang::NamedDecl *D,
bool &hasCustomName);

/// Imports the name of the given Clang decl into Swift.
///
/// Note that this may result in a name different from the Clang name, so it
Expand Down
10 changes: 10 additions & 0 deletions test/IDE/Inputs/swift_name.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
#define SWIFT_NAME(X) __attribute__((swift_name(#X)))

// Renaming global variables.
int SNFoo SWIFT_NAME(Bar);

// Renaming tags and fields.
struct SWIFT_NAME(SomeStruct) SNSomeStruct {
double X SWIFT_NAME(x);
};

// Renaming C functions
struct SNSomeStruct SNMakeSomeStruct(double X, double Y) SWIFT_NAME(makeSomeStruct(x:y:));

struct SNSomeStruct SNMakeSomeStructForX(double X) SWIFT_NAME(makeSomeStruct(x:));

// swift_private attribute
void SNTransposeInPlace(struct SNSomeStruct *value) __attribute__((swift_private));
8 changes: 8 additions & 0 deletions test/IDE/dump_swift_lookup_tables.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,17 @@
// CHECK: Base -> full name mappings:
// CHECK-NEXT: Bar --> Bar
// CHECK-NEXT: SomeStruct --> SomeStruct
// CHECK-NEXT: __SNTransposeInPlace --> __SNTransposeInPlace
// CHECK-NEXT: makeSomeStruct --> makeSomeStruct(x:y:), makeSomeStruct(x:)

// CHECK: Full name -> entry mappings:
// CHECK-NEXT: Bar:
// CHECK-NEXT: TU: SNFoo
// CHECK-NEXT: SomeStruct:
// CHECK-NEXT: TU: SNSomeStruct
// CHECK-NEXT: __SNTransposeInPlace:
// CHECK-NEXT: TU: SNTransposeInPlace
// CHECK-NEXT: makeSomeStruct(x:):
// CHECK-NEXT: TU: SNMakeSomeStructForX
// CHECK-NEXT: makeSomeStruct(x:y:):
// CHECK-NEXT: TU: SNMakeSomeStruct

0 comments on commit a861a73

Please sign in to comment.