Skip to content

Commit 0868b6f

Browse files
committed
stdlib: Dictionary, Set: allow querying for a mismatched type when bridged
Swift's Dictionary and Set are typed, but when bridged to NSDictionary and NSSet they should behave accordingly, that is, allow querying for keys of arbitrary types. rdar://problem/23679193
1 parent b4c7c1f commit 0868b6f

File tree

4 files changed

+71
-6
lines changed

4 files changed

+71
-6
lines changed

stdlib/public/core/HashedCollections.swift.gyb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3275,7 +3275,9 @@ final internal class _Native${Self}StorageOwner<${TypeParametersDecl}>
32753275
@warn_unused_result
32763276
internal func bridgingObjectForKey(_ aKey: AnyObject)
32773277
-> AnyObject? {
3278-
let nativeKey = _forceBridgeFromObjectiveC(aKey, Key.self)
3278+
guard let nativeKey = _conditionallyBridgeFromObjectiveC(aKey, Key.self)
3279+
else { return nil }
3280+
32793281
let (i, found) = nativeStorage._find(
32803282
nativeKey, startBucket: nativeStorage._bucket(nativeKey))
32813283
if found {

test/1_stdlib/Inputs/DictionaryKeyValueTypesObjC.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,39 @@ class TestObjCKeyTy : NSObject, NSCopying {
113113
var serial: Int
114114
}
115115

116+
// A type that satisfies the requirements of an NSDictionary key (or an NSSet
117+
// member), but traps when any of its methods are called.
118+
class TestObjCInvalidKeyTy {
119+
init() {
120+
_objcKeyCount.fetchAndAdd(1)
121+
serial = _objcKeySerial.addAndFetch(1)
122+
}
123+
124+
deinit {
125+
assert(serial > 0, "double destruction")
126+
_objcKeyCount.fetchAndAdd(-1)
127+
serial = -serial
128+
}
129+
130+
@objc
131+
var description: String {
132+
assert(serial > 0, "dead TestObjCInvalidKeyTy")
133+
fatalError()
134+
}
135+
136+
@objc
137+
func isEqual(_ object: AnyObject!) -> Bool {
138+
fatalError()
139+
}
140+
141+
@objc
142+
var hash : Int {
143+
fatalError()
144+
}
145+
146+
var serial: Int
147+
}
148+
116149
var _objcValueCount = _stdlib_AtomicInt(0)
117150
var _objcValueSerial = _stdlib_AtomicInt(0)
118151

validation-test/stdlib/Dictionary.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2558,6 +2558,11 @@ DictionaryTestSuite.test("BridgedToObjC.Verbatim.ObjectForKey") {
25582558

25592559
expectEmpty(d.object(forKey: TestObjCKeyTy(40)))
25602560

2561+
// NSDictionary can store mixed key types. Swift's Dictionary is typed, but
2562+
// when bridged to NSDictionary, it should behave like one, and allow queries
2563+
// for mismatched key types.
2564+
expectEmpty(d.object(forKey: TestObjCInvalidKeyTy()))
2565+
25612566
for i in 0..<3 {
25622567
expectEqual(idValue10, unsafeBitCast(
25632568
d.object(forKey: TestObjCKeyTy(10)), to: UInt.self))

validation-test/stdlib/Set.swift

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,12 +2154,37 @@ SetTestSuite.test("BridgedToObjC.Verbatim.Count") {
21542154
}
21552155

21562156
SetTestSuite.test("BridgedToObjC.Verbatim.Contains") {
2157-
let nss = getBridgedNSSetOfRefTypesBridgedVerbatim()
2157+
let s = getBridgedNSSetOfRefTypesBridgedVerbatim()
21582158

2159-
expectNotEmpty(nss.member(TestObjCKeyTy(1010)))
2160-
expectNotEmpty(nss.member(TestObjCKeyTy(2020)))
2161-
expectNotEmpty(nss.member(TestObjCKeyTy(3030)))
2162-
expectEmpty(nss.member(TestObjCKeyTy(4040)))
2159+
var v: AnyObject? = s.member(TestObjCKeyTy(1010))
2160+
expectEqual(1010, (v as! TestObjCKeyTy).value)
2161+
let idValue10 = unsafeBitCast(v, to: UInt.self)
2162+
2163+
v = s.member(TestObjCKeyTy(2020))
2164+
expectEqual(2020, (v as! TestObjCKeyTy).value)
2165+
let idValue20 = unsafeBitCast(v, to: UInt.self)
2166+
2167+
v = s.member(TestObjCKeyTy(3030))
2168+
expectEqual(3030, (v as! TestObjCKeyTy).value)
2169+
let idValue30 = unsafeBitCast(v, to: UInt.self)
2170+
2171+
expectEmpty(s.member(TestObjCKeyTy(4040)))
2172+
2173+
// NSSet can store mixed key types. Swift's Set is typed, but when bridged
2174+
// to NSSet, it should behave like one, and allow queries for mismatched key
2175+
// types.
2176+
expectEmpty(s.member(TestObjCInvalidKeyTy()))
2177+
2178+
for i in 0..<3 {
2179+
expectEqual(idValue10,
2180+
unsafeBitCast(s.member(TestObjCKeyTy(1010)), to: UInt.self))
2181+
2182+
expectEqual(idValue20,
2183+
unsafeBitCast(s.member(TestObjCKeyTy(2020)), to: UInt.self))
2184+
2185+
expectEqual(idValue30,
2186+
unsafeBitCast(s.member(TestObjCKeyTy(3030)), to: UInt.self))
2187+
}
21632188

21642189
expectAutoreleasedKeysAndValues(unopt: (3, 0))
21652190
}

0 commit comments

Comments
 (0)