Skip to content

Commit a562c0e

Browse files
authored
Merge pull request #6720 from jrose-apple/one-swift-two-swift-old-swift-new-swift
Begin importing APIs under both Swift 3 and Swift 4 names, to produce errors
2 parents ae1c984 + c9124d9 commit a562c0e

13 files changed

+309
-118
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,17 +2738,51 @@ void ClangImporter::Implementation::lookupValue(
27382738
}
27392739
}
27402740

2741-
// If we have a declaration and nothing matched so far, try the Swift 2
2742-
// name.
2741+
// If we have a declaration and nothing matched so far, try the names used
2742+
// in other versions of Swift.
27432743
if (!anyMatching) {
27442744
if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) {
2745-
if (auto swift2Decl = cast_or_null<ValueDecl>(importDeclReal(
2746-
clangDecl->getMostRecentDecl(), ImportNameVersion::Swift2))) {
2747-
if (swift2Decl->getFullName().matchesRef(name) &&
2748-
swift2Decl->getDeclContext()->isModuleScopeContext()) {
2749-
consumer.foundDecl(swift2Decl,
2745+
const clang::NamedDecl *recentClangDecl =
2746+
clangDecl->getMostRecentDecl();
2747+
auto tryImport = [&](ImportNameVersion nameVersion) -> bool {
2748+
// Check to see if the name and context match what we expect.
2749+
ImportedName newName = importFullName(recentClangDecl, nameVersion);
2750+
if (!newName.getDeclName().matchesRef(name))
2751+
return false;
2752+
2753+
const clang::DeclContext *clangDC =
2754+
newName.getEffectiveContext().getAsDeclContext();
2755+
if (!clangDC || !clangDC->isFileContext())
2756+
return false;
2757+
2758+
// Then try to import the decl under the alternate name.
2759+
auto alternateNamedDecl =
2760+
cast_or_null<ValueDecl>(importDeclReal(recentClangDecl,
2761+
nameVersion));
2762+
if (!alternateNamedDecl)
2763+
return false;
2764+
assert(alternateNamedDecl->getFullName().matchesRef(name) &&
2765+
"importFullName behaved differently from importDecl");
2766+
if (alternateNamedDecl->getDeclContext()->isModuleScopeContext()) {
2767+
consumer.foundDecl(alternateNamedDecl,
27502768
DeclVisibilityKind::VisibleAtTopLevel);
2769+
return true;
27512770
}
2771+
return false;
2772+
};
2773+
2774+
// Try importing previous versions of the decl first...
2775+
ImportNameVersion nameVersion = CurrentVersion;
2776+
while (!anyMatching && nameVersion != ImportNameVersion::Raw) {
2777+
--nameVersion;
2778+
anyMatching = tryImport(nameVersion);
2779+
}
2780+
// ...then move on to newer versions if none of the old versions
2781+
// matched.
2782+
nameVersion = CurrentVersion;
2783+
while (!anyMatching && nameVersion != ImportNameVersion::LAST_VERSION) {
2784+
++nameVersion;
2785+
anyMatching = tryImport(nameVersion);
27522786
}
27532787
}
27542788
}

lib/ClangImporter/ImportDecl.cpp

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2009,10 +2009,34 @@ namespace {
20092009
/*fullyQualified=*/correctSwiftName.importAsMember(), os);
20102010
}
20112011

2012-
auto attr = AvailableAttr::createPlatformAgnostic(
2013-
ctx, StringRef(), ctx.AllocateCopy(renamed.str()),
2014-
PlatformAgnosticAvailabilityKind::SwiftVersionSpecific,
2015-
clang::VersionTuple(3));
2012+
unsigned majorVersion = majorVersionNumberForNameVersion(getVersion());
2013+
DeclAttribute *attr;
2014+
if (isActiveSwiftVersion() || getVersion() == ImportNameVersion::Raw) {
2015+
// "Raw" is the Objective-C name, which was never available in Swift.
2016+
// Variants within the active version are usually declarations that
2017+
// have been superseded, like the accessors of a property.
2018+
attr = AvailableAttr::createPlatformAgnostic(
2019+
ctx, /*Message*/StringRef(), ctx.AllocateCopy(renamed.str()),
2020+
PlatformAgnosticAvailabilityKind::UnavailableInSwift);
2021+
} else if (getVersion() < getActiveSwiftVersion()) {
2022+
// A Swift 2 name, for example, was obsoleted in Swift 3.
2023+
attr = AvailableAttr::createPlatformAgnostic(
2024+
ctx, /*Message*/StringRef(), ctx.AllocateCopy(renamed.str()),
2025+
PlatformAgnosticAvailabilityKind::SwiftVersionSpecific,
2026+
clang::VersionTuple(majorVersion + 1));
2027+
} else {
2028+
// Future names are introduced in their future version.
2029+
assert(getVersion() > getActiveSwiftVersion());
2030+
attr = new (ctx) AvailableAttr(
2031+
SourceLoc(), SourceRange(), PlatformKind::none,
2032+
/*Message*/StringRef(), ctx.AllocateCopy(renamed.str()),
2033+
/*Introduced*/clang::VersionTuple(majorVersion), SourceRange(),
2034+
/*Deprecated*/clang::VersionTuple(), SourceRange(),
2035+
/*Obsoleted*/clang::VersionTuple(), SourceRange(),
2036+
PlatformAgnosticAvailabilityKind::SwiftVersionSpecific,
2037+
/*Implicit*/true);
2038+
}
2039+
20162040
decl->getAttrs().add(attr);
20172041
decl->setImplicit();
20182042
}
@@ -3485,31 +3509,12 @@ namespace {
34853509
{decl->param_begin(), decl->param_size()},
34863510
decl->isVariadic(), redundant);
34873511

3488-
// Directly ask the NameImporter for the non-init variant of the Swift 2
3489-
// name.
3490-
auto rawName = Impl.importFullName(decl, ImportNameVersion::Raw);
3491-
if (!rawName)
3492-
return result;
3493-
3494-
auto rawDecl = importNonInitObjCMethodDecl(decl, dc, rawName, selector,
3495-
forceClassMethod);
3496-
if (!rawDecl)
3497-
return result;
3498-
3499-
// Mark the raw imported class method "unavailable", with a useful error
3500-
// message.
3501-
llvm::SmallString<64> message;
3502-
llvm::raw_svector_ostream os(message);
3503-
os << "use object construction '" << decl->getClassInterface()->getName()
3504-
<< "(";
3505-
for (auto arg : importedName.getDeclName().getArgumentNames()) {
3506-
os << arg << ":";
3512+
if (auto rawDecl = Impl.importDecl(decl, ImportNameVersion::Raw)) {
3513+
// We expect the raw decl to always be a method.
3514+
assert(isa<FuncDecl>(rawDecl));
3515+
Impl.addAlternateDecl(result, cast<ValueDecl>(rawDecl));
35073516
}
3508-
os << ")'";
3509-
rawDecl->getAttrs().add(AvailableAttr::createPlatformAgnostic(
3510-
Impl.SwiftContext, Impl.SwiftContext.AllocateCopy(os.str())));
3511-
markAsVariant(rawDecl, importedName);
3512-
Impl.addAlternateDecl(result, cast<ValueDecl>(rawDecl));
3517+
35133518
return result;
35143519
}
35153520

lib/ClangImporter/ImportName.cpp

Lines changed: 99 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,20 @@ importer::nameVersionFromOptions(const LangOptions &langOpts) {
6868
}
6969
}
7070

71+
unsigned importer::majorVersionNumberForNameVersion(ImportNameVersion version) {
72+
switch (version) {
73+
case ImportNameVersion::Raw:
74+
return 0;
75+
case ImportNameVersion::Swift2:
76+
return 2;
77+
case ImportNameVersion::Swift3:
78+
return 3;
79+
case ImportNameVersion::Swift4:
80+
return 4;
81+
}
82+
}
83+
84+
7185
/// Determine whether the given Clang selector matches the given
7286
/// selector pieces.
7387
static bool isNonNullarySelector(clang::Selector selector,
@@ -560,12 +574,90 @@ determineCtorInitializerKind(const clang::ObjCMethodDecl *method) {
560574
return None;
561575
}
562576

577+
template <typename A>
578+
static bool matchesVersion(A *versionedAttr, ImportNameVersion version) {
579+
clang::VersionTuple attrVersion = versionedAttr->getVersion();
580+
if (attrVersion.empty())
581+
return version == ImportNameVersion::LAST_VERSION;
582+
return attrVersion.getMajor() == majorVersionNumberForNameVersion(version);
583+
}
584+
585+
const clang::SwiftNameAttr *
586+
importer::findSwiftNameAttr(const clang::Decl *decl,
587+
ImportNameVersion version) {
588+
if (version == ImportNameVersion::Raw)
589+
return nullptr;
590+
591+
// Handle versioned API notes for Swift 3 and later. This is the common case.
592+
if (version != ImportNameVersion::Swift2) {
593+
for (auto *attr : decl->attrs()) {
594+
if (auto *versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(attr)) {
595+
if (!matchesVersion(versionedAttr, version))
596+
continue;
597+
if (auto *added =
598+
dyn_cast<clang::SwiftNameAttr>(versionedAttr->getAttrToAdd())) {
599+
return added;
600+
}
601+
}
602+
603+
if (auto *removeAttr = dyn_cast<clang::SwiftVersionedRemovalAttr>(attr)) {
604+
if (!matchesVersion(removeAttr, version))
605+
continue;
606+
if (removeAttr->getAttrKindToRemove() == clang::attr::SwiftName)
607+
return nullptr;
608+
}
609+
}
610+
611+
return decl->getAttr<clang::SwiftNameAttr>();
612+
}
613+
614+
// The remainder of this function emulates the limited form of swift_name
615+
// supported in Swift 2.
616+
auto attr = decl->getAttr<clang::SwiftNameAttr>();
617+
if (!attr) return nullptr;
618+
619+
// API notes produce implicit attributes; ignore them because they weren't
620+
// used for naming in Swift 2.
621+
if (attr->isImplicit()) return nullptr;
622+
623+
// Whitelist certain explicitly-written Swift names that were
624+
// permitted and used in Swift 2. All others are ignored, so that we are
625+
// assuming a more direct translation from the Objective-C APIs into Swift.
626+
627+
if (auto enumerator = dyn_cast<clang::EnumConstantDecl>(decl)) {
628+
// Foundation's NSXMLDTDKind had an explicit swift_name attribute in
629+
// Swift 2. Honor it.
630+
if (enumerator->getName() == "NSXMLDTDKind") return attr;
631+
return nullptr;
632+
}
633+
634+
if (auto method = dyn_cast<clang::ObjCMethodDecl>(decl)) {
635+
// Special case: mapping to an initializer.
636+
if (attr->getName().startswith("init(")) {
637+
// If we have a class method, honor the annotation to turn a class
638+
// method into an initializer.
639+
if (method->isClassMethod()) return attr;
640+
641+
return nullptr;
642+
}
643+
644+
// Special case: preventing a mapping to an initializer.
645+
if (matchFactoryAsInitName(method) && determineCtorInitializerKind(method))
646+
return attr;
647+
648+
return nullptr;
649+
}
650+
651+
return nullptr;
652+
}
653+
563654
/// Determine whether the given class method should be imported as
564655
/// an initializer.
565656
static FactoryAsInitKind
566657
getFactoryAsInit(const clang::ObjCInterfaceDecl *classDecl,
567-
const clang::ObjCMethodDecl *method) {
568-
if (auto *customNameAttr = method->getAttr<clang::SwiftNameAttr>()) {
658+
const clang::ObjCMethodDecl *method,
659+
ImportNameVersion version) {
660+
if (auto *customNameAttr = findSwiftNameAttr(method, version)) {
569661
if (customNameAttr->getName().startswith("init("))
570662
return FactoryAsInitKind::AsInitializer;
571663
else
@@ -586,6 +678,7 @@ getFactoryAsInit(const clang::ObjCInterfaceDecl *classDecl,
586678
/// imported. Note that this does not distinguish designated
587679
/// vs. convenience; both will be classified as "designated".
588680
static bool shouldImportAsInitializer(const clang::ObjCMethodDecl *method,
681+
ImportNameVersion version,
589682
unsigned &prefixLength,
590683
CtorInitializerKind &kind) {
591684
/// Is this an initializer?
@@ -604,7 +697,7 @@ static bool shouldImportAsInitializer(const clang::ObjCMethodDecl *method,
604697

605698
// Check whether we should try to import this factory method as an
606699
// initializer.
607-
switch (getFactoryAsInit(objcClass, method)) {
700+
switch (getFactoryAsInit(objcClass, method, version)) {
608701
case FactoryAsInitKind::AsInitializer:
609702
// Okay; check for the correct result type below.
610703
prefixLength = 0;
@@ -634,55 +727,6 @@ static bool shouldImportAsInitializer(const clang::ObjCMethodDecl *method,
634727
return false;
635728
}
636729

637-
/// Find the swift_name attribute associated with this declaration, if
638-
/// any.
639-
///
640-
/// \param version The version we're importing the name as
641-
static clang::SwiftNameAttr *findSwiftNameAttr(const clang::Decl *decl,
642-
ImportNameVersion version) {
643-
// Find the attribute.
644-
auto attr = decl->getAttr<clang::SwiftNameAttr>();
645-
if (!attr) return nullptr;
646-
647-
// If we're not emulating the Swift 2 behavior, return what we got.
648-
if (version != ImportNameVersion::Swift2)
649-
return attr;
650-
651-
// API notes produce implicit attributes; ignore them because they weren't
652-
// used for naming in Swift 2.
653-
if (attr->isImplicit()) return nullptr;
654-
655-
// Whitelist certain explicitly-written Swift names that were
656-
// permitted and used in Swift 2. All others are ignored, so that we are
657-
// assuming a more direct translation from the Objective-C APIs into Swift.
658-
659-
if (auto enumerator = dyn_cast<clang::EnumConstantDecl>(decl)) {
660-
// Foundation's NSXMLDTDKind had an explicit swift_name attribute in
661-
// Swift 2. Honor it.
662-
if (enumerator->getName() == "NSXMLDTDKind") return attr;
663-
return nullptr;
664-
}
665-
666-
if (auto method = dyn_cast<clang::ObjCMethodDecl>(decl)) {
667-
// Special case: mapping to an initializer.
668-
if (attr->getName().startswith("init(")) {
669-
// If we have a class method, honor the annotation to turn a class
670-
// method into an initializer.
671-
if (method->isClassMethod()) return attr;
672-
673-
return nullptr;
674-
}
675-
676-
// Special case: preventing a mapping to an initializer.
677-
if (matchFactoryAsInitName(method) && determineCtorInitializerKind(method))
678-
return attr;
679-
680-
return nullptr;
681-
}
682-
683-
return nullptr;
684-
}
685-
686730
/// Attempt to omit needless words from the given function name.
687731
static bool omitNeedlessWordsInFunctionName(
688732
StringRef &baseName, SmallVectorImpl<StringRef> &argumentNames,
@@ -1179,7 +1223,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
11791223
if (method) {
11801224
unsigned initPrefixLength;
11811225
if (parsedName.BaseName == "init" && parsedName.IsFunctionName) {
1182-
if (!shouldImportAsInitializer(method, initPrefixLength,
1226+
if (!shouldImportAsInitializer(method, version, initPrefixLength,
11831227
result.info.initKind)) {
11841228
// We cannot import this as an initializer anyway.
11851229
return ImportedName();
@@ -1354,7 +1398,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
13541398
if (baseName.empty())
13551399
return ImportedName();
13561400

1357-
isInitializer = shouldImportAsInitializer(objcMethod, initializerPrefixLen,
1401+
isInitializer = shouldImportAsInitializer(objcMethod, version,
1402+
initializerPrefixLen,
13581403
result.info.initKind);
13591404

13601405
// If we would import a factory method as an initializer but were

0 commit comments

Comments
 (0)