[ClangImporter] Always import types under their Swift 4 name.

This means all cross-module references and all mangled names will
consistently use the Swift 4 name (the canonical type), no special
handling required.

The main thing we lose here is that the Swift 4 names of imported
types become usable in Swift 3 mode without any diagnostics, similar
to how most language features introduced in Swift 4 are available in
Swift 3 mode. It also implies that the Swift 4 name will show up in
demangled names.

rdar://problem/31616162
This commit is contained in:
Jordan Rose
2017-04-24 16:39:52 -07:00
parent 90647e4cdd
commit cb9b9ea734
8 changed files with 132 additions and 33 deletions

View File

@@ -2953,7 +2953,7 @@ void ClangImporter::Implementation::lookupValue(
auto alternateNamedDecl =
cast_or_null<ValueDecl>(importDeclReal(recentClangDecl,
nameVersion));
if (!alternateNamedDecl)
if (!alternateNamedDecl || alternateNamedDecl == decl)
return;
assert(alternateNamedDecl->getFullName().matchesRef(name) &&
"importFullName behaved differently from importDecl");

View File

@@ -1893,28 +1893,54 @@ namespace {
/// Note: Use this rather than calling Impl.importFullName directly!
ImportedName importFullName(const clang::NamedDecl *D,
Optional<ImportedName> &correctSwiftName) {
if (isActiveSwiftVersion()) {
// Just import the current Swift name.
correctSwiftName = None;
return Impl.importFullName(D, getVersion());
ImportNameVersion canonicalVersion = getActiveSwiftVersion();
if (isa<clang::TypeDecl>(D) || isa<clang::ObjCContainerDecl>(D)) {
canonicalVersion = ImportNameVersion::ForTypes;
}
correctSwiftName = None;
// First, import based on the Swift name of the canonical declaration:
// the latest version for types and the current version for non-type
// values. If that fails, we won't do anything.
auto canonicalName = Impl.importFullName(D, canonicalVersion);
if (!canonicalName)
return ImportedName();
if (getVersion() == canonicalVersion) {
// Make sure we don't try to import the same type twice as canonical.
if (canonicalVersion != getActiveSwiftVersion()) {
auto activeName = Impl.importFullName(D, getActiveSwiftVersion());
if (activeName &&
activeName.getDeclName() == canonicalName.getDeclName()) {
return ImportedName();
}
}
return canonicalName;
}
// Special handling when we import using the older Swift name.
//
// First, import based on the current Swift name. If that fails, we won't
// do anything.
correctSwiftName = Impl.importFullName(D, getActiveSwiftVersion());
if (!*correctSwiftName)
return {};
// Import using the alternate Swift name. If that fails, or if it's
// identical to the active Swift name, we won't introduce an alternate
// Swift name stub declaration.
auto alternateName = Impl.importFullName(D, getVersion());
if (!alternateName || alternateName.getDeclName() == correctSwiftName->getDeclName())
if (!alternateName)
return ImportedName();
// Okay, return the alternate Swift name.
if (alternateName.getDeclName() == canonicalName.getDeclName()) {
if (getVersion() == getActiveSwiftVersion()) {
assert(canonicalVersion != getActiveSwiftVersion());
return alternateName;
}
return ImportedName();
}
// Always use the active version as the preferred name, even if the
// canonical name is a different version.
correctSwiftName = Impl.importFullName(D, getActiveSwiftVersion());
assert(correctSwiftName);
return alternateName;
}
@@ -2035,6 +2061,11 @@ namespace {
/// Mark the given declaration as an older Swift version variant of the
/// current name.
void markAsVariant(Decl *decl, ImportedName correctSwiftName) {
// Types always import using the latest version. Make sure all names up
// to that version are considered available.
if (isa<TypeDecl>(decl) && getVersion() >= getActiveSwiftVersion())
return;
// TODO: some versions should be deprecated instead of unavailable
ASTContext &ctx = decl->getASTContext();
@@ -3853,7 +3884,7 @@ namespace {
return nullptr;
// Find the Swift class being extended.
auto objcClass = cast_or_null<ClassDecl>(
auto objcClass = castIgnoringCompatibilityAlias<ClassDecl>(
Impl.importDecl(decl->getClassInterface(), getActiveSwiftVersion()));
if (!objcClass)
return nullptr;
@@ -4669,7 +4700,7 @@ SwiftDeclConverter::importCFClassType(const clang::TypedefNameDecl *decl,
if (auto attr = record->getAttr<clang::ObjCBridgeAttr>()) {
// Record the Objective-C class to which this CF type is toll-free
// bridged.
if (ClassDecl *objcClass = dyn_cast_or_null<ClassDecl>(
if (ClassDecl *objcClass = dynCastIgnoringCompatibilityAlias<ClassDecl>(
Impl.importDeclByName(attr->getBridgedType()->getName()))) {
theClass->getAttrs().add(new (Impl.SwiftContext)
ObjCBridgedAttr(objcClass));
@@ -4679,7 +4710,7 @@ SwiftDeclConverter::importCFClassType(const clang::TypedefNameDecl *decl,
if (auto attr = record->getAttr<clang::ObjCBridgeMutableAttr>()) {
// Record the Objective-C class to which this CF type is toll-free
// bridged.
if (ClassDecl *objcClass = dyn_cast_or_null<ClassDecl>(
if (ClassDecl *objcClass = dynCastIgnoringCompatibilityAlias<ClassDecl>(
Impl.importDeclByName(attr->getBridgedType()->getName()))) {
theClass->getAttrs().add(new (Impl.SwiftContext)
ObjCBridgedAttr(objcClass));
@@ -4696,7 +4727,11 @@ Decl *SwiftDeclConverter::importCompatibilityTypeAlias(
ImportedName correctSwiftName) {
// Import the referenced declaration. If it doesn't come in as a type,
// we don't care.
auto importedDecl = Impl.importDecl(decl, getActiveSwiftVersion());
Decl *importedDecl = nullptr;
if (getVersion() >= getActiveSwiftVersion())
importedDecl = Impl.importDecl(decl, ImportNameVersion::ForTypes);
if (!importedDecl)
importedDecl = Impl.importDecl(decl, getActiveSwiftVersion());
auto typeDecl = dyn_cast_or_null<TypeDecl>(importedDecl);
if (!typeDecl)
return nullptr;
@@ -6193,7 +6228,7 @@ void SwiftDeclConverter::importObjCProtocols(
for (auto cp = clangProtocols.begin(), cpEnd = clangProtocols.end();
cp != cpEnd; ++cp) {
if (auto proto = cast_or_null<ProtocolDecl>(
if (auto proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
Impl.importDecl(*cp, getActiveSwiftVersion()))) {
addProtocols(proto, protocols, knownProtocols);
inheritedTypes.push_back(TypeLoc::withoutLoc(proto->getDeclaredType()));
@@ -6275,7 +6310,7 @@ Optional<GenericParamList *> SwiftDeclConverter::importObjCGenericParams(
inherited.push_back(TypeLoc::withoutLoc(superclassType));
}
for (clang::ObjCProtocolDecl *clangProto : clangBound->quals()) {
ProtocolDecl *proto = cast_or_null<ProtocolDecl>(
ProtocolDecl *proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
Impl.importDecl(clangProto, getActiveSwiftVersion()));
if (!proto) {
return None;
@@ -6915,7 +6950,7 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
if (hasMissingRequiredMember) {
// Mark the protocol as having missing requirements.
if (auto proto = cast_or_null<ProtocolDecl>(
if (auto proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
importDecl(clangProto, CurrentVersion))) {
proto->setHasMissingRequirements(true);
}
@@ -6929,7 +6964,7 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
// Only allow this to affect declarations in the same top-level module
// as the original class.
if (getClangModuleForDecl(theClass) == getClangModuleForDecl(method)) {
if (auto swiftClass = cast_or_null<ClassDecl>(
if (auto swiftClass = castIgnoringCompatibilityAlias<ClassDecl>(
importDecl(theClass, CurrentVersion))) {
swiftClass->setHasMissingDesignatedInitializers();
}

View File

@@ -54,7 +54,14 @@ enum class ImportNameVersion : unsigned {
Swift4,
/// A placeholder for the latest version, to be used in loops and such.
LAST_VERSION = Swift4
LAST_VERSION = Swift4,
/// The version which should be used for importing types, which need to have
/// one canonical definition.
///
/// FIXME: Is this supposed to be the /newest/ version, or a canonical
/// version that lasts forever as part of the ABI?
ForTypes = Swift4
};
static inline ImportNameVersion &operator++(ImportNameVersion &value) {

View File

@@ -576,7 +576,7 @@ namespace {
genericEnv = ext->getGenericEnvironment();
} else if (auto *interface =
dyn_cast<clang::ObjCInterfaceDecl>(typeParamContext)) {
auto cls = cast_or_null<ClassDecl>(
auto cls = castIgnoringCompatibilityAlias<ClassDecl>(
Impl.importDecl(interface, Impl.CurrentVersion));
if (!cls)
return ImportResult();
@@ -853,7 +853,7 @@ namespace {
// If this object pointer refers to an Objective-C class (possibly
// qualified),
if (auto objcClass = type->getInterfaceDecl()) {
auto imported = cast_or_null<ClassDecl>(
auto imported = castIgnoringCompatibilityAlias<ClassDecl>(
Impl.importDecl(objcClass, Impl.CurrentVersion));
if (!imported)
return nullptr;
@@ -1038,7 +1038,7 @@ namespace {
for (auto cp = type->qual_begin(), cpEnd = type->qual_end();
cp != cpEnd; ++cp) {
auto proto = cast_or_null<ProtocolDecl>(
auto proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
Impl.importDecl(*cp, Impl.CurrentVersion));
if (!proto)
return Type();
@@ -2432,7 +2432,8 @@ static Type getNamedProtocolType(ClangImporter::Implementation &impl,
for (auto decl : lookupResult) {
if (auto swiftDecl =
impl.importDecl(decl->getUnderlyingDecl(), impl.CurrentVersion)) {
if (auto protoDecl = dyn_cast<ProtocolDecl>(swiftDecl)) {
if (auto protoDecl =
dynCastIgnoringCompatibilityAlias<ProtocolDecl>(swiftDecl)) {
return protoDecl->getDeclaredType();
}
}

View File

@@ -1194,6 +1194,32 @@ namespace importer {
/// Whether we should suppress the import of the given Clang declaration.
bool shouldSuppressDeclImport(const clang::Decl *decl);
/// Finds a particular kind of nominal by looking through typealiases.
template <typename T>
static T *dynCastIgnoringCompatibilityAlias(Decl *D) {
static_assert(std::is_base_of<NominalTypeDecl, T>::value,
"only meant for use with NominalTypeDecl and subclasses");
if (auto *alias = dyn_cast_or_null<TypeAliasDecl>(D)) {
if (!alias->isCompatibilityAlias())
return nullptr;
D = alias->getDeclaredInterfaceType()->getAnyNominal();
}
return dyn_cast_or_null<T>(D);
}
/// Finds a particular kind of nominal by looking through typealiases.
template <typename T>
static T *castIgnoringCompatibilityAlias(Decl *D) {
static_assert(std::is_base_of<NominalTypeDecl, T>::value,
"only meant for use with NominalTypeDecl and subclasses");
if (auto *alias = dyn_cast_or_null<TypeAliasDecl>(D)) {
assert(alias->isCompatibilityAlias() &&
"non-compatible typealias found where nominal was expected");
D = alias->getDeclaredInterfaceType()->getAnyNominal();
}
return cast_or_null<T>(D);
}
class SwiftNameLookupExtension : public clang::ModuleFileExtension {
std::unique_ptr<SwiftLookupTable> &pchLookupTable;
LookupTableMap &lookupTables;

View File

@@ -0,0 +1,15 @@
// RUN: rm -rf %t && mkdir -p %t
// Use fake mangled names here with the name of the imported module.
// swift-ide-test isn't testing the full demangling algorithm.
// RUN: %target-swift-ide-test -F %S/Inputs/custom-frameworks -print-ast-typechecked -source-filename %s -swift-version 3 -find-mangled _T0So16ImportantCStructa | %FileCheck -check-prefix=CHECK-TOP-ALIAS %s
// RUN: %target-swift-ide-test -F %S/Inputs/custom-frameworks -print-ast-typechecked -source-filename %s -swift-version 4 -find-mangled _T0So16ImportantCStructa | %FileCheck -check-prefix=CHECK-TOP-ALIAS %s
// RUN: %target-swift-ide-test -F %S/Inputs/custom-frameworks -print-ast-typechecked -source-filename %s -swift-version 3 -find-mangled _T0So13InnerInSwift4a | %FileCheck -check-prefix=CHECK-NESTED-ALIAS %s
// RUN: %target-swift-ide-test -F %S/Inputs/custom-frameworks -print-ast-typechecked -source-filename %s -swift-version 4 -find-mangled _T0So13InnerInSwift4a | %FileCheck -check-prefix=CHECK-NESTED-ALIAS %s
import APINotesFrameworkTest
// CHECK-TOP-ALIAS: typealias ImportantCStruct = VeryImportantCStruct
// CHECK-NESTED-ALIAS: typealias InnerInSwift4 = Outer.Inner

View File

@@ -13,9 +13,13 @@
// RUN: not %target-swift-frontend -typecheck -F %S/Inputs/custom-frameworks -swift-version 4 %s 2>&1 | %FileCheck -check-prefix=CHECK-DIAGS -check-prefix=CHECK-DIAGS-4 %s
// RUN: not %target-swift-frontend -typecheck -F %S/Inputs/custom-frameworks -swift-version 3 %s 2>&1 | %FileCheck -check-prefix=CHECK-DIAGS -check-prefix=CHECK-DIAGS-3 %s
// RUN: %target-swift-frontend -emit-silgen -F %S/Inputs/custom-frameworks -swift-version 3 %s -DSILGEN 2>&1 | %FileCheck -check-prefix=CHECK-SILGEN -check-prefix=CHECK-SILGEN-3 %s
// RUN: %target-swift-frontend -emit-silgen -F %S/Inputs/custom-frameworks -swift-version 4 %s -DSILGEN 2>&1 | %FileCheck -check-prefix=CHECK-SILGEN -check-prefix=CHECK-SILGEN-4 %s
import APINotesFrameworkTest
func testRenamedTopLevel() {
#if !SILGEN
func testRenamedTopLevelDiags() {
var value = 0.0
// CHECK-DIAGS-4-NOT: versioned.swift:[[@LINE+1]]:
@@ -53,8 +57,7 @@ func testRenamedTopLevel() {
// CHECK-DIAGS-4-NOT: versioned.swift:[[@LINE+1]]:
_ = VeryImportantCStruct()
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:7: error: 'VeryImportantCStruct' has been renamed to 'ImportantCStruct'
// CHECK-DIAGS-3: note: 'VeryImportantCStruct' was introduced in Swift 4
// CHECK-DIAGS-3-NOTE: versioned.swift:[[@LINE-1]]:
// CHECK-DIAGS-3-NOT: versioned.swift:[[@LINE+1]]:
_ = InnerInSwift4()
@@ -63,5 +66,18 @@ func testRenamedTopLevel() {
// CHECK-DIAGS-4-NOT: versioned.swift:[[@LINE+1]]:
_ = Outer.Inner()
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:13: error: 'Inner' has been renamed to 'InnerInSwift4'
// CHECK-DIAGS-3-NOT: versioned.swift:[[@LINE-1]]:
}
#endif
#if !swift(>=4)
func useSwift3Name(_: ImportantCStruct) {}
// CHECK-SILGEN-3: sil hidden @_T09versioned13useSwift3NameySo20VeryImportantCStructVF
func useNewlyNested(_: InnerInSwift4) {}
// CHECK-SILGEN-3: sil hidden @_T09versioned14useNewlyNestedySo5OuterV5InnerVF
#endif
func useSwift4Name(_: VeryImportantCStruct) {}
// CHECK-SILGEN: sil hidden @_T09versioned13useSwift4NameySo20VeryImportantCStructVF

View File

@@ -68,9 +68,8 @@
// PRINT-APINOTES-3-NEXT: init(oldLabel _: Int32)
// PRINT-APINOTES-3-NEXT: @available(swift, introduced: 4, renamed: "Struct1.init(oldLabel:)")
// PRINT-APINOTES-3-NEXT: init(newLabel _: Int32)
// PRINT-APINOTES-3-NEXT: typealias OldApiNoteType = Double
// PRINT-APINOTES-3-NEXT: @available(swift, introduced: 4, renamed: "Struct1.OldApiNoteType")
// PRINT-APINOTES-3-NEXT: typealias NewApiNoteType = Struct1.OldApiNoteType
// PRINT-APINOTES-3-NEXT: typealias OldApiNoteType = Struct1.NewApiNoteType
// PRINT-APINOTES-3-NEXT: typealias NewApiNoteType = Double
// PRINT-APINOTES-3-NEXT: }
// PRINT-APINOTES-3-NOT: @available
// PRINT-APINOTES-3: var IAMStruct1APINoteVarInSwift4: Double