From 77bf53511607ff257ed98ad534ef594fb2750e7f Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Fri, 28 Sep 2018 19:36:25 +0100 Subject: [PATCH 1/3] [stdlib] Set, Dictionary: Flatten switch statements The optimizer dislikes nested switch statements; flatten them out to simplify optimization and to hopefully speed things up a little. --- stdlib/public/core/Dictionary.swift | 84 ++++-- stdlib/public/core/DictionaryBridging.swift | 8 +- stdlib/public/core/DictionaryCasting.swift | 14 +- stdlib/public/core/DictionaryVariant.swift | 278 ++++++++------------ stdlib/public/core/NativeDictionary.swift | 9 +- stdlib/public/core/NativeSet.swift | 9 +- stdlib/public/core/Set.swift | 73 +++-- stdlib/public/core/SetBridging.swift | 8 +- stdlib/public/core/SetCasting.swift | 8 +- stdlib/public/core/SetVariant.swift | 209 +++++++-------- 10 files changed, 338 insertions(+), 362 deletions(-) diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index cc15df4bf11b..b89b736a7b81 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -1513,20 +1513,14 @@ extension Dictionary { @inlinable public mutating func swapAt(_ i: Index, _ j: Index) { guard i != j else { return } - let (a, b): (_HashTable.Bucket, _HashTable.Bucket) - switch _variant { - case .native(let native): - a = native.validatedBucket(for: i) - b = native.validatedBucket(for: j) #if _runtime(_ObjC) - case .cocoa(let cocoa): - _variant.cocoaPath() - let native = _NativeDictionary(cocoa) - a = native.validatedBucket(for: i) - b = native.validatedBucket(for: j) - _variant = .native(native) -#endif + if !_variant.isNative { + _variant = .native(_NativeDictionary(_variant.asCocoa)) } +#endif + let native = _variant.asNative + let a = native.validatedBucket(for: i) + let b = native.validatedBucket(for: j) let isUnique = _variant.isUniquelyReferenced() _variant.asNative.swapValuesAt(a, b, isUnique: isUnique) } @@ -1850,6 +1844,17 @@ extension Dictionary.Index { var handle = _asCocoa.handleBitPattern return handle == 0 || _isUnique_native(&handle) } + + @usableFromInline @_transparent + internal var _isNative: Bool { + switch _variant { + case .native: + return true + case .cocoa: + _cocoaPath() + return false + } + } #endif @usableFromInline @_transparent @@ -1935,19 +1940,17 @@ extension Dictionary.Index: Comparable { extension Dictionary.Index: Hashable { public // FIXME(cocoa-index): Make inlinable func hash(into hasher: inout Hasher) { - #if _runtime(_ObjC) - switch _variant { - case .native(let nativeIndex): - hasher.combine(0 as UInt8) - hasher.combine(nativeIndex.bucket.offset) - case .cocoa(let cocoaIndex): - _cocoaPath() +#if _runtime(_ObjC) + guard _isNative else { hasher.combine(1 as UInt8) - hasher.combine(cocoaIndex.storage.currentKeyIndex) + hasher.combine(_asCocoa.storage.currentKeyIndex) + return } - #else + hasher.combine(0 as UInt8) hasher.combine(_asNative.bucket.offset) - #endif +#else + hasher.combine(_asNative.bucket.offset) +#endif } } @@ -2011,6 +2014,17 @@ extension Dictionary.Iterator { _conditionallyUnreachable() } } + + @usableFromInline @_transparent + internal var _isNative: Bool { + switch _variant { + case .native: + return true + case .cocoa: + _cocoaPath() + return false + } + } #endif @usableFromInline @_transparent @@ -2029,6 +2043,21 @@ extension Dictionary.Iterator { self._variant = .native(newValue) } } + +#if _runtime(_ObjC) + @usableFromInline @_transparent + internal var _asCocoa: _CocoaDictionary.Iterator { + get { + switch _variant { + case .native: + _sanityCheckFailure("internal error: does not contain a Cocoa index") + case .cocoa(let cocoa): + return cocoa + } + } + } +#endif + } extension Dictionary.Iterator: IteratorProtocol { @@ -2039,20 +2068,17 @@ extension Dictionary.Iterator: IteratorProtocol { @inlinable @inline(__always) public mutating func next() -> (key: Key, value: Value)? { - switch _variant { - case .native: - return _asNative.next() #if _runtime(_ObjC) - case .cocoa(let cocoaIterator): - _cocoaPath() - if let (cocoaKey, cocoaValue) = cocoaIterator.next() { + guard _isNative else { + if let (cocoaKey, cocoaValue) = _asCocoa.next() { let nativeKey = _forceBridgeFromObjectiveC(cocoaKey, Key.self) let nativeValue = _forceBridgeFromObjectiveC(cocoaValue, Value.self) return (nativeKey, nativeValue) } return nil -#endif } +#endif + return _asNative.next() } } diff --git a/stdlib/public/core/DictionaryBridging.swift b/stdlib/public/core/DictionaryBridging.swift index a434aaf7db9c..cf67d5887a18 100644 --- a/stdlib/public/core/DictionaryBridging.swift +++ b/stdlib/public/core/DictionaryBridging.swift @@ -797,12 +797,10 @@ extension _CocoaDictionary.Iterator: IteratorProtocol { extension Dictionary { @inlinable public __consuming func _bridgeToObjectiveCImpl() -> _NSDictionaryCore { - switch _variant { - case .native(let nativeDictionary): - return nativeDictionary.bridged() - case .cocoa(let cocoaDictionary): - return cocoaDictionary.object + guard _variant.isNative else { + return _variant.asCocoa.object } + return _variant.asNative.bridged() } /// Returns the native Dictionary hidden inside this NSDictionary; diff --git a/stdlib/public/core/DictionaryCasting.swift b/stdlib/public/core/DictionaryCasting.swift index 50403ee41c7e..38f83b940772 100644 --- a/stdlib/public/core/DictionaryCasting.swift +++ b/stdlib/public/core/DictionaryCasting.swift @@ -56,14 +56,14 @@ public func _dictionaryDownCast( && _isClassOrObjCExistential(DerivedKey.self) && _isClassOrObjCExistential(DerivedValue.self) { - switch source._variant { - case .native(let native): - // Note: it is safe to treat the buffer as immutable here because - // Dictionary will not mutate buffer with reference count greater than 1. - return Dictionary(_immutableCocoaDictionary: native.bridged()) - case .cocoa(let cocoa): - return Dictionary(_immutableCocoaDictionary: cocoa.object) + guard source._variant.isNative else { + return Dictionary( + _immutableCocoaDictionary: source._variant.asCocoa.object) } + // Note: it is safe to treat the buffer as immutable here because + // Dictionary will not mutate buffer with reference count greater than 1. + return Dictionary( + _immutableCocoaDictionary: source._variant.asNative.bridged()) } #endif return _dictionaryDownCastConditional(source)! diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index ee5ea7d82123..8f3d0f67dac0 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -60,21 +60,31 @@ extension Dictionary._Variant { @inlinable internal mutating func isUniquelyReferenced() -> Bool { +#if _runtime(_ObjC) + guard isNative else { + // Don't consider Cocoa a buffer mutable, even if it is mutable and it is + // uniquely referenced. + return false + } +#endif + // Note that &self drills down through .native(_NativeDictionary) to the + // first property in _NativeDictionary, which is the reference to the + // storage. + return _isUnique_native(&self) + } + +#if _runtime(_ObjC) + @usableFromInline @_transparent + internal var isNative: Bool { switch self { case .native: - // Note that &self drills down through .native(_NativeDictionary) to the - // first property in _NativeDictionary, which is the reference to the - // storage. - return _isUnique_native(&self) -#if _runtime(_ObjC) + return true case .cocoa: cocoaPath() - // Don't consider Cocoa buffer mutable, even if it is mutable and is - // uniquely referenced. return false -#endif } } +#endif @inlinable internal var asNative: _NativeDictionary { @@ -110,17 +120,16 @@ extension Dictionary._Variant { /// Reserves enough space for the specified number of elements to be stored /// without reallocating additional storage. internal mutating func reserveCapacity(_ capacity: Int) { - switch self { - case .native: - let isUnique = isUniquelyReferenced() - asNative.reserveCapacity(capacity, isUnique: isUnique) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { + let cocoa = asCocoa let capacity = Swift.max(cocoa.count, capacity) self = .native(_NativeDictionary(cocoa, capacity: capacity)) -#endif + return } +#endif + let isUnique = isUniquelyReferenced() + asNative.reserveCapacity(capacity, isUnique: isUnique) } /// The number of elements that can be stored without expanding the current @@ -132,15 +141,12 @@ extension Dictionary._Variant { /// at which adding any more elements will exceed the load factor. @inlinable internal var capacity: Int { - switch self { - case .native: - return asNative.capacity #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return cocoa.count -#endif + guard isNative else { + return asCocoa.count } +#endif + return asNative.capacity } } @@ -152,41 +158,32 @@ extension Dictionary._Variant: _DictionaryBuffer { @inlinable internal var startIndex: Index { - switch self { - case .native(let native): - return native.startIndex #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return Index(_cocoa: cocoa.startIndex) -#endif + guard isNative else { + return Index(_cocoa: asCocoa.startIndex) } +#endif + return asNative.startIndex } @inlinable internal var endIndex: Index { - switch self { - case .native(let native): - return native.endIndex #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return Index(_cocoa: cocoa.endIndex) -#endif + guard isNative else { + return Index(_cocoa: asCocoa.endIndex) } +#endif + return asNative.endIndex } @inlinable internal func index(after index: Index) -> Index { - switch self { - case .native(let native): - return native.index(after: index) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return Index(_cocoa: cocoa.index(after: index._asCocoa)) -#endif + guard isNative else { + return Index(_cocoa: asCocoa.index(after: index._asCocoa)) } +#endif + return asNative.index(after: index) } @inlinable @@ -206,111 +203,90 @@ extension Dictionary._Variant: _DictionaryBuffer { @inlinable @inline(__always) internal func index(forKey key: Key) -> Index? { - switch self { - case .native(let native): - return native.index(forKey: key) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { let cocoaKey = _bridgeAnythingToObjectiveC(key) - guard let index = cocoa.index(forKey: cocoaKey) else { return nil } + guard let index = asCocoa.index(forKey: cocoaKey) else { return nil } return Index(_cocoa: index) -#endif } +#endif + return asNative.index(forKey: key) } @inlinable internal var count: Int { @inline(__always) get { - switch self { - case .native(let native): - return native.count #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return cocoa.count -#endif + guard isNative else { + return asCocoa.count } +#endif + return asNative.count } } @inlinable @inline(__always) func contains(_ key: Key) -> Bool { - switch self { - case .native(let native): - return native.contains(key) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { let cocoaKey = _bridgeAnythingToObjectiveC(key) - return cocoa.contains(cocoaKey) -#endif + return asCocoa.contains(cocoaKey) } +#endif + return asNative.contains(key) } @inlinable @inline(__always) func lookup(_ key: Key) -> Value? { - switch self { - case .native(let native): - return native.lookup(key) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { let cocoaKey = _bridgeAnythingToObjectiveC(key) - guard let cocoaValue = cocoa.lookup(cocoaKey) else { return nil } + guard let cocoaValue = asCocoa.lookup(cocoaKey) else { return nil } return _forceBridgeFromObjectiveC(cocoaValue, Value.self) -#endif } +#endif + return asNative.lookup(key) } @inlinable @inline(__always) func lookup(_ index: Index) -> (key: Key, value: Value) { - switch self { - case .native(let native): - return native.lookup(index) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - let (cocoaKey, cocoaValue) = cocoa.lookup(index._asCocoa) + guard isNative else { + let (cocoaKey, cocoaValue) = asCocoa.lookup(index._asCocoa) let nativeKey = _forceBridgeFromObjectiveC(cocoaKey, Key.self) let nativeValue = _forceBridgeFromObjectiveC(cocoaValue, Value.self) return (nativeKey, nativeValue) -#endif } +#endif + return asNative.lookup(index) } @inlinable @inline(__always) func key(at index: Index) -> Key { - switch self { - case .native(let native): - return native.key(at: index) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - let cocoaKey = cocoa.key(at: index._asCocoa) + guard isNative else { + let cocoaKey = asCocoa.key(at: index._asCocoa) return _forceBridgeFromObjectiveC(cocoaKey, Key.self) -#endif } +#endif + return asNative.key(at: index) } @inlinable @inline(__always) func value(at index: Index) -> Value { - switch self { - case .native(let native): - return native.value(at: index) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - let cocoaValue = cocoa.value(at: index._asCocoa) + guard isNative else { + let cocoaValue = asCocoa.value(at: index._asCocoa) return _forceBridgeFromObjectiveC(cocoaValue, Value.self) -#endif } +#endif + return asNative.value(at: index) } } @@ -325,29 +301,27 @@ extension Dictionary._Variant { internal mutating func mutatingFind( _ key: Key ) -> (bucket: _NativeDictionary.Bucket, found: Bool) { - switch self { - case .native: - let isUnique = isUniquelyReferenced() - return asNative.mutatingFind(key, isUnique: isUnique) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { + let cocoa = asCocoa var native = _NativeDictionary( cocoa, capacity: cocoa.count + 1) let result = native.mutatingFind(key, isUnique: true) self = .native(native) return result -#endif } +#endif + let isUnique = isUniquelyReferenced() + return asNative.mutatingFind(key, isUnique: isUnique) } @inlinable @inline(__always) internal mutating func ensureUniqueNative() -> _NativeDictionary { #if _runtime(_ObjC) - if case .cocoa(let cocoa) = self { + guard isNative else { cocoaPath() - let native = _NativeDictionary(cocoa) + let native = _NativeDictionary(asCocoa) self = .native(native) return native } @@ -364,41 +338,35 @@ extension Dictionary._Variant { _ value: __owned Value, forKey key: Key ) -> Value? { - switch self { - case .native: - let isUnique = self.isUniquelyReferenced() - return asNative.updateValue(value, forKey: key, isUnique: isUnique) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { // Make sure we have space for an extra element. + let cocoa = asCocoa var native = _NativeDictionary( cocoa, capacity: cocoa.count + 1) let result = native.updateValue(value, forKey: key, isUnique: true) self = .native(native) return result -#endif } +#endif + let isUnique = self.isUniquelyReferenced() + return asNative.updateValue(value, forKey: key, isUnique: isUnique) } @inlinable internal mutating func setValue(_ value: __owned Value, forKey key: Key) { - switch self { - case .native: - let isUnique = self.isUniquelyReferenced() - asNative.setValue(value, forKey: key, isUnique: isUnique) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + if !isNative { // Make sure we have space for an extra element. - var native = _NativeDictionary( + let cocoa = asCocoa + self = .native(_NativeDictionary( cocoa, - capacity: cocoa.count + 1) - native.setValue(value, forKey: key, isUnique: true) - self = .native(native) -#endif + capacity: cocoa.count + 1)) } +#endif + let isUnique = self.isUniquelyReferenced() + asNative.setValue(value, forKey: key, isUnique: isUnique) } @inlinable @@ -412,16 +380,10 @@ extension Dictionary._Variant { @inlinable internal mutating func removeValue(forKey key: Key) -> Value? { - switch self { - case .native: - let (bucket, found) = asNative.find(key) - guard found else { return nil } - let isUnique = isUniquelyReferenced() - return asNative.uncheckedRemove(at: bucket, isUnique: isUnique).value #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { let cocoaKey = _bridgeAnythingToObjectiveC(key) + let cocoa = asCocoa guard cocoa.lookup(cocoaKey) != nil else { return nil } var native = _NativeDictionary(cocoa) let (bucket, found) = native.find(key) @@ -429,8 +391,12 @@ extension Dictionary._Variant { let old = native.uncheckedRemove(at: bucket, isUnique: true).value self = .native(native) return old -#endif } +#endif + let (bucket, found) = asNative.find(key) + guard found else { return nil } + let isUnique = isUniquelyReferenced() + return asNative.uncheckedRemove(at: bucket, isUnique: isUnique).value } @inlinable @@ -441,16 +407,14 @@ extension Dictionary._Variant { } guard count > 0 else { return } - switch self { - case .native: - let isUnique = isUniquelyReferenced() - asNative.removeAll(isUnique: isUnique) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - self = .native(_NativeDictionary(capacity: cocoa.count)) -#endif + guard isNative else { + self = .native(_NativeDictionary(capacity: asCocoa.count)) + return } +#endif + let isUnique = isUniquelyReferenced() + asNative.removeAll(isUnique: isUnique) } } @@ -461,15 +425,12 @@ extension Dictionary._Variant { @inlinable @inline(__always) __consuming internal func makeIterator() -> Dictionary.Iterator { - switch self { - case .native(let native): - return Dictionary.Iterator(_native: native.makeIterator()) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return Dictionary.Iterator(_cocoa: cocoa.makeIterator()) -#endif + guard isNative else { + return Dictionary.Iterator(_cocoa: asCocoa.makeIterator()) } +#endif + return Dictionary.Iterator(_native: asNative.makeIterator()) } } @@ -478,15 +439,12 @@ extension Dictionary._Variant { internal func mapValues( _ transform: (Value) throws -> T ) rethrows -> _NativeDictionary { - switch self { - case .native(let native): - return try native.mapValues(transform) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return try cocoa.mapValues(transform) -#endif + guard isNative else { + return try asCocoa.mapValues(transform) } +#endif + return try asNative.mapValues(transform) } @inlinable @@ -494,24 +452,22 @@ extension Dictionary._Variant { _ keysAndValues: __owned S, uniquingKeysWith combine: (Value, Value) throws -> Value ) rethrows where S.Element == (Key, Value) { - switch self { - case .native: - let isUnique = isUniquelyReferenced() - try asNative.merge( - keysAndValues, - isUnique: isUnique, - uniquingKeysWith: combine) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - var native = _NativeDictionary(cocoa) + guard isNative else { + var native = _NativeDictionary(asCocoa) try native.merge( keysAndValues, isUnique: true, uniquingKeysWith: combine) self = .native(native) -#endif + return } +#endif + let isUnique = isUniquelyReferenced() + try asNative.merge( + keysAndValues, + isUnique: isUnique, + uniquingKeysWith: combine) } } diff --git a/stdlib/public/core/NativeDictionary.swift b/stdlib/public/core/NativeDictionary.swift index 5dd30798392e..1119c4db0994 100644 --- a/stdlib/public/core/NativeDictionary.swift +++ b/stdlib/public/core/NativeDictionary.swift @@ -275,15 +275,13 @@ extension _NativeDictionary { @inlinable @inline(__always) func validatedBucket(for index: Dictionary.Index) -> Bucket { - switch index._variant { - case .native(let native): - return validatedBucket(for: native) #if _runtime(_ObjC) - case .cocoa(let cocoa): + guard index._isNative else { index._cocoaPath() // Accept Cocoa indices as long as they contain a key that exists in this // dictionary, and the address of their Cocoa object generates the same // age. + let cocoa = index._asCocoa if cocoa.age == self.age { let key = _forceBridgeFromObjectiveC(cocoa.key, Key.self) let (bucket, found) = find(key) @@ -293,8 +291,9 @@ extension _NativeDictionary { } _preconditionFailure( "Attempting to access Dictionary elements using an invalid index") -#endif } +#endif + return validatedBucket(for: index._asNative) } } diff --git a/stdlib/public/core/NativeSet.swift b/stdlib/public/core/NativeSet.swift index 38aa7b19b19e..6b261216637e 100644 --- a/stdlib/public/core/NativeSet.swift +++ b/stdlib/public/core/NativeSet.swift @@ -241,12 +241,10 @@ extension _NativeSet { @inlinable @inline(__always) func validatedBucket(for index: Set.Index) -> Bucket { - switch index._variant { - case .native(let native): - return validatedBucket(for: native) #if _runtime(_ObjC) - case .cocoa(let cocoa): + guard index._isNative else { index._cocoaPath() + let cocoa = index._asCocoa // Accept Cocoa indices as long as they contain an element that exists in // this set, and the address of their Cocoa object generates the same age. if cocoa.age == self.age { @@ -258,8 +256,9 @@ extension _NativeSet { } _preconditionFailure( "Attempting to access Set elements using an invalid index") -#endif } +#endif + return validatedBucket(for: index._asNative) } } diff --git a/stdlib/public/core/Set.swift b/stdlib/public/core/Set.swift index 2dadaabf386a..dba65a482cd7 100644 --- a/stdlib/public/core/Set.swift +++ b/stdlib/public/core/Set.swift @@ -1358,6 +1358,19 @@ extension Set.Index { } #endif +#if _runtime(_ObjC) + @usableFromInline @_transparent + internal var _isNative: Bool { + switch _variant { + case .native: + return true + case .cocoa: + _cocoaPath() + return false + } + } +#endif + @usableFromInline @_transparent internal var _asNative: _HashTable.Index { switch _variant { @@ -1446,19 +1459,17 @@ extension Set.Index: Hashable { /// of this instance. public // FIXME(cocoa-index): Make inlinable func hash(into hasher: inout Hasher) { - #if _runtime(_ObjC) - switch _variant { - case .native(let nativeIndex): - hasher.combine(0 as UInt8) - hasher.combine(nativeIndex.bucket.offset) - case .cocoa(let cocoaIndex): - _cocoaPath() +#if _runtime(_ObjC) + guard _isNative else { hasher.combine(1 as UInt8) - hasher.combine(cocoaIndex.storage.currentKeyIndex) + hasher.combine(_asCocoa.storage.currentKeyIndex) + return } - #else + hasher.combine(0 as UInt8) hasher.combine(_asNative.bucket.offset) - #endif +#else + hasher.combine(_asNative.bucket.offset) +#endif } } @@ -1523,6 +1534,19 @@ extension Set.Iterator { } #endif +#if _runtime(_ObjC) + @usableFromInline @_transparent + internal var _isNative: Bool { + switch _variant { + case .native: + return true + case .cocoa: + _cocoaPath() + return false + } + } +#endif + @usableFromInline @_transparent internal var _asNative: _NativeSet.Iterator { get { @@ -1539,6 +1563,20 @@ extension Set.Iterator { self._variant = .native(newValue) } } + +#if _runtime(_ObjC) + @usableFromInline @_transparent + internal var _asCocoa: _CocoaSet.Iterator { + get { + switch _variant { + case .native: + _sanityCheckFailure("internal error: does not contain a Cocoa index") + case .cocoa(let cocoa): + return cocoa + } + } + } +#endif } extension Set.Iterator: IteratorProtocol { @@ -1550,19 +1588,12 @@ extension Set.Iterator: IteratorProtocol { @inline(__always) public mutating func next() -> Element? { #if _runtime(_ObjC) - switch _variant { - case .native: - return _asNative.next() - case .cocoa(let cocoaIterator): - _cocoaPath() - if let cocoaElement = cocoaIterator.next() { - return _forceBridgeFromObjectiveC(cocoaElement, Element.self) - } - return nil + guard _isNative else { + guard let cocoaElement = _asCocoa.next() else { return nil } + return _forceBridgeFromObjectiveC(cocoaElement, Element.self) } -#else - return _asNative.next() #endif + return _asNative.next() } } diff --git a/stdlib/public/core/SetBridging.swift b/stdlib/public/core/SetBridging.swift index 06cdef01d569..d793b971abdb 100644 --- a/stdlib/public/core/SetBridging.swift +++ b/stdlib/public/core/SetBridging.swift @@ -619,12 +619,10 @@ extension _CocoaSet.Iterator: IteratorProtocol { extension Set { @inlinable public __consuming func _bridgeToObjectiveCImpl() -> _NSSetCore { - switch _variant { - case .native(let nativeSet): - return nativeSet.bridged() - case .cocoa(let cocoaSet): - return cocoaSet.object + guard _variant.isNative else { + return _variant.asCocoa.object } + return _variant.asNative.bridged() } /// Returns the native Dictionary hidden inside this NSDictionary; diff --git a/stdlib/public/core/SetCasting.swift b/stdlib/public/core/SetCasting.swift index 787c51a42071..6562738c228b 100644 --- a/stdlib/public/core/SetCasting.swift +++ b/stdlib/public/core/SetCasting.swift @@ -48,12 +48,10 @@ public func _setDownCast(_ source: Set) #if _runtime(_ObjC) if _isClassOrObjCExistential(BaseValue.self) && _isClassOrObjCExistential(DerivedValue.self) { - switch source._variant { - case .native(let nativeSet): - return Set(_immutableCocoaSet: nativeSet.bridged()) - case .cocoa(let cocoaSet): - return Set(_immutableCocoaSet: cocoaSet.object) + guard source._variant.isNative else { + return Set(_immutableCocoaSet: source._variant.asCocoa.object) } + return Set(_immutableCocoaSet: source._variant.asNative.bridged()) } #endif return _setDownCastConditional(source)! diff --git a/stdlib/public/core/SetVariant.swift b/stdlib/public/core/SetVariant.swift index 33ce0d85cd88..2ee712554c9f 100644 --- a/stdlib/public/core/SetVariant.swift +++ b/stdlib/public/core/SetVariant.swift @@ -58,20 +58,30 @@ extension Set._Variant { @inlinable internal mutating func isUniquelyReferenced() -> Bool { +#if _runtime(_ObjC) + guard isNative else { + // Don't consider Cocoa buffer mutable, even if it is mutable and is + // uniquely referenced. + return false + } +#endif // Note that &self drills down through .native(_NativeSet) to the first // property in _NativeSet, which is the reference to the storage. + return _isUnique_native(&self) + } + +#if _runtime(_ObjC) + @usableFromInline @_transparent + internal var isNative: Bool { switch self { case .native: - return _isUnique_native(&self) -#if _runtime(_ObjC) + return true case .cocoa: cocoaPath() - // Don't consider Cocoa buffer mutable, even if it is mutable and is - // uniquely referenced. return false -#endif } } +#endif @usableFromInline @_transparent internal var asNative: _NativeSet { @@ -105,17 +115,16 @@ extension Set._Variant { /// Reserves enough space for the specified number of elements to be stored /// without reallocating additional storage. internal mutating func reserveCapacity(_ capacity: Int) { - switch self { - case .native: - let isUnique = isUniquelyReferenced() - asNative.reserveCapacity(capacity, isUnique: isUnique) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { + let cocoa = asCocoa let capacity = Swift.max(cocoa.count, capacity) self = .native(_NativeSet(cocoa, capacity: capacity)) -#endif + return } +#endif + let isUnique = isUniquelyReferenced() + asNative.reserveCapacity(capacity, isUnique: isUnique) } /// The number of elements that can be stored without expanding the current @@ -127,15 +136,12 @@ extension Set._Variant { /// at which adding any more elements will exceed the load factor. @inlinable internal var capacity: Int { - switch self { - case .native: - return asNative.capacity #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return cocoa.count -#endif + guard isNative else { + return asCocoa.count } +#endif + return asNative.capacity } } @@ -145,41 +151,32 @@ extension Set._Variant: _SetBuffer { @inlinable internal var startIndex: Index { - switch self { - case .native(let native): - return native.startIndex #if _runtime(_ObjC) - case .cocoa(let cocoaSet): - cocoaPath() - return Index(_cocoa: cocoaSet.startIndex) -#endif + guard isNative else { + return Index(_cocoa: asCocoa.startIndex) } +#endif + return asNative.startIndex } @inlinable internal var endIndex: Index { - switch self { - case .native(let native): - return native.endIndex #if _runtime(_ObjC) - case .cocoa(let cocoaSet): - cocoaPath() - return Index(_cocoa: cocoaSet.endIndex) -#endif + guard isNative else { + return Index(_cocoa: asCocoa.endIndex) } +#endif + return asNative.endIndex } @inlinable internal func index(after index: Index) -> Index { - switch self { - case .native(let native): - return native.index(after: index) #if _runtime(_ObjC) - case .cocoa(let cocoaSet): - cocoaPath() - return Index(_cocoa: cocoaSet.index(after: index._asCocoa)) -#endif + guard isNative else { + return Index(_cocoa: asCocoa.index(after: index._asCocoa)) } +#endif + return asNative.index(after: index) } @inlinable @@ -199,102 +196,78 @@ extension Set._Variant: _SetBuffer { @inlinable @inline(__always) internal func index(for element: Element) -> Index? { - switch self { - case .native(let native): - return native.index(for: element) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { let cocoaElement = _bridgeAnythingToObjectiveC(element) - guard let index = cocoa.index(for: cocoaElement) else { return nil } + guard let index = asCocoa.index(for: cocoaElement) else { return nil } return Index(_cocoa: index) -#endif } +#endif + return asNative.index(for: element) } @inlinable internal var count: Int { @inline(__always) get { - switch self { - case .native(let native): - return native.count #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return cocoa.count -#endif + guard isNative else { + return asCocoa.count } +#endif + return asNative.count } } @inlinable @inline(__always) internal func contains(_ member: Element) -> Bool { - switch self { - case .native(let native): - return native.contains(member) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return cocoa.contains(_bridgeAnythingToObjectiveC(member)) -#endif + guard isNative else { + return asCocoa.contains(_bridgeAnythingToObjectiveC(member)) } +#endif + return asNative.contains(member) } @inlinable @inline(__always) internal func element(at index: Index) -> Element { - switch self { - case .native(let native): - return native.element(at: index) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - let cocoaMember = cocoa.element(at: index._asCocoa) + guard isNative else { + let cocoaMember = asCocoa.element(at: index._asCocoa) return _forceBridgeFromObjectiveC(cocoaMember, Element.self) -#endif } +#endif + return asNative.element(at: index) } } extension Set._Variant { @inlinable internal mutating func update(with value: __owned Element) -> Element? { - switch self { - case .native: - let isUnique = self.isUniquelyReferenced() - return asNative.update(with: value, isUnique: isUnique) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { // Make sure we have space for an extra element. - var native = _NativeSet(cocoa, capacity: cocoa.count + 1) + var native = _NativeSet(asCocoa, capacity: asCocoa.count + 1) let old = native.update(with: value, isUnique: true) self = .native(native) return old -#endif } +#endif + let isUnique = self.isUniquelyReferenced() + return asNative.update(with: value, isUnique: isUnique) } @inlinable internal mutating func insert( _ element: __owned Element ) -> (inserted: Bool, memberAfterInsert: Element) { - switch self { - case .native: - let (bucket, found) = asNative.find(element) - if found { - return (false, asNative.uncheckedElement(at: bucket)) - } - let isUnique = self.isUniquelyReferenced() - asNative.insertNew(element, at: bucket, isUnique: isUnique) - return (true, element) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { // Make sure we have space for an extra element. let cocoaMember = _bridgeAnythingToObjectiveC(element) + let cocoa = asCocoa if let m = cocoa.member(for: cocoaMember) { return (false, _forceBridgeFromObjectiveC(m, Element.self)) } @@ -302,47 +275,50 @@ extension Set._Variant { native.insertNew(element, isUnique: true) self = .native(native) return (true, element) + } #endif + let (bucket, found) = asNative.find(element) + if found { + return (false, asNative.uncheckedElement(at: bucket)) } + let isUnique = self.isUniquelyReferenced() + asNative.insertNew(element, at: bucket, isUnique: isUnique) + return (true, element) } @inlinable @discardableResult internal mutating func remove(at index: Index) -> Element { - switch self { - case .native: - let isUnique = isUniquelyReferenced() - let bucket = asNative.validatedBucket(for: index) - return asNative.uncheckedRemove(at: bucket, isUnique: isUnique) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { // We have to migrate the data first. But after we do so, the Cocoa // index becomes useless, so get the element first. + let cocoa = asCocoa let cocoaMember = cocoa.member(for: index._asCocoa) let nativeMember = _forceBridgeFromObjectiveC(cocoaMember, Element.self) return _migrateToNative(cocoa, removing: nativeMember) -#endif } +#endif + let isUnique = isUniquelyReferenced() + let bucket = asNative.validatedBucket(for: index) + return asNative.uncheckedRemove(at: bucket, isUnique: isUnique) } @inlinable @discardableResult internal mutating func remove(_ member: Element) -> Element? { - switch self { - case .native: - let (bucket, found) = asNative.find(member) - guard found else { return nil } - let isUnique = isUniquelyReferenced() - return asNative.uncheckedRemove(at: bucket, isUnique: isUnique) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { + let cocoa = asCocoa let cocoaMember = _bridgeAnythingToObjectiveC(member) guard cocoa.contains(cocoaMember) else { return nil } return _migrateToNative(cocoa, removing: member) -#endif } +#endif + let (bucket, found) = asNative.find(member) + guard found else { return nil } + let isUnique = isUniquelyReferenced() + return asNative.uncheckedRemove(at: bucket, isUnique: isUnique) } #if _runtime(_ObjC) @@ -371,16 +347,14 @@ extension Set._Variant { } guard count > 0 else { return } - switch self { - case .native: - let isUnique = isUniquelyReferenced() - asNative.removeAll(isUnique: isUnique) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - self = .native(_NativeSet(capacity: cocoa.count)) -#endif + guard isNative else { + self = .native(_NativeSet(capacity: asCocoa.count)) + return } +#endif + let isUnique = isUniquelyReferenced() + asNative.removeAll(isUnique: isUnique) } } @@ -391,15 +365,12 @@ extension Set._Variant { @inlinable @inline(__always) internal __consuming func makeIterator() -> Set.Iterator { - switch self { - case .native(let native): - return Set.Iterator(_native: native.makeIterator()) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() - return Set.Iterator(_cocoa: cocoa.makeIterator()) -#endif + guard isNative else { + return Set.Iterator(_cocoa: asCocoa.makeIterator()) } +#endif + return Set.Iterator(_native: asNative.makeIterator()) } } From 44d7abbdcc8c63c463c7316e537bf892a5a0d020 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Mon, 1 Oct 2018 16:56:43 +0100 Subject: [PATCH 2/3] [stdlib] Try extracting isNative checks to isolated blocks --- stdlib/public/core/Dictionary.swift | 12 +++- stdlib/public/core/DictionaryVariant.swift | 68 +++++++++++++++++----- 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index b89b736a7b81..f907479d33ea 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -1514,7 +1514,9 @@ extension Dictionary { public mutating func swapAt(_ i: Index, _ j: Index) { guard i != j else { return } #if _runtime(_ObjC) - if !_variant.isNative { + let isNative: Bool + do { isNative = _variant.isNative } + if !isNative { _variant = .native(_NativeDictionary(_variant.asCocoa)) } #endif @@ -1941,7 +1943,9 @@ extension Dictionary.Index: Hashable { public // FIXME(cocoa-index): Make inlinable func hash(into hasher: inout Hasher) { #if _runtime(_ObjC) - guard _isNative else { + let isNative: Bool + do { isNative = _isNative } + guard isNative else { hasher.combine(1 as UInt8) hasher.combine(_asCocoa.storage.currentKeyIndex) return @@ -2069,7 +2073,9 @@ extension Dictionary.Iterator: IteratorProtocol { @inline(__always) public mutating func next() -> (key: Key, value: Value)? { #if _runtime(_ObjC) - guard _isNative else { + let isNative: Bool + do { isNative = _isNative } + guard isNative else { if let (cocoaKey, cocoaValue) = _asCocoa.next() { let nativeKey = _forceBridgeFromObjectiveC(cocoaKey, Key.self) let nativeValue = _forceBridgeFromObjectiveC(cocoaValue, Value.self) diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index 8f3d0f67dac0..08338ac3141e 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -56,11 +56,24 @@ extension Dictionary._Variant { _conditionallyUnreachable() } } + + @usableFromInline @_transparent + internal var isNative: Bool { + switch self { + case .native: + return true + case .cocoa: + cocoaPath() + return false + } + } #endif @inlinable internal mutating func isUniquelyReferenced() -> Bool { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { // Don't consider Cocoa a buffer mutable, even if it is mutable and it is // uniquely referenced. @@ -73,19 +86,6 @@ extension Dictionary._Variant { return _isUnique_native(&self) } -#if _runtime(_ObjC) - @usableFromInline @_transparent - internal var isNative: Bool { - switch self { - case .native: - return true - case .cocoa: - cocoaPath() - return false - } - } -#endif - @inlinable internal var asNative: _NativeDictionary { @inline(__always) @@ -121,6 +121,8 @@ extension Dictionary._Variant { /// without reallocating additional storage. internal mutating func reserveCapacity(_ capacity: Int) { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { let cocoa = asCocoa let capacity = Swift.max(cocoa.count, capacity) @@ -142,6 +144,8 @@ extension Dictionary._Variant { @inlinable internal var capacity: Int { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { return asCocoa.count } @@ -159,6 +163,8 @@ extension Dictionary._Variant: _DictionaryBuffer { @inlinable internal var startIndex: Index { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { return Index(_cocoa: asCocoa.startIndex) } @@ -169,6 +175,8 @@ extension Dictionary._Variant: _DictionaryBuffer { @inlinable internal var endIndex: Index { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { return Index(_cocoa: asCocoa.endIndex) } @@ -179,6 +187,8 @@ extension Dictionary._Variant: _DictionaryBuffer { @inlinable internal func index(after index: Index) -> Index { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { return Index(_cocoa: asCocoa.index(after: index._asCocoa)) } @@ -204,6 +214,8 @@ extension Dictionary._Variant: _DictionaryBuffer { @inline(__always) internal func index(forKey key: Key) -> Index? { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { let cocoaKey = _bridgeAnythingToObjectiveC(key) guard let index = asCocoa.index(forKey: cocoaKey) else { return nil } @@ -218,7 +230,9 @@ extension Dictionary._Variant: _DictionaryBuffer { @inline(__always) get { #if _runtime(_ObjC) - guard isNative else { + let isNative: Bool + do { isNative = self.isNative } + guard isNative else { return asCocoa.count } #endif @@ -230,6 +244,8 @@ extension Dictionary._Variant: _DictionaryBuffer { @inline(__always) func contains(_ key: Key) -> Bool { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { let cocoaKey = _bridgeAnythingToObjectiveC(key) return asCocoa.contains(cocoaKey) @@ -242,6 +258,8 @@ extension Dictionary._Variant: _DictionaryBuffer { @inline(__always) func lookup(_ key: Key) -> Value? { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { let cocoaKey = _bridgeAnythingToObjectiveC(key) guard let cocoaValue = asCocoa.lookup(cocoaKey) else { return nil } @@ -255,6 +273,8 @@ extension Dictionary._Variant: _DictionaryBuffer { @inline(__always) func lookup(_ index: Index) -> (key: Key, value: Value) { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { let (cocoaKey, cocoaValue) = asCocoa.lookup(index._asCocoa) let nativeKey = _forceBridgeFromObjectiveC(cocoaKey, Key.self) @@ -269,6 +289,8 @@ extension Dictionary._Variant: _DictionaryBuffer { @inline(__always) func key(at index: Index) -> Key { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { let cocoaKey = asCocoa.key(at: index._asCocoa) return _forceBridgeFromObjectiveC(cocoaKey, Key.self) @@ -281,6 +303,8 @@ extension Dictionary._Variant: _DictionaryBuffer { @inline(__always) func value(at index: Index) -> Value { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { let cocoaValue = asCocoa.value(at: index._asCocoa) return _forceBridgeFromObjectiveC(cocoaValue, Value.self) @@ -302,6 +326,8 @@ extension Dictionary._Variant { _ key: Key ) -> (bucket: _NativeDictionary.Bucket, found: Bool) { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { let cocoa = asCocoa var native = _NativeDictionary( @@ -319,6 +345,8 @@ extension Dictionary._Variant { @inline(__always) internal mutating func ensureUniqueNative() -> _NativeDictionary { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { cocoaPath() let native = _NativeDictionary(asCocoa) @@ -339,6 +367,8 @@ extension Dictionary._Variant { forKey key: Key ) -> Value? { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { // Make sure we have space for an extra element. let cocoa = asCocoa @@ -381,6 +411,8 @@ extension Dictionary._Variant { @inlinable internal mutating func removeValue(forKey key: Key) -> Value? { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { let cocoaKey = _bridgeAnythingToObjectiveC(key) let cocoa = asCocoa @@ -408,6 +440,8 @@ extension Dictionary._Variant { guard count > 0 else { return } #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { self = .native(_NativeDictionary(capacity: asCocoa.count)) return @@ -426,6 +460,8 @@ extension Dictionary._Variant { @inline(__always) __consuming internal func makeIterator() -> Dictionary.Iterator { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { return Dictionary.Iterator(_cocoa: asCocoa.makeIterator()) } @@ -440,6 +476,8 @@ extension Dictionary._Variant { _ transform: (Value) throws -> T ) rethrows -> _NativeDictionary { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { return try asCocoa.mapValues(transform) } @@ -453,6 +491,8 @@ extension Dictionary._Variant { uniquingKeysWith combine: (Value, Value) throws -> Value ) rethrows where S.Element == (Key, Value) { #if _runtime(_ObjC) + let isNative: Bool + do { isNative = self.isNative } guard isNative else { var native = _NativeDictionary(asCocoa) try native.merge( From 755d8cc5f72f519f13ea6c0d366032bf9ba1fa19 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Mon, 1 Oct 2018 18:08:17 +0100 Subject: [PATCH 3/3] [stdlib] Dictionary.Values.swapAt: Fix uniqueness check --- stdlib/public/core/Dictionary.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index f907479d33ea..600768baf4fe 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -1520,10 +1520,9 @@ extension Dictionary { _variant = .native(_NativeDictionary(_variant.asCocoa)) } #endif - let native = _variant.asNative - let a = native.validatedBucket(for: i) - let b = native.validatedBucket(for: j) let isUnique = _variant.isUniquelyReferenced() + let a = _variant.asNative.validatedBucket(for: i) + let b = _variant.asNative.validatedBucket(for: j) _variant.asNative.swapValuesAt(a, b, isUnique: isUnique) } }