Skip to content

Commit a2e2166

Browse files
authored
Merge pull request #19694 from lorentey/values-isnt-really-a-mutablecollection
[stdlib] Allow native dictionaries to advance Cocoa indices
2 parents 5db65bf + 6e671b6 commit a2e2166

File tree

4 files changed

+62
-1
lines changed

4 files changed

+62
-1
lines changed

stdlib/public/core/Dictionary.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,6 +1862,19 @@ extension Dictionary.Index {
18621862
}
18631863
#endif
18641864

1865+
@usableFromInline @_transparent
1866+
internal var _isNative: Bool {
1867+
switch _variant {
1868+
case .native:
1869+
return true
1870+
#if _runtime(_ObjC)
1871+
case .cocoa:
1872+
_cocoaPath()
1873+
return false
1874+
#endif
1875+
}
1876+
}
1877+
18651878
@usableFromInline @_transparent
18661879
internal var _asNative: _HashTable.Index {
18671880
switch _variant {

stdlib/public/core/DictionaryBridging.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,14 @@ extension _CocoaDictionary.Index {
641641
}
642642
}
643643

644+
@usableFromInline
645+
internal var dictionary: _CocoaDictionary {
646+
@_effects(releasenone)
647+
get {
648+
return storage.base
649+
}
650+
}
651+
644652
@usableFromInline
645653
internal func copy() -> _CocoaDictionary.Index {
646654
let storage = self.storage

stdlib/public/core/NativeDictionary.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,13 @@ extension _NativeDictionary: _DictionaryBuffer {
316316

317317
@inlinable
318318
internal func index(after index: Index) -> Index {
319-
// Note that _asNative forces this not to work on Cocoa indices.
319+
#if _runtime(_ObjC)
320+
guard _fastPath(index._isNative) else {
321+
let _ = validatedBucket(for: index)
322+
let i = index._asCocoa
323+
return Index(_cocoa: i.dictionary.index(after: i))
324+
}
325+
#endif
320326
let bucket = validatedBucket(for: index._asNative)
321327
let next = hashTable.occupiedBucket(after: bucket)
322328
return Index(_native: _HashTable.Index(bucket: next, age: age))

validation-test/stdlib/Dictionary.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4778,6 +4778,40 @@ DictionaryTestSuite.test("Hashable") {
47784778
checkHashable(variants, equalityOracle: { _, _ in true })
47794779
}
47804780

4781+
#if _runtime(_ObjC)
4782+
DictionaryTestSuite.test("Values.MutationDoesNotInvalidateIndices") {
4783+
let objects: [NSNumber] = [1, 2, 3, 4]
4784+
let keys: [NSString] = ["Blanche", "Rose", "Dorothy", "Sophia"]
4785+
let ns = NSDictionary(objects: objects, forKeys: keys)
4786+
var d = ns as! Dictionary<NSString, NSNumber>
4787+
4788+
let i = d.index(forKey: "Rose")!
4789+
expectEqual(d[i].key, "Rose")
4790+
expectEqual(d[i].value, 2 as NSNumber)
4791+
4792+
// Mutating a value through the Values view will convert the bridged
4793+
// NSDictionary instance to native Dictionary storage. However, Values is a
4794+
// MutableCollection, so doing so must not invalidate existing indices.
4795+
d.values[i] = 20 as NSNumber
4796+
4797+
// The old Cocoa-based index must still work with the new dictionary.
4798+
expectEqual(d.values[i], 20 as NSNumber)
4799+
4800+
let i2 = d.index(forKey: "Rose")
4801+
4802+
// You should also be able to advance Cocoa indices.
4803+
let j = d.index(after: i)
4804+
4805+
// Unfortunately, Cocoa and Native indices aren't comparable, so the
4806+
// Collection conformance is not quite perfect.
4807+
expectCrash(withMessage: "Comparing indexes from different dictionaries") {
4808+
print(i == i2)
4809+
}
4810+
}
4811+
#endif
4812+
4813+
4814+
47814815
DictionaryTestSuite.setUp {
47824816
#if _runtime(_ObjC)
47834817
// Exercise ARC's autoreleased return value optimization in Foundation.

0 commit comments

Comments
 (0)