Skip to content

Commit 361ab62

Browse files
authored
Make all CF types Equatable and Hashable. (#4394)
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
1 parent 9a0f9d1 commit 361ab62

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
@@ -5553,6 +5553,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) {
55535553
case KnownProtocolKind::OptionSet:
55545554
case KnownProtocolKind::BridgedNSError:
55555555
case KnownProtocolKind::BridgedStoredNSError:
5556+
case KnownProtocolKind::CFObject:
55565557
case KnownProtocolKind::ErrorCodeProtocol:
55575558
return SpecialProtocol::None;
55585559
}

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"
@@ -1139,39 +1140,87 @@ bool WitnessChecker::findBestWitness(ValueDecl *requirement,
11391140
unsigned &numViable,
11401141
unsigned &bestIdx,
11411142
bool &doNotDiagnoseMatches) {
1142-
auto witnesses = lookupValueWitnesses(requirement, ignoringNames);
1143-
1144-
// Match each of the witnesses to the requirement.
1145-
bool anyFromUnconstrainedExtension = false;
1146-
numViable = 0;
1147-
bestIdx = 0;
1148-
1149-
for (auto witness : witnesses) {
1150-
// Don't match anything in a protocol.
1151-
// FIXME: When default implementations come along, we can try to match
1152-
// these when they're default implementations coming from another
1153-
// (unrelated) protocol.
1154-
if (isa<ProtocolDecl>(witness->getDeclContext())) {
1155-
continue;
1156-
}
1143+
enum Attempt {
1144+
Regular,
1145+
OperatorsFromOverlay,
1146+
Done
1147+
};
11571148

1158-
if (!witness->hasType())
1159-
TC.validateDecl(witness, true);
1149+
SmallVector<ValueDecl *, 4> witnesses;
1150+
bool anyFromUnconstrainedExtension;
11601151

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

1170-
if (auto *ext = dyn_cast<ExtensionDecl>(match.Witness->getDeclContext()))
1171-
if (!ext->isConstrainedExtension() && ext->getAsProtocolExtensionContext())
1172-
anyFromUnconstrainedExtension = true;
1203+
auto match = matchWitness(TC, Proto, conformance, DC,
1204+
requirement, witness);
1205+
if (match.isViable()) {
1206+
++numViable;
1207+
bestIdx = matches.size();
1208+
} else if (match.Kind == MatchKind::WitnessInvalid) {
1209+
doNotDiagnoseMatches = true;
1210+
}
1211+
1212+
if (auto *ext = dyn_cast<ExtensionDecl>(match.Witness->getDeclContext())){
1213+
if (!ext->isConstrainedExtension() &&
1214+
ext->getAsProtocolExtensionContext()) {
1215+
anyFromUnconstrainedExtension = true;
1216+
}
1217+
}
11731218

1174-
matches.push_back(std::move(match));
1219+
matches.push_back(std::move(match));
1220+
}
1221+
1222+
if (numViable > 0)
1223+
break;
11751224
}
11761225

11771226
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
@@ -5,8 +5,7 @@ add_swift_library(swiftCoreAudio ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK
55
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
66
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
77
TARGET_SDKS OSX IOS IOS_SIMULATOR TVOS TVOS_SIMULATOR WATCHOS WATCHOS_SIMULATOR
8-
SWIFT_MODULE_DEPENDS Dispatch
8+
SWIFT_MODULE_DEPENDS Dispatch CoreFoundation
99
SWIFT_MODULE_DEPENDS_OSX IOKit
10-
# Also depends on: CoreFoundation
1110
FRAMEWORK_DEPENDS CoreAudio)
1211

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
@@ -7,7 +7,7 @@ add_swift_library(swiftCoreGraphics ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_
77

88
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
99
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
10-
SWIFT_MODULE_DEPENDS ObjectiveC Dispatch Darwin
10+
SWIFT_MODULE_DEPENDS ObjectiveC Dispatch Darwin CoreFoundation
1111
SWIFT_MODULE_DEPENDS_OSX IOKit XPC
1212
FRAMEWORK_DEPENDS CoreGraphics)
1313

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
@@ -37,7 +37,7 @@ add_swift_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SD
3737

3838
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
3939
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
40-
SWIFT_MODULE_DEPENDS ObjectiveC CoreGraphics Dispatch os
40+
SWIFT_MODULE_DEPENDS ObjectiveC CoreFoundation CoreGraphics Dispatch os
4141
SWIFT_MODULE_DEPENDS_OSX XPC
4242
FRAMEWORK_DEPENDS Foundation)
4343

stdlib/public/SDK/IOKit/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ add_swift_library(swiftIOKit ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SDK_OVE
44
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}"
55
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
66
TARGET_SDKS OSX
7-
SWIFT_MODULE_DEPENDS ObjectiveC Dispatch
7+
SWIFT_MODULE_DEPENDS ObjectiveC Dispatch CoreFoundation
88
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)