Skip to content

Commit cb9b9ea

Browse files
committed
[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
1 parent 90647e4 commit cb9b9ea

File tree

8 files changed

+132
-33
lines changed

8 files changed

+132
-33
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2953,7 +2953,7 @@ void ClangImporter::Implementation::lookupValue(
29532953
auto alternateNamedDecl =
29542954
cast_or_null<ValueDecl>(importDeclReal(recentClangDecl,
29552955
nameVersion));
2956-
if (!alternateNamedDecl)
2956+
if (!alternateNamedDecl || alternateNamedDecl == decl)
29572957
return;
29582958
assert(alternateNamedDecl->getFullName().matchesRef(name) &&
29592959
"importFullName behaved differently from importDecl");

lib/ClangImporter/ImportDecl.cpp

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,28 +1893,54 @@ namespace {
18931893
/// Note: Use this rather than calling Impl.importFullName directly!
18941894
ImportedName importFullName(const clang::NamedDecl *D,
18951895
Optional<ImportedName> &correctSwiftName) {
1896-
if (isActiveSwiftVersion()) {
1897-
// Just import the current Swift name.
1898-
correctSwiftName = None;
1899-
return Impl.importFullName(D, getVersion());
1896+
ImportNameVersion canonicalVersion = getActiveSwiftVersion();
1897+
if (isa<clang::TypeDecl>(D) || isa<clang::ObjCContainerDecl>(D)) {
1898+
canonicalVersion = ImportNameVersion::ForTypes;
1899+
}
1900+
correctSwiftName = None;
1901+
1902+
// First, import based on the Swift name of the canonical declaration:
1903+
// the latest version for types and the current version for non-type
1904+
// values. If that fails, we won't do anything.
1905+
auto canonicalName = Impl.importFullName(D, canonicalVersion);
1906+
if (!canonicalName)
1907+
return ImportedName();
1908+
1909+
if (getVersion() == canonicalVersion) {
1910+
// Make sure we don't try to import the same type twice as canonical.
1911+
if (canonicalVersion != getActiveSwiftVersion()) {
1912+
auto activeName = Impl.importFullName(D, getActiveSwiftVersion());
1913+
if (activeName &&
1914+
activeName.getDeclName() == canonicalName.getDeclName()) {
1915+
return ImportedName();
1916+
}
1917+
}
1918+
1919+
return canonicalName;
19001920
}
19011921

19021922
// Special handling when we import using the older Swift name.
19031923
//
1904-
// First, import based on the current Swift name. If that fails, we won't
1905-
// do anything.
1906-
correctSwiftName = Impl.importFullName(D, getActiveSwiftVersion());
1907-
if (!*correctSwiftName)
1908-
return {};
1909-
19101924
// Import using the alternate Swift name. If that fails, or if it's
19111925
// identical to the active Swift name, we won't introduce an alternate
19121926
// Swift name stub declaration.
19131927
auto alternateName = Impl.importFullName(D, getVersion());
1914-
if (!alternateName || alternateName.getDeclName() == correctSwiftName->getDeclName())
1928+
if (!alternateName)
19151929
return ImportedName();
19161930

1917-
// Okay, return the alternate Swift name.
1931+
if (alternateName.getDeclName() == canonicalName.getDeclName()) {
1932+
if (getVersion() == getActiveSwiftVersion()) {
1933+
assert(canonicalVersion != getActiveSwiftVersion());
1934+
return alternateName;
1935+
}
1936+
return ImportedName();
1937+
}
1938+
1939+
// Always use the active version as the preferred name, even if the
1940+
// canonical name is a different version.
1941+
correctSwiftName = Impl.importFullName(D, getActiveSwiftVersion());
1942+
assert(correctSwiftName);
1943+
19181944
return alternateName;
19191945
}
19201946

@@ -2035,6 +2061,11 @@ namespace {
20352061
/// Mark the given declaration as an older Swift version variant of the
20362062
/// current name.
20372063
void markAsVariant(Decl *decl, ImportedName correctSwiftName) {
2064+
// Types always import using the latest version. Make sure all names up
2065+
// to that version are considered available.
2066+
if (isa<TypeDecl>(decl) && getVersion() >= getActiveSwiftVersion())
2067+
return;
2068+
20382069
// TODO: some versions should be deprecated instead of unavailable
20392070

20402071
ASTContext &ctx = decl->getASTContext();
@@ -3853,7 +3884,7 @@ namespace {
38533884
return nullptr;
38543885

38553886
// Find the Swift class being extended.
3856-
auto objcClass = cast_or_null<ClassDecl>(
3887+
auto objcClass = castIgnoringCompatibilityAlias<ClassDecl>(
38573888
Impl.importDecl(decl->getClassInterface(), getActiveSwiftVersion()));
38583889
if (!objcClass)
38593890
return nullptr;
@@ -4669,7 +4700,7 @@ SwiftDeclConverter::importCFClassType(const clang::TypedefNameDecl *decl,
46694700
if (auto attr = record->getAttr<clang::ObjCBridgeAttr>()) {
46704701
// Record the Objective-C class to which this CF type is toll-free
46714702
// bridged.
4672-
if (ClassDecl *objcClass = dyn_cast_or_null<ClassDecl>(
4703+
if (ClassDecl *objcClass = dynCastIgnoringCompatibilityAlias<ClassDecl>(
46734704
Impl.importDeclByName(attr->getBridgedType()->getName()))) {
46744705
theClass->getAttrs().add(new (Impl.SwiftContext)
46754706
ObjCBridgedAttr(objcClass));
@@ -4679,7 +4710,7 @@ SwiftDeclConverter::importCFClassType(const clang::TypedefNameDecl *decl,
46794710
if (auto attr = record->getAttr<clang::ObjCBridgeMutableAttr>()) {
46804711
// Record the Objective-C class to which this CF type is toll-free
46814712
// bridged.
4682-
if (ClassDecl *objcClass = dyn_cast_or_null<ClassDecl>(
4713+
if (ClassDecl *objcClass = dynCastIgnoringCompatibilityAlias<ClassDecl>(
46834714
Impl.importDeclByName(attr->getBridgedType()->getName()))) {
46844715
theClass->getAttrs().add(new (Impl.SwiftContext)
46854716
ObjCBridgedAttr(objcClass));
@@ -4696,7 +4727,11 @@ Decl *SwiftDeclConverter::importCompatibilityTypeAlias(
46964727
ImportedName correctSwiftName) {
46974728
// Import the referenced declaration. If it doesn't come in as a type,
46984729
// we don't care.
4699-
auto importedDecl = Impl.importDecl(decl, getActiveSwiftVersion());
4730+
Decl *importedDecl = nullptr;
4731+
if (getVersion() >= getActiveSwiftVersion())
4732+
importedDecl = Impl.importDecl(decl, ImportNameVersion::ForTypes);
4733+
if (!importedDecl)
4734+
importedDecl = Impl.importDecl(decl, getActiveSwiftVersion());
47004735
auto typeDecl = dyn_cast_or_null<TypeDecl>(importedDecl);
47014736
if (!typeDecl)
47024737
return nullptr;
@@ -6193,7 +6228,7 @@ void SwiftDeclConverter::importObjCProtocols(
61936228

61946229
for (auto cp = clangProtocols.begin(), cpEnd = clangProtocols.end();
61956230
cp != cpEnd; ++cp) {
6196-
if (auto proto = cast_or_null<ProtocolDecl>(
6231+
if (auto proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
61976232
Impl.importDecl(*cp, getActiveSwiftVersion()))) {
61986233
addProtocols(proto, protocols, knownProtocols);
61996234
inheritedTypes.push_back(TypeLoc::withoutLoc(proto->getDeclaredType()));
@@ -6275,7 +6310,7 @@ Optional<GenericParamList *> SwiftDeclConverter::importObjCGenericParams(
62756310
inherited.push_back(TypeLoc::withoutLoc(superclassType));
62766311
}
62776312
for (clang::ObjCProtocolDecl *clangProto : clangBound->quals()) {
6278-
ProtocolDecl *proto = cast_or_null<ProtocolDecl>(
6313+
ProtocolDecl *proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
62796314
Impl.importDecl(clangProto, getActiveSwiftVersion()));
62806315
if (!proto) {
62816316
return None;
@@ -6915,7 +6950,7 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
69156950

69166951
if (hasMissingRequiredMember) {
69176952
// Mark the protocol as having missing requirements.
6918-
if (auto proto = cast_or_null<ProtocolDecl>(
6953+
if (auto proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
69196954
importDecl(clangProto, CurrentVersion))) {
69206955
proto->setHasMissingRequirements(true);
69216956
}
@@ -6929,7 +6964,7 @@ ClangImporter::Implementation::importDeclImpl(const clang::NamedDecl *ClangDecl,
69296964
// Only allow this to affect declarations in the same top-level module
69306965
// as the original class.
69316966
if (getClangModuleForDecl(theClass) == getClangModuleForDecl(method)) {
6932-
if (auto swiftClass = cast_or_null<ClassDecl>(
6967+
if (auto swiftClass = castIgnoringCompatibilityAlias<ClassDecl>(
69336968
importDecl(theClass, CurrentVersion))) {
69346969
swiftClass->setHasMissingDesignatedInitializers();
69356970
}

lib/ClangImporter/ImportName.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,14 @@ enum class ImportNameVersion : unsigned {
5454
Swift4,
5555

5656
/// A placeholder for the latest version, to be used in loops and such.
57-
LAST_VERSION = Swift4
57+
LAST_VERSION = Swift4,
58+
59+
/// The version which should be used for importing types, which need to have
60+
/// one canonical definition.
61+
///
62+
/// FIXME: Is this supposed to be the /newest/ version, or a canonical
63+
/// version that lasts forever as part of the ABI?
64+
ForTypes = Swift4
5865
};
5966

6067
static inline ImportNameVersion &operator++(ImportNameVersion &value) {

lib/ClangImporter/ImportType.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ namespace {
576576
genericEnv = ext->getGenericEnvironment();
577577
} else if (auto *interface =
578578
dyn_cast<clang::ObjCInterfaceDecl>(typeParamContext)) {
579-
auto cls = cast_or_null<ClassDecl>(
579+
auto cls = castIgnoringCompatibilityAlias<ClassDecl>(
580580
Impl.importDecl(interface, Impl.CurrentVersion));
581581
if (!cls)
582582
return ImportResult();
@@ -853,7 +853,7 @@ namespace {
853853
// If this object pointer refers to an Objective-C class (possibly
854854
// qualified),
855855
if (auto objcClass = type->getInterfaceDecl()) {
856-
auto imported = cast_or_null<ClassDecl>(
856+
auto imported = castIgnoringCompatibilityAlias<ClassDecl>(
857857
Impl.importDecl(objcClass, Impl.CurrentVersion));
858858
if (!imported)
859859
return nullptr;
@@ -1038,7 +1038,7 @@ namespace {
10381038

10391039
for (auto cp = type->qual_begin(), cpEnd = type->qual_end();
10401040
cp != cpEnd; ++cp) {
1041-
auto proto = cast_or_null<ProtocolDecl>(
1041+
auto proto = castIgnoringCompatibilityAlias<ProtocolDecl>(
10421042
Impl.importDecl(*cp, Impl.CurrentVersion));
10431043
if (!proto)
10441044
return Type();
@@ -2432,7 +2432,8 @@ static Type getNamedProtocolType(ClangImporter::Implementation &impl,
24322432
for (auto decl : lookupResult) {
24332433
if (auto swiftDecl =
24342434
impl.importDecl(decl->getUnderlyingDecl(), impl.CurrentVersion)) {
2435-
if (auto protoDecl = dyn_cast<ProtocolDecl>(swiftDecl)) {
2435+
if (auto protoDecl =
2436+
dynCastIgnoringCompatibilityAlias<ProtocolDecl>(swiftDecl)) {
24362437
return protoDecl->getDeclaredType();
24372438
}
24382439
}

lib/ClangImporter/ImporterImpl.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,32 @@ namespace importer {
11941194
/// Whether we should suppress the import of the given Clang declaration.
11951195
bool shouldSuppressDeclImport(const clang::Decl *decl);
11961196

1197+
/// Finds a particular kind of nominal by looking through typealiases.
1198+
template <typename T>
1199+
static T *dynCastIgnoringCompatibilityAlias(Decl *D) {
1200+
static_assert(std::is_base_of<NominalTypeDecl, T>::value,
1201+
"only meant for use with NominalTypeDecl and subclasses");
1202+
if (auto *alias = dyn_cast_or_null<TypeAliasDecl>(D)) {
1203+
if (!alias->isCompatibilityAlias())
1204+
return nullptr;
1205+
D = alias->getDeclaredInterfaceType()->getAnyNominal();
1206+
}
1207+
return dyn_cast_or_null<T>(D);
1208+
}
1209+
1210+
/// Finds a particular kind of nominal by looking through typealiases.
1211+
template <typename T>
1212+
static T *castIgnoringCompatibilityAlias(Decl *D) {
1213+
static_assert(std::is_base_of<NominalTypeDecl, T>::value,
1214+
"only meant for use with NominalTypeDecl and subclasses");
1215+
if (auto *alias = dyn_cast_or_null<TypeAliasDecl>(D)) {
1216+
assert(alias->isCompatibilityAlias() &&
1217+
"non-compatible typealias found where nominal was expected");
1218+
D = alias->getDeclaredInterfaceType()->getAnyNominal();
1219+
}
1220+
return cast_or_null<T>(D);
1221+
}
1222+
11971223
class SwiftNameLookupExtension : public clang::ModuleFileExtension {
11981224
std::unique_ptr<SwiftLookupTable> &pchLookupTable;
11991225
LookupTableMap &lookupTables;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// RUN: rm -rf %t && mkdir -p %t
2+
3+
// Use fake mangled names here with the name of the imported module.
4+
// swift-ide-test isn't testing the full demangling algorithm.
5+
6+
// 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
7+
// 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
8+
9+
// 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
10+
// 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
11+
12+
import APINotesFrameworkTest
13+
14+
// CHECK-TOP-ALIAS: typealias ImportantCStruct = VeryImportantCStruct
15+
// CHECK-NESTED-ALIAS: typealias InnerInSwift4 = Outer.Inner

test/APINotes/versioned.swift

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@
1313
// 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
1414
// 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
1515

16+
// 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
17+
// 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
18+
1619
import APINotesFrameworkTest
1720

18-
func testRenamedTopLevel() {
21+
#if !SILGEN
22+
func testRenamedTopLevelDiags() {
1923
var value = 0.0
2024

2125
// CHECK-DIAGS-4-NOT: versioned.swift:[[@LINE+1]]:
@@ -53,8 +57,7 @@ func testRenamedTopLevel() {
5357

5458
// CHECK-DIAGS-4-NOT: versioned.swift:[[@LINE+1]]:
5559
_ = VeryImportantCStruct()
56-
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:7: error: 'VeryImportantCStruct' has been renamed to 'ImportantCStruct'
57-
// CHECK-DIAGS-3: note: 'VeryImportantCStruct' was introduced in Swift 4
60+
// CHECK-DIAGS-3-NOTE: versioned.swift:[[@LINE-1]]:
5861

5962
// CHECK-DIAGS-3-NOT: versioned.swift:[[@LINE+1]]:
6063
_ = InnerInSwift4()
@@ -63,5 +66,18 @@ func testRenamedTopLevel() {
6366

6467
// CHECK-DIAGS-4-NOT: versioned.swift:[[@LINE+1]]:
6568
_ = Outer.Inner()
66-
// CHECK-DIAGS-3: versioned.swift:[[@LINE-1]]:13: error: 'Inner' has been renamed to 'InnerInSwift4'
69+
// CHECK-DIAGS-3-NOT: versioned.swift:[[@LINE-1]]:
6770
}
71+
#endif
72+
73+
#if !swift(>=4)
74+
func useSwift3Name(_: ImportantCStruct) {}
75+
// CHECK-SILGEN-3: sil hidden @_T09versioned13useSwift3NameySo20VeryImportantCStructVF
76+
77+
func useNewlyNested(_: InnerInSwift4) {}
78+
// CHECK-SILGEN-3: sil hidden @_T09versioned14useNewlyNestedySo5OuterV5InnerVF
79+
#endif
80+
81+
func useSwift4Name(_: VeryImportantCStruct) {}
82+
// CHECK-SILGEN: sil hidden @_T09versioned13useSwift4NameySo20VeryImportantCStructVF
83+

test/IDE/import_as_member.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,8 @@
6868
// PRINT-APINOTES-3-NEXT: init(oldLabel _: Int32)
6969
// PRINT-APINOTES-3-NEXT: @available(swift, introduced: 4, renamed: "Struct1.init(oldLabel:)")
7070
// PRINT-APINOTES-3-NEXT: init(newLabel _: Int32)
71-
// PRINT-APINOTES-3-NEXT: typealias OldApiNoteType = Double
72-
// PRINT-APINOTES-3-NEXT: @available(swift, introduced: 4, renamed: "Struct1.OldApiNoteType")
73-
// PRINT-APINOTES-3-NEXT: typealias NewApiNoteType = Struct1.OldApiNoteType
71+
// PRINT-APINOTES-3-NEXT: typealias OldApiNoteType = Struct1.NewApiNoteType
72+
// PRINT-APINOTES-3-NEXT: typealias NewApiNoteType = Double
7473
// PRINT-APINOTES-3-NEXT: }
7574
// PRINT-APINOTES-3-NOT: @available
7675
// PRINT-APINOTES-3: var IAMStruct1APINoteVarInSwift4: Double

0 commit comments

Comments
 (0)