Skip to content

Commit 526162f

Browse files
committed
[stdlib] Set, Dictionary: Fix uniqueness checking for Cocoa indices
1 parent 15f4b91 commit 526162f

File tree

4 files changed

+74
-40
lines changed

4 files changed

+74
-40
lines changed

stdlib/public/core/Dictionary.swift

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1827,6 +1827,14 @@ extension Dictionary.Index {
18271827
_conditionallyUnreachable()
18281828
}
18291829
}
1830+
1831+
@inlinable
1832+
@inline(__always)
1833+
internal mutating func _isUniquelyReferenced() -> Bool {
1834+
defer { _fixLifetime(self) }
1835+
var handle = _asCocoa.handleBitPattern
1836+
return handle == 0 || _isUnique_native(&handle)
1837+
}
18301838
#endif
18311839

18321840
@usableFromInline @_transparent
@@ -1843,14 +1851,27 @@ extension Dictionary.Index {
18431851
}
18441852

18451853
#if _runtime(_ObjC)
1846-
@usableFromInline @_transparent
1854+
@usableFromInline
18471855
internal var _asCocoa: _CocoaDictionary.Index {
1848-
switch _variant {
1849-
case .native:
1850-
_preconditionFailure(
1851-
"Attempting to access Dictionary elements using an invalid index")
1852-
case .cocoa(let cocoaIndex):
1853-
return cocoaIndex
1856+
@_transparent
1857+
get {
1858+
switch _variant {
1859+
case .native:
1860+
_preconditionFailure(
1861+
"Attempting to access Dictionary elements using an invalid index")
1862+
case .cocoa(let cocoaIndex):
1863+
return cocoaIndex
1864+
}
1865+
}
1866+
_modify {
1867+
guard case .cocoa(var cocoa) = _variant else {
1868+
_preconditionFailure(
1869+
"Attempting to access Dictionary elements using an invalid index")
1870+
}
1871+
let dummy = _HashTable.Index(bucket: _HashTable.Bucket(offset: 0), age: 0)
1872+
_variant = .native(dummy)
1873+
yield &cocoa
1874+
_variant = .cocoa(cocoa)
18541875
}
18551876
}
18561877
#endif

stdlib/public/core/DictionaryBridging.swift

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -471,8 +471,8 @@ extension _CocoaDictionary: _DictionaryBuffer {
471471
@usableFromInline // FIXME(cocoa-index): Should be inlinable
472472
@_effects(releasenone)
473473
internal func index(after index: Index) -> Index {
474-
var result = index
475-
formIndex(after: &result)
474+
var result = index.copy()
475+
formIndex(after: &result, isUnique: true)
476476
return result
477477
}
478478

@@ -484,11 +484,9 @@ extension _CocoaDictionary: _DictionaryBuffer {
484484

485485
@usableFromInline // FIXME(cocoa-index): Should be inlinable
486486
@_effects(releasenone)
487-
internal func formIndex(after index: inout Index) {
487+
internal func formIndex(after index: inout Index, isUnique: Bool) {
488488
validate(index)
489-
let isUnique = index.isUniquelyReferenced()
490489
if !isUnique { index = index.copy() }
491-
_sanityCheck(index.isUniquelyReferenced())
492490
let storage = index.storage // FIXME: rdar://problem/44863751
493491
storage.currentKeyIndex += 1
494492
}
@@ -636,18 +634,16 @@ extension _CocoaDictionary.Index {
636634
}
637635

638636
extension _CocoaDictionary.Index {
639-
@inlinable
640-
internal mutating func isUniquelyReferenced() -> Bool {
641-
defer { _fixLifetime(self) }
642-
guard _isNativePointer(_storage) else {
643-
return false
637+
@usableFromInline
638+
internal var handleBitPattern: UInt {
639+
@_effects(readonly)
640+
get {
641+
return unsafeBitCast(storage, to: UInt.self)
644642
}
645-
unowned(unsafe) var storage = _bridgeObject(toNative: _storage)
646-
return _isUnique_native(&storage)
647643
}
648644

649645
@usableFromInline
650-
internal mutating func copy() -> _CocoaDictionary.Index {
646+
internal func copy() -> _CocoaDictionary.Index {
651647
let storage = self.storage
652648
return _CocoaDictionary.Index(Storage(
653649
storage.base,

stdlib/public/core/Set.swift

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,14 @@ extension Set.Index {
13431343
_conditionallyUnreachable()
13441344
}
13451345
}
1346+
1347+
@inlinable
1348+
@inline(__always)
1349+
internal mutating func _isUniquelyReferenced() -> Bool {
1350+
defer { _fixLifetime(self) }
1351+
var handle = _asCocoa.handleBitPattern
1352+
return handle == 0 || _isUnique_native(&handle)
1353+
}
13461354
#endif
13471355

13481356
@usableFromInline @_transparent
@@ -1359,14 +1367,27 @@ extension Set.Index {
13591367
}
13601368

13611369
#if _runtime(_ObjC)
1362-
@usableFromInline @_transparent
1370+
@usableFromInline
13631371
internal var _asCocoa: _CocoaSet.Index {
1364-
switch _variant {
1365-
case .native:
1366-
_preconditionFailure(
1367-
"Attempting to access Set elements using an invalid index")
1368-
case .cocoa(let cocoaIndex):
1369-
return cocoaIndex
1372+
@_transparent
1373+
get {
1374+
switch _variant {
1375+
case .native:
1376+
_preconditionFailure(
1377+
"Attempting to access Set elements using an invalid index")
1378+
case .cocoa(let cocoaIndex):
1379+
return cocoaIndex
1380+
}
1381+
}
1382+
_modify {
1383+
guard case .cocoa(var cocoa) = _variant else {
1384+
_preconditionFailure(
1385+
"Attempting to access Set elements using an invalid index")
1386+
}
1387+
let dummy = _HashTable.Index(bucket: _HashTable.Bucket(offset: 0), age: 0)
1388+
_variant = .native(dummy)
1389+
yield &cocoa
1390+
_variant = .cocoa(cocoa)
13701391
}
13711392
}
13721393
#endif

stdlib/public/core/SetBridging.swift

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -345,8 +345,8 @@ extension _CocoaSet: _SetBuffer {
345345
@usableFromInline // FIXME(cocoa-index): Should be inlinable
346346
@_effects(releasenone)
347347
internal func index(after index: Index) -> Index {
348-
var result = index
349-
formIndex(after: &result)
348+
var result = index.copy()
349+
formIndex(after: &result, isUnique: true)
350350
return result
351351
}
352352

@@ -359,9 +359,8 @@ extension _CocoaSet: _SetBuffer {
359359

360360
@usableFromInline // FIXME(cocoa-index): Should be inlinable
361361
@_effects(releasenone)
362-
internal func formIndex(after index: inout Index) {
362+
internal func formIndex(after index: inout Index, isUnique: Bool) {
363363
validate(index)
364-
let isUnique = index.isUniquelyReferenced()
365364
if !isUnique { index = index.copy() }
366365
let storage = index.storage // FIXME: rdar://problem/44863751
367366
storage.currentKeyIndex += 1
@@ -472,18 +471,15 @@ extension _CocoaSet.Index {
472471
}
473472

474473
extension _CocoaSet.Index {
475-
@inlinable
476-
internal mutating func isUniquelyReferenced() -> Bool {
477-
defer { _fixLifetime(self) }
478-
guard _isNativePointer(_storage) else {
479-
return false
474+
@usableFromInline
475+
internal var handleBitPattern: UInt {
476+
@_effects(readonly)
477+
get {
478+
return unsafeBitCast(storage, to: UInt.self)
480479
}
481-
unowned(unsafe) var storage = _bridgeObject(toNative: _storage)
482-
return _isUnique_native(&storage)
483480
}
484481

485-
@usableFromInline
486-
internal mutating func copy() -> _CocoaSet.Index {
482+
internal func copy() -> _CocoaSet.Index {
487483
let storage = self.storage
488484
return _CocoaSet.Index(Storage(
489485
storage.base,

0 commit comments

Comments
 (0)