Skip to content
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
216 changes: 167 additions & 49 deletions lib/ClangImporter/ImportName.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -539,12 +539,26 @@ determineFactoryInitializerKind(const clang::ObjCMethodDecl *method) {
}

namespace {
/// Describes the details of any swift_name or swift_async_name
/// attribute found via
struct AnySwiftNameAttr {
/// The name itself.
StringRef name;

/// Whether this was a swift_async_name attribute.
bool isAsync;

friend bool operator==(AnySwiftNameAttr lhs, AnySwiftNameAttr rhs) {
return lhs.name == rhs.name && lhs.isAsync == rhs.isAsync;
}
};

/// Aggregate struct for the common members of clang::SwiftVersionedAttr and
/// clang::SwiftVersionedRemovalAttr.
///
/// For a SwiftVersionedRemovalAttr, the Attr member will be null.
struct VersionedSwiftNameInfo {
const clang::SwiftNameAttr *Attr;
Optional<AnySwiftNameAttr> Attr;
llvm::VersionTuple Version;
bool IsReplacedByActive;
};
Expand Down Expand Up @@ -594,8 +608,7 @@ checkVersionedSwiftName(VersionedSwiftNameInfo info,
return VersionedSwiftNameAction::Use;
}


static const clang::SwiftNameAttr *
static Optional<AnySwiftNameAttr>
findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
#ifndef NDEBUG
if (Optional<const clang::Decl *> def = getDefinitionForClangTypeDecl(decl)) {
Expand All @@ -605,7 +618,24 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
#endif

if (version == ImportNameVersion::raw())
return nullptr;
return None;

/// Decode the given Clang attribute to try to determine whether it is
/// a Swift name attribute.
auto decodeAttr =
[&](const clang::Attr *attr) -> Optional<AnySwiftNameAttr> {
if (version.supportsConcurrency()) {
if (auto asyncAttr = dyn_cast<clang::SwiftAsyncNameAttr>(attr)) {
return AnySwiftNameAttr { asyncAttr->getName(), /*isAsync=*/true };
}
}

if (auto nameAttr = dyn_cast<clang::SwiftNameAttr>(attr)) {
return AnySwiftNameAttr { nameAttr->getName(), /*isAsync=*/false };
}

return None;
};

// Handle versioned API notes for Swift 3 and later. This is the common case.
if (version > ImportNameVersion::swift2()) {
Expand All @@ -615,15 +645,22 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
if (importer::isSpecialUIKitStructZeroProperty(namedDecl))
version = ImportNameVersion::swift4_2();

const auto *activeAttr = decl->getAttr<clang::SwiftNameAttr>();
const clang::SwiftNameAttr *result = activeAttr;
// Dig out the attribute that specifies the Swift name.
Optional<AnySwiftNameAttr> activeAttr;
if (auto asyncAttr = decl->getAttr<clang::SwiftAsyncNameAttr>())
activeAttr = decodeAttr(asyncAttr);
if (!activeAttr) {
if (auto nameAttr = decl->getAttr<clang::SwiftNameAttr>())
activeAttr = decodeAttr(nameAttr);
}

Optional<AnySwiftNameAttr> result = activeAttr;
llvm::VersionTuple bestSoFar;
for (auto *attr : decl->attrs()) {
VersionedSwiftNameInfo info;

if (auto *versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(attr)) {
auto *added =
dyn_cast<clang::SwiftNameAttr>(versionedAttr->getAttrToAdd());
auto added = decodeAttr(versionedAttr->getAttrToAdd());
if (!added)
continue;

Expand All @@ -634,7 +671,7 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
dyn_cast<clang::SwiftVersionedRemovalAttr>(attr)) {
if (removeAttr->getAttrKindToRemove() != clang::attr::SwiftName)
continue;
info = {nullptr, removeAttr->getVersion(),
info = {None, removeAttr->getVersion(),
removeAttr->getIsReplacedByActive()};

} else {
Expand Down Expand Up @@ -673,11 +710,11 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
// The remainder of this function emulates the limited form of swift_name
// supported in Swift 2.
auto attr = decl->getAttr<clang::SwiftNameAttr>();
if (!attr) return nullptr;
if (!attr) return None;

// API notes produce attributes with no source location; ignore them because
// they weren't used for naming in Swift 2.
if (attr->getLocation().isInvalid()) return nullptr;
if (attr->getLocation().isInvalid()) return None;

// Hardcode certain kinds of explicitly-written Swift names that were
// permitted and used in Swift 2. All others are ignored, so that we are
Expand All @@ -686,28 +723,28 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
if (auto enumerator = dyn_cast<clang::EnumConstantDecl>(decl)) {
// Foundation's NSXMLDTDKind had an explicit swift_name attribute in
// Swift 2. Honor it.
if (enumerator->getName() == "NSXMLDTDKind") return attr;
return nullptr;
if (enumerator->getName() == "NSXMLDTDKind") return decodeAttr(attr);
return None;
}

if (auto method = dyn_cast<clang::ObjCMethodDecl>(decl)) {
// Special case: mapping to an initializer.
if (attr->getName().startswith("init(")) {
// If we have a class method, honor the annotation to turn a class
// method into an initializer.
if (method->isClassMethod()) return attr;
if (method->isClassMethod()) return decodeAttr(attr);

return nullptr;
return None;
}

// Special case: preventing a mapping to an initializer.
if (matchFactoryAsInitName(method) && determineFactoryInitializerKind(method))
return attr;
return decodeAttr(attr);

return nullptr;
return None;
}

return nullptr;
return None;
}

/// Determine whether the given class method should be imported as
Expand All @@ -716,8 +753,8 @@ static FactoryAsInitKind
getFactoryAsInit(const clang::ObjCInterfaceDecl *classDecl,
const clang::ObjCMethodDecl *method,
ImportNameVersion version) {
if (auto *customNameAttr = findSwiftNameAttr(method, version)) {
if (customNameAttr->getName().startswith("init("))
if (auto customNameAttr = findSwiftNameAttr(method, version)) {
if (customNameAttr->name.startswith("init("))
return FactoryAsInitKind::AsInitializer;
else
return FactoryAsInitKind::AsClassMethod;
Expand Down Expand Up @@ -1004,7 +1041,26 @@ bool NameImporter::hasNamingConflict(const clang::NamedDecl *decl,

static bool shouldBeSwiftPrivate(NameImporter &nameImporter,
const clang::NamedDecl *decl,
ImportNameVersion version) {
ImportNameVersion version,
bool isAsyncImport) {
// For an async import, check whether there is a swift_async attribute
// that specifies whether this should be considered swift_private or not.
if (isAsyncImport) {
if (auto *asyncAttr = decl->getAttr<clang::SwiftAsyncAttr>()) {
switch (asyncAttr->getKind()) {
case clang::SwiftAsyncAttr::None:
// Fall through to let us decide based on swift_private.
break;

case clang::SwiftAsyncAttr::SwiftPrivate:
return true;

case clang::SwiftAsyncAttr::NotSwiftPrivate:
return false;
}
}
}

// Decl with the attribute are obviously private
if (decl->hasAttr<clang::SwiftPrivateAttr>())
return true;
Expand Down Expand Up @@ -1172,39 +1228,71 @@ NameImporter::considerAsyncImport(
StringRef baseName,
SmallVectorImpl<StringRef> &paramNames,
ArrayRef<const clang::ParmVarDecl *> params,
bool isInitializer, bool hasCustomName,
bool isInitializer,
Optional<unsigned> explicitCompletionHandlerParamIndex,
CustomAsyncName customName,
Optional<ForeignErrorConvention::Info> errorInfo) {
// If there are no unclaimed parameters, there's no .
unsigned errorParamAdjust = errorInfo ? 1 : 0;
if (params.size() - errorParamAdjust == 0)
return None;

// When there is a custom async name, it will have removed the completion
// handler parameter already.
unsigned customAsyncNameAdjust =
customName == CustomAsyncName::SwiftAsyncName ? 1 : 0;

// If the # of parameter names doesn't line up with the # of parameters,
// bail out. There are extra C parameters on the method or a custom name
// was incorrect.
if (params.size() != paramNames.size() + errorParamAdjust)
if (params.size() !=
paramNames.size() + errorParamAdjust + customAsyncNameAdjust)
return None;

// The last parameter will be the completion handler for an async function.
unsigned completionHandlerParamIndex = params.size() - 1;
unsigned completionHandlerParamNameIndex = paramNames.size() - 1;

// Determine whether the naming indicates that this is a completion
// handler.
if (isCompletionHandlerParamName(
paramNames[completionHandlerParamNameIndex]) ||
(completionHandlerParamNameIndex > 0 &&
stripWithCompletionHandlerSuffix(
paramNames[completionHandlerParamNameIndex]))) {
// The argument label itself has an appropriate name.
} else if (!hasCustomName && completionHandlerParamIndex == 0 &&
stripWithCompletionHandlerSuffix(baseName)) {
// The base name implies that the first parameter is a completion handler.
} else if (isCompletionHandlerParamName(
params[completionHandlerParamIndex]->getName())) {
// The parameter has an appropriate name.
// If we don't already know the completion handler parameter index, go
// try to figure it out.
unsigned completionHandlerParamIndex;
unsigned completionHandlerParamNameIndex;
if (!explicitCompletionHandlerParamIndex) {
// Determine whether the naming indicates that this is a completion
// handler.
completionHandlerParamIndex = params.size() - 1;
completionHandlerParamNameIndex = paramNames.size() - 1;
switch (customName) {
case CustomAsyncName::None:
// Check whether the first parameter is the completion handler and the
// base name has a suitable completion-handler suffix.
if (completionHandlerParamIndex == 0 &&
stripWithCompletionHandlerSuffix(baseName))
break;

LLVM_FALLTHROUGH;

case CustomAsyncName::SwiftName:
// Check whether the argument label itself has an appropriate name.
if (isCompletionHandlerParamName(
paramNames[completionHandlerParamNameIndex]) ||
(completionHandlerParamNameIndex > 0 &&
stripWithCompletionHandlerSuffix(
paramNames[completionHandlerParamNameIndex]))) {
break;
}

// Check whether the parameter itself has a name that indicates that
// it is a completion handelr.
if (isCompletionHandlerParamName(
params[completionHandlerParamIndex]->getName()))
break;

return None;

case CustomAsyncName::SwiftAsyncName:
// Having a custom async name implies that this is a completion handler.
break;
}
} else {
return None;
completionHandlerParamIndex = *explicitCompletionHandlerParamIndex;
completionHandlerParamNameIndex = *explicitCompletionHandlerParamIndex;
}

// Used for returns once we've determined that the method cannot be
Expand Down Expand Up @@ -1284,8 +1372,16 @@ NameImporter::considerAsyncImport(
break;
}

// Drop the completion handler parameter name.
paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex);
// Drop the completion handler parameter name when needed.
switch (customName) {
case CustomAsyncName::None:
case CustomAsyncName::SwiftName:
paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex);
break;

case CustomAsyncName::SwiftAsyncName:
break;
}

return ForeignAsyncConvention::Info(
completionHandlerParamIndex, completionHandlerErrorParamIndex);
Expand Down Expand Up @@ -1380,6 +1476,20 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
return ImportedName();
result.effectiveContext = effectiveCtx;

// Gather information from the swift_async attribute, if there is one.
Optional<unsigned> completionHandlerParamIndex;
if (version.supportsConcurrency()) {
if (const auto *swiftAsyncAttr = D->getAttr<clang::SwiftAsyncAttr>()) {
// If this is swift_async(none), don't import as async at all.
if (swiftAsyncAttr->getKind() == clang::SwiftAsyncAttr::None)
return ImportedName();

// Get the completion handler parameter index, if there is one.
completionHandlerParamIndex =
swiftAsyncAttr->getCompletionHandlerIndex().getASTIndex();
}
}

// FIXME: ugly to check here, instead perform unified check up front in
// containing struct...
if (findSwiftNewtype(D, clangSema, version))
Expand Down Expand Up @@ -1449,11 +1559,11 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
}

// If we have a swift_name attribute, use that.
if (auto *nameAttr = findSwiftNameAttr(D, version)) {
if (auto nameAttr = findSwiftNameAttr(D, version)) {
bool skipCustomName = false;

// Parse the name.
ParsedDeclName parsedName = parseDeclName(nameAttr->getName());
ParsedDeclName parsedName = parseDeclName(nameAttr->name);
if (!parsedName || parsedName.isOperator())
return result;

Expand Down Expand Up @@ -1528,7 +1638,10 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
if (version.supportsConcurrency()) {
if (auto asyncInfo = considerAsyncImport(
method, parsedName.BaseName, parsedName.ArgumentLabels,
params, isInitializer, /*hasCustomName=*/true,
params, isInitializer,
completionHandlerParamIndex,
nameAttr->isAsync ? CustomAsyncName::SwiftAsyncName
: CustomAsyncName::SwiftName,
result.getErrorInfo())) {
result.info.hasAsyncInfo = true;
result.info.asyncInfo = *asyncInfo;
Expand All @@ -1537,6 +1650,10 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
result.declName = formDeclName(
swiftCtx, parsedName.BaseName, parsedName.ArgumentLabels,
/*isFunction=*/true, isInitializer);
} else if (nameAttr->isAsync) {
// The custom name was for an async import, but we didn't in fact
// import as async for some reason. Ignore this import.
return ImportedName();
}
}
}
Expand Down Expand Up @@ -1812,7 +1929,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
result.info.accessorKind == ImportedAccessorKind::None) {
if (auto asyncInfo = considerAsyncImport(
objcMethod, baseName, argumentNames, params, isInitializer,
/*hasCustomName=*/false, result.getErrorInfo())) {
completionHandlerParamIndex, CustomAsyncName::None,
result.getErrorInfo())) {
result.info.hasAsyncInfo = true;
result.info.asyncInfo = *asyncInfo;
}
Expand Down Expand Up @@ -1987,7 +2105,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
// If this declaration has the swift_private attribute, prepend "__" to the
// appropriate place.
SmallString<16> swiftPrivateScratch;
if (shouldBeSwiftPrivate(*this, D, version)) {
if (shouldBeSwiftPrivate(*this, D, version, result.info.hasAsyncInfo)) {
// Special case: empty arg factory, "for historical reasons", is not private
if (isInitializer && argumentNames.empty() &&
(result.getInitKind() == CtorInitializerKind::Factory ||
Expand Down
Loading