Skip to content

Commit 726b2c7

Browse files
authored
Make all CF types Equatable and Hashable. (#4417)
Like NSObject, CFType has primitive operations CFEqual and CFHash, so Swift should allow those types to show up in Hashable positions (like dictionaries). The most general way to do this was to introduce a new protocol, _CFObject, and then have the importer automatically make all CF types conform to it. This did require one additional change: the == implementation that calls through to CFEqual is in a new CoreFoundation overlay, but the conformance is in the underlying Clang module. Therefore, operator lookup for conformances has been changed to look in the overlay for an imported declaration (if there is one). https://bugs.swift.org/browse/SR-2388 (cherry picked from commit 361ab62)
1 parent fe392de commit 726b2c7

File tree

19 files changed

+321
-83
lines changed

19 files changed

+321
-83
lines changed

include/swift/AST/KnownIdentifiers.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ IDENTIFIER(atIndexedSubscript)
3030
IDENTIFIER_(bridgeToObjectiveC)
3131
IDENTIFIER_WITH_NAME(code_, "_code")
3232
IDENTIFIER(CGFloat)
33+
IDENTIFIER(CoreFoundation)
3334
IDENTIFIER(CVarArg)
3435
IDENTIFIER(Darwin)
3536
IDENTIFIER(dealloc)

include/swift/AST/KnownProtocols.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,14 @@ PROTOCOL(Comparable)
5858
PROTOCOL(Error)
5959
PROTOCOL_(ErrorCodeProtocol)
6060
PROTOCOL(OptionSet)
61+
6162
PROTOCOL_(BridgedNSError)
6263
PROTOCOL_(BridgedStoredNSError)
64+
PROTOCOL_(CFObject)
65+
PROTOCOL_(SwiftNewtypeWrapper)
6366

6467
PROTOCOL_(ObjectiveCBridgeable)
6568
PROTOCOL_(DestructorSafeContainer)
66-
PROTOCOL_(SwiftNewtypeWrapper)
6769

6870
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByArrayLiteral)
6971
EXPRESSIBLE_BY_LITERAL_PROTOCOL(ExpressibleByBooleanLiteral)

lib/AST/ASTContext.cpp

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -927,23 +927,26 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const {
927927
// Find all of the declarations with this name in the appropriate module.
928928
SmallVector<ValueDecl *, 1> results;
929929

930-
// _BridgedNSError, _BridgedStoredNSError, and _ErrorCodeProtocol
931-
// are in the Foundation module.
932-
if (kind == KnownProtocolKind::BridgedNSError ||
933-
kind == KnownProtocolKind::BridgedStoredNSError ||
934-
kind == KnownProtocolKind::ErrorCodeProtocol) {
935-
Module *foundation =
936-
const_cast<ASTContext *>(this)->getLoadedModule(Id_Foundation);
937-
if (!foundation)
938-
return nullptr;
939-
940-
auto identifier = getIdentifier(getProtocolName(kind));
941-
foundation->lookupValue({ }, identifier, NLKind::UnqualifiedLookup,
942-
results);
943-
} else {
944-
lookupInSwiftModule(getProtocolName(kind), results);
930+
const Module *M;
931+
switch (kind) {
932+
case KnownProtocolKind::BridgedNSError:
933+
case KnownProtocolKind::BridgedStoredNSError:
934+
case KnownProtocolKind::ErrorCodeProtocol:
935+
M = getLoadedModule(Id_Foundation);
936+
break;
937+
case KnownProtocolKind::CFObject:
938+
M = getLoadedModule(Id_CoreFoundation);
939+
break;
940+
default:
941+
M = getStdlibModule();
942+
break;
945943
}
946944

945+
if (!M)
946+
return nullptr;
947+
M->lookupValue({ }, getIdentifier(getProtocolName(kind)),
948+
NLKind::UnqualifiedLookup, results);
949+
947950
for (auto result : results) {
948951
if (auto protocol = dyn_cast<ProtocolDecl>(result)) {
949952
Impl.KnownProtocols[index] = protocol;

lib/ClangImporter/ImportDecl.cpp

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1279,9 +1279,14 @@ namespace {
12791279
addObjCAttribute(theClass, None);
12801280
Impl.registerExternalDecl(theClass);
12811281

1282-
SmallVector<ProtocolDecl *, 4> protocols;
1283-
theClass->getImplicitProtocols(protocols);
1284-
addObjCProtocolConformances(theClass, protocols);
1282+
auto *cfObjectProto =
1283+
Impl.SwiftContext.getProtocol(KnownProtocolKind::CFObject);
1284+
if (cfObjectProto) {
1285+
populateInheritedTypes(theClass, cfObjectProto, superclass);
1286+
auto *attr = new (Impl.SwiftContext) SynthesizedProtocolAttr(
1287+
KnownProtocolKind::CFObject);
1288+
theClass->getAttrs().add(attr);
1289+
}
12851290

12861291
// Look for bridging attributes on the clang record. We can
12871292
// just check the most recent redeclaration, which will inherit
@@ -2209,11 +2214,14 @@ namespace {
22092214
}
22102215

22112216
void populateInheritedTypes(NominalTypeDecl *nominal,
2212-
ArrayRef<ProtocolDecl *> protocols) {
2217+
ArrayRef<ProtocolDecl *> protocols,
2218+
Type superclass = Type()) {
22132219
SmallVector<TypeLoc, 4> inheritedTypes;
2214-
inheritedTypes.resize(protocols.size());
2215-
for_each(MutableArrayRef<TypeLoc>(inheritedTypes),
2216-
ArrayRef<ProtocolDecl *>(protocols),
2220+
inheritedTypes.resize(protocols.size() + (superclass ? 1 : 0));
2221+
if (superclass)
2222+
inheritedTypes[0] = TypeLoc::withoutLoc(superclass);
2223+
for_each(MutableArrayRef<TypeLoc>(inheritedTypes).slice(superclass?1:0),
2224+
protocols,
22172225
[](TypeLoc &tl, ProtocolDecl *proto) {
22182226
tl = TypeLoc::withoutLoc(proto->getDeclaredType());
22192227
});
@@ -4995,7 +5003,7 @@ namespace {
49955003
/// given vector, guarded by the known set of protocols.
49965004
void addProtocols(ProtocolDecl *protocol,
49975005
SmallVectorImpl<ProtocolDecl *> &protocols,
4998-
llvm::SmallPtrSet<ProtocolDecl *, 4> &known) {
5006+
llvm::SmallPtrSetImpl<ProtocolDecl *> &known) {
49995007
if (!known.insert(protocol).second)
50005008
return;
50015009

@@ -5005,6 +5013,13 @@ namespace {
50055013
addProtocols(inherited, protocols, known);
50065014
}
50075015

5016+
void addProtocols(ProtocolDecl *protocol,
5017+
SmallVectorImpl<ProtocolDecl *> &protocols) {
5018+
llvm::SmallPtrSet<ProtocolDecl *, 4> known(protocols.begin(),
5019+
protocols.end());
5020+
addProtocols(protocol, protocols, known);
5021+
}
5022+
50085023
// Import the given Objective-C protocol list, along with any
50095024
// implicitly-provided protocols, and attach them to the given
50105025
// declaration.
@@ -5013,16 +5028,14 @@ namespace {
50135028
SmallVectorImpl<TypeLoc> &inheritedTypes) {
50145029
SmallVector<ProtocolDecl *, 4> protocols;
50155030
llvm::SmallPtrSet<ProtocolDecl *, 4> knownProtocols;
5016-
if (auto nominal = dyn_cast<NominalTypeDecl>(decl)) {
5031+
if (auto nominal = dyn_cast<NominalTypeDecl>(decl))
50175032
nominal->getImplicitProtocols(protocols);
5018-
knownProtocols.insert(protocols.begin(), protocols.end());
5019-
}
50205033

50215034
for (auto cp = clangProtocols.begin(), cpEnd = clangProtocols.end();
50225035
cp != cpEnd; ++cp) {
50235036
if (auto proto = cast_or_null<ProtocolDecl>(Impl.importDecl(*cp,
50245037
false))) {
5025-
addProtocols(proto, protocols, knownProtocols);
5038+
addProtocols(proto, protocols);
50265039
inheritedTypes.push_back(
50275040
TypeLoc::withoutLoc(proto->getDeclaredType()));
50285041
}

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5541,6 +5541,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
55415541
case KnownProtocolKind::OptionSet:
55425542
case KnownProtocolKind::BridgedNSError:
55435543
case KnownProtocolKind::BridgedStoredNSError:
5544+
case KnownProtocolKind::CFObject:
55445545
case KnownProtocolKind::ErrorCodeProtocol:
55455546
return SpecialProtocol::None;
55465547
}

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 77 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "swift/AST/TypeMatcher.h"
3030
#include "swift/AST/TypeWalker.h"
3131
#include "swift/Basic/Defer.h"
32+
#include "swift/ClangImporter/ClangModule.h"
3233
#include "swift/Sema/IDETypeChecking.h"
3334
#include "llvm/ADT/ScopedHashTable.h"
3435
#include "llvm/ADT/SmallString.h"
@@ -1135,39 +1136,87 @@ bool WitnessChecker::findBestWitness(ValueDecl *requirement,
11351136
unsigned &numViable,
11361137
unsigned &bestIdx,
11371138
bool &doNotDiagnoseMatches) {
1138-
auto witnesses = lookupValueWitnesses(requirement, ignoringNames);
1139-
1140-
// Match each of the witnesses to the requirement.
1141-
bool anyFromUnconstrainedExtension = false;
1142-
numViable = 0;
1143-
bestIdx = 0;
1144-
1145-
for (auto witness : witnesses) {
1146-
// Don't match anything in a protocol.
1147-
// FIXME: When default implementations come along, we can try to match
1148-
// these when they're default implementations coming from another
1149-
// (unrelated) protocol.
1150-
if (isa<ProtocolDecl>(witness->getDeclContext())) {
1151-
continue;
1152-
}
1139+
enum Attempt {
1140+
Regular,
1141+
OperatorsFromOverlay,
1142+
Done
1143+
};
11531144

1154-
if (!witness->hasType())
1155-
TC.validateDecl(witness, true);
1145+
SmallVector<ValueDecl *, 4> witnesses;
1146+
bool anyFromUnconstrainedExtension;
11561147

1157-
auto match = matchWitness(TC, Proto, conformance, DC,
1158-
requirement, witness);
1159-
if (match.isViable()) {
1160-
++numViable;
1161-
bestIdx = matches.size();
1162-
} else if (match.Kind == MatchKind::WitnessInvalid) {
1163-
doNotDiagnoseMatches = true;
1148+
for (Attempt attempt = Regular; attempt != Done;
1149+
attempt = static_cast<Attempt>(attempt + 1)) {
1150+
switch (attempt) {
1151+
case Regular:
1152+
witnesses = lookupValueWitnesses(requirement, ignoringNames);
1153+
break;
1154+
case OperatorsFromOverlay: {
1155+
// If we have a Clang declaration, the matching operator might be in the
1156+
// overlay for that module.
1157+
if (!requirement->isOperator())
1158+
continue;
1159+
1160+
auto *clangModule =
1161+
dyn_cast<ClangModuleUnit>(DC->getModuleScopeContext());
1162+
if (!clangModule)
1163+
continue;
1164+
1165+
DeclContext *overlay = clangModule->getAdapterModule();
1166+
if (!overlay)
1167+
continue;
1168+
1169+
auto lookupOptions = defaultUnqualifiedLookupOptions;
1170+
lookupOptions |= NameLookupFlags::KnownPrivate;
1171+
auto lookup = TC.lookupUnqualified(overlay, requirement->getName(),
1172+
SourceLoc(), lookupOptions);
1173+
witnesses.clear();
1174+
for (auto candidate : lookup)
1175+
witnesses.push_back(candidate.Decl);
1176+
break;
11641177
}
1178+
case Done:
1179+
llvm_unreachable("should have exited loop");
1180+
}
1181+
1182+
// Match each of the witnesses to the requirement.
1183+
anyFromUnconstrainedExtension = false;
1184+
numViable = 0;
1185+
bestIdx = 0;
1186+
1187+
for (auto witness : witnesses) {
1188+
// Don't match anything in a protocol.
1189+
// FIXME: When default implementations come along, we can try to match
1190+
// these when they're default implementations coming from another
1191+
// (unrelated) protocol.
1192+
if (isa<ProtocolDecl>(witness->getDeclContext())) {
1193+
continue;
1194+
}
1195+
1196+
if (!witness->hasType())
1197+
TC.validateDecl(witness, true);
11651198

1166-
if (auto *ext = dyn_cast<ExtensionDecl>(match.Witness->getDeclContext()))
1167-
if (!ext->isConstrainedExtension() && ext->getAsProtocolExtensionContext())
1168-
anyFromUnconstrainedExtension = true;
1199+
auto match = matchWitness(TC, Proto, conformance, DC,
1200+
requirement, witness);
1201+
if (match.isViable()) {
1202+
++numViable;
1203+
bestIdx = matches.size();
1204+
} else if (match.Kind == MatchKind::WitnessInvalid) {
1205+
doNotDiagnoseMatches = true;
1206+
}
1207+
1208+
if (auto *ext = dyn_cast<ExtensionDecl>(match.Witness->getDeclContext())){
1209+
if (!ext->isConstrainedExtension() &&
1210+
ext->getAsProtocolExtensionContext()) {
1211+
anyFromUnconstrainedExtension = true;
1212+
}
1213+
}
11691214

1170-
matches.push_back(std::move(match));
1215+
matches.push_back(std::move(match));
1216+
}
1217+
1218+
if (numViable > 0)
1219+
break;
11711220
}
11721221

11731222
if (numViable == 0) {

stdlib/public/SDK/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ add_subdirectory(CloudKit)
1717
add_subdirectory(Contacts)
1818
add_subdirectory(CoreAudio)
1919
add_subdirectory(CoreData)
20+
add_subdirectory(CoreFoundation)
2021
add_subdirectory(CoreGraphics)
2122
add_subdirectory(CoreImage)
2223
add_subdirectory(CoreLocation)

stdlib/public/SDK/CoreAudio/CMakeLists.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ add_swift_library(swiftCoreAudio ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK
33
../../../public/core/WriteBackMutableSlice.swift
44

55
TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR WATCHOS WATCHOS_SIMULATOR
6-
SWIFT_MODULE_DEPENDS Dispatch
6+
SWIFT_MODULE_DEPENDS Dispatch CoreFoundation
77
SWIFT_MODULE_DEPENDS_OSX IOKit
8-
# Also depends on: CoreFoundation
98
FRAMEWORK_DEPENDS CoreAudio)
109

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
add_swift_library(swiftCoreFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
2+
CoreFoundation.swift
3+
4+
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
5+
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
6+
SWIFT_MODULE_DEPENDS ObjectiveC Dispatch os
7+
FRAMEWORK_DEPENDS CoreFoundation)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
@_exported import CoreFoundation
14+
15+
public protocol _CFObject: class, Hashable {}
16+
extension _CFObject {
17+
public var hashValue: Int {
18+
return Int(bitPattern: CFHash(self))
19+
}
20+
public static func ==(left: Self, right: Self) -> Bool {
21+
return CFEqual(left, right)
22+
}
23+
}

stdlib/public/SDK/CoreGraphics/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ add_swift_library(swiftCoreGraphics ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_
44
Private.swift
55
# rdar://problem/20891746
66
# SWIFT_COMPILE_FLAGS ${STDLIB_SIL_SERIALIZE_ALL}
7-
SWIFT_MODULE_DEPENDS ObjectiveC Dispatch Darwin
7+
SWIFT_MODULE_DEPENDS ObjectiveC Dispatch Darwin CoreFoundation
88
SWIFT_MODULE_DEPENDS_OSX IOKit XPC
99
FRAMEWORK_DEPENDS CoreGraphics)
1010

stdlib/public/SDK/CoreGraphics/CoreGraphics.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,6 @@ extension CGColor {
4646
#endif
4747
}
4848

49-
extension CGColor: Equatable {}
50-
public func ==(lhs: CGColor, rhs: CGColor) -> Bool {
51-
return lhs.__equalTo(rhs)
52-
}
53-
5449

5550
//===----------------------------------------------------------------------===//
5651
// CGColorSpace
@@ -471,11 +466,6 @@ extension CGPath {
471466
}
472467
}
473468

474-
extension CGPath: Equatable {}
475-
public func ==(lhs: CGPath, rhs: CGPath) -> Bool {
476-
return lhs.__equalTo(rhs)
477-
}
478-
479469
extension CGMutablePath {
480470

481471
public func addRoundedRect(in rect: CGRect, cornerWidth: CGFloat,

stdlib/public/SDK/Foundation/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ add_swift_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SD
3535
Hashing.m
3636
Thunks.mm
3737

38-
SWIFT_MODULE_DEPENDS ObjectiveC CoreGraphics Dispatch os
38+
SWIFT_MODULE_DEPENDS ObjectiveC CoreFoundation CoreGraphics Dispatch os
3939
SWIFT_MODULE_DEPENDS_OSX XPC
4040
FRAMEWORK_DEPENDS Foundation)
4141

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
add_swift_library(swiftIOKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVERLAY
22
IOKit.swift
33
TARGET_SDKS OSX
4-
SWIFT_MODULE_DEPENDS ObjectiveC Dispatch
4+
SWIFT_MODULE_DEPENDS ObjectiveC Dispatch CoreFoundation
55
FRAMEWORK_DEPENDS IOKit)

test/ClangModules/Inputs/SwiftPrivateAttr.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ struct NSOptions : OptionSet {
9595
static var __privA: NSOptions { get }
9696
static var B: NSOptions { get }
9797
}
98-
class __PrivCFType {
98+
class __PrivCFType : _CFObject {
9999
}
100100
typealias __PrivCFSub = __PrivCFType
101101
typealias __PrivInt = Int32

0 commit comments

Comments
 (0)