From e24f2ba6e5a0e094401e324fe2920463f985ef07 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Fri, 28 Sep 2018 19:36:25 +0100 Subject: [PATCH 01/18] [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 843b616de7b4d..f2829a05a53b9 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -1517,20 +1517,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) } @@ -1860,6 +1854,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 @@ -1958,19 +1963,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 } } @@ -2034,6 +2037,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 @@ -2052,6 +2066,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 { @@ -2062,20 +2091,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 1edaa80d46e48..b0e9236419991 100644 --- a/stdlib/public/core/DictionaryBridging.swift +++ b/stdlib/public/core/DictionaryBridging.swift @@ -805,12 +805,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 50403ee41c7e9..38f83b940772f 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 ee5ea7d82123a..8f3d0f67dac0c 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 a44968030e8d0..afe2a65a826bb 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 5ebeba3d7bd45..37566ac9c415e 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 6541ba75d28f0..8bd67031371b1 100644 --- a/stdlib/public/core/Set.swift +++ b/stdlib/public/core/Set.swift @@ -1361,6 +1361,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 { @@ -1449,19 +1462,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 } } @@ -1526,6 +1537,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 { @@ -1542,6 +1566,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 { @@ -1553,19 +1591,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 06cdef01d569b..d793b971abdba 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 787c51a420717..6562738c228b0 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 33ce0d85cd88b..2ee712554c9f7 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 64d19f44db6f94a314386dcc5831b693c1d52b06 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 3 Oct 2018 17:35:34 +0100 Subject: [PATCH 02/18] [stdlib] Set, Dictionary: Switch to using a _BridgeObject instead of an enum --- stdlib/public/core/Dictionary.swift | 76 ++++++++------- stdlib/public/core/DictionaryVariant.swift | 104 +++++++-------------- stdlib/public/core/Set.swift | 54 +++++------ stdlib/public/core/SetVariant.swift | 93 +++++++----------- 4 files changed, 133 insertions(+), 194 deletions(-) diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index f2829a05a53b9..2557c0cb9fffc 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -396,13 +396,13 @@ public struct Dictionary { @inlinable internal init(_native: __owned _NativeDictionary) { - _variant = .native(_native) + _variant = _Variant(native: _native) } #if _runtime(_ObjC) @inlinable internal init(_cocoa: __owned _CocoaDictionary) { - _variant = .cocoa(_cocoa) + _variant = _Variant(cocoa: _cocoa) } /// Private initializer used for bridging. @@ -444,7 +444,7 @@ public struct Dictionary { /// reallocating its storage buffer. public // FIXME(reserveCapacity): Should be inlinable init(minimumCapacity: Int) { - _variant = .native(_NativeDictionary(capacity: minimumCapacity)) + _variant = _Variant(native: _NativeDictionary(capacity: minimumCapacity)) } /// Creates a new dictionary from the key-value pairs in the given sequence. @@ -1317,7 +1317,7 @@ extension Dictionary { } _modify { var values = Values(_dictionary: self) - _variant = .native(_NativeDictionary()) + _variant = _Variant(native: _NativeDictionary()) yield &values self._variant = values._variant } @@ -1404,10 +1404,22 @@ extension Dictionary { @inlinable public static func ==(lhs: Keys, rhs: Keys) -> Bool { // Equal if the two dictionaries share storage. - if case (.native(let ln), .native(let rn)) = (lhs._variant, rhs._variant), - ln._storage === rn._storage { + if + lhs._variant.isNative, + rhs._variant.isNative, + lhs._variant.asNative._storage === rhs._variant.asNative._storage + { return true } +#if _runtime(_ObjC) + if + !lhs._variant.isNative, + !rhs._variant.isNative, + lhs._variant.asCocoa.object === rhs._variant.asCocoa.object + { + return true + } +#endif // Not equal if the dictionaries are different sizes. if lhs.count != rhs.count { @@ -1519,7 +1531,7 @@ extension Dictionary { guard i != j else { return } #if _runtime(_ObjC) if !_variant.isNative { - _variant = .native(_NativeDictionary(_variant.asCocoa)) + _variant = .init(native: _NativeDictionary(_variant.asCocoa)) } #endif let native = _variant.asNative @@ -1600,48 +1612,44 @@ extension Dictionary.Values { extension Dictionary: Equatable where Value: Equatable { @inlinable public static func == (lhs: [Key: Value], rhs: [Key: Value]) -> Bool { - switch (lhs._variant, rhs._variant) { - case (.native(let lhsNative), .native(let rhsNative)): + switch (lhs._variant.isNative, rhs._variant.isNative) { + case (true, true): + let lhs = lhs._variant.asNative + let rhs = rhs._variant.asNative - if lhsNative._storage === rhsNative._storage { - return true - } - - if lhsNative.count != rhsNative.count { - return false - } + if lhs._storage === rhs._storage { return true } + if lhs.count != rhs.count { return false } for (k, v) in lhs { - let (bucket, found) = rhsNative.find(k) - guard found, rhsNative.uncheckedValue(at: bucket) == v else { - return false - } + let (bucket, found) = rhs.find(k) + guard found, rhs.uncheckedValue(at: bucket) == v else { return false } } return true - #if _runtime(_ObjC) - case (.cocoa(let lhsCocoa), .cocoa(let rhsCocoa)): - return lhsCocoa == rhsCocoa +#if _runtime(_ObjC) + case (false, false): + return lhs._variant.asCocoa == rhs._variant.asCocoa - case (.native(let lhsNative), .cocoa(let rhsCocoa)): - if lhsNative.count != rhsCocoa.count { - return false - } + case (true, false): + let lhs = lhs._variant.asNative + let rhs = rhs._variant.asCocoa + + if lhs.count != rhs.count { return false } - defer { _fixLifetime(lhsNative) } - for bucket in lhsNative.hashTable { - let key = lhsNative.uncheckedKey(at: bucket) - let value = lhsNative.uncheckedValue(at: bucket) + defer { _fixLifetime(lhs) } + for bucket in lhs.hashTable { + let key = lhs.uncheckedKey(at: bucket) + let value = lhs.uncheckedValue(at: bucket) guard - let rhsValue = rhsCocoa.lookup(_bridgeAnythingToObjectiveC(key)), - value == _forceBridgeFromObjectiveC(rhsValue, Value.self) + let cocoaValue = rhs.lookup(_bridgeAnythingToObjectiveC(key)), + value == _forceBridgeFromObjectiveC(cocoaValue, Value.self) else { return false } } return true - case (.cocoa, .native): + case (false, true): return rhs == lhs #endif } diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index 8f3d0f67dac0c..a0bd30849b685 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -32,12 +32,22 @@ internal protocol _DictionaryBuffer { extension Dictionary { @usableFromInline - @_frozen - internal enum _Variant { - case native(_NativeDictionary) -#if _runtime(_ObjC) - case cocoa(_CocoaDictionary) -#endif + @_fixed_layout + internal struct _Variant { + @usableFromInline + internal var object: _BridgeStorage<_RawDictionaryStorage, _NSDictionary> + + @inlinable + @inline(__always) + init(native: __owned _NativeDictionary) { + self.object = _BridgeStorage(native: native._storage) + } + + @inlinable + @inline(__always) + init(cocoa: __owned _CocoaDictionary) { + self.object = _BridgeStorage(objC: cocoa.object) + } } } @@ -47,73 +57,34 @@ extension Dictionary._Variant { internal var guaranteedNative: Bool { return _canBeClass(Key.self) == 0 || _canBeClass(Value.self) == 0 } - - // Allow the optimizer to consider the surrounding code unreachable if Element - // is guaranteed to be native. - @usableFromInline @_transparent - internal func cocoaPath() { - if guaranteedNative { - _conditionallyUnreachable() - } - } #endif @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) + return object.isUniquelyReferencedNative() } #if _runtime(_ObjC) @usableFromInline @_transparent internal var isNative: Bool { - switch self { - case .native: - return true - case .cocoa: - cocoaPath() - return false - } + return guaranteedNative || object.isNative } #endif - @inlinable + @usableFromInline @_transparent internal var asNative: _NativeDictionary { - @inline(__always) get { - switch self { - case .native(let native): - return native -#if _runtime(_ObjC) - case .cocoa: - _sanityCheckFailure("internal error: not backed by native buffer") -#endif - } + return _NativeDictionary(object.nativeInstance) } - @inline(__always) set { - self = .native(newValue) + self = .init(native: newValue) } } #if _runtime(_ObjC) @inlinable internal var asCocoa: _CocoaDictionary { - switch self { - case .native: - _sanityCheckFailure("internal error: not backed by NSDictionary") - case .cocoa(let cocoa): - return cocoa - } + return _CocoaDictionary(object.objCInstance) } #endif @@ -124,7 +95,7 @@ extension Dictionary._Variant { guard isNative else { let cocoa = asCocoa let capacity = Swift.max(cocoa.count, capacity) - self = .native(_NativeDictionary(cocoa, capacity: capacity)) + self = .init(native: _NativeDictionary(cocoa, capacity: capacity)) return } #endif @@ -188,16 +159,14 @@ extension Dictionary._Variant: _DictionaryBuffer { @inlinable internal func formIndex(after index: inout Index) { - switch self { - case .native(let native): - index = native.index(after: index) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { let isUnique = index._isUniquelyReferenced() - cocoa.formIndex(after: &index._asCocoa, isUnique: isUnique) -#endif + asCocoa.formIndex(after: &index._asCocoa, isUnique: isUnique) + return } +#endif + index = asNative.index(after: index) } @inlinable @@ -307,7 +276,7 @@ extension Dictionary._Variant { var native = _NativeDictionary( cocoa, capacity: cocoa.count + 1) let result = native.mutatingFind(key, isUnique: true) - self = .native(native) + self = .init(native: native) return result } #endif @@ -320,9 +289,8 @@ extension Dictionary._Variant { internal mutating func ensureUniqueNative() -> _NativeDictionary { #if _runtime(_ObjC) guard isNative else { - cocoaPath() let native = _NativeDictionary(asCocoa) - self = .native(native) + self = .init(native: native) return native } #endif @@ -346,7 +314,7 @@ extension Dictionary._Variant { cocoa, capacity: cocoa.count + 1) let result = native.updateValue(value, forKey: key, isUnique: true) - self = .native(native) + self = .init(native: native) return result } #endif @@ -360,7 +328,7 @@ extension Dictionary._Variant { if !isNative { // Make sure we have space for an extra element. let cocoa = asCocoa - self = .native(_NativeDictionary( + self = .init(native: _NativeDictionary( cocoa, capacity: cocoa.count + 1)) } @@ -389,7 +357,7 @@ extension Dictionary._Variant { let (bucket, found) = native.find(key) _precondition(found, "Bridging did not preserve equality") let old = native.uncheckedRemove(at: bucket, isUnique: true).value - self = .native(native) + self = .init(native: native) return old } #endif @@ -402,14 +370,14 @@ extension Dictionary._Variant { @inlinable internal mutating func removeAll(keepingCapacity keepCapacity: Bool) { if !keepCapacity { - self = .native(_NativeDictionary()) + self = .init(native: _NativeDictionary()) return } guard count > 0 else { return } #if _runtime(_ObjC) guard isNative else { - self = .native(_NativeDictionary(capacity: asCocoa.count)) + self = .init(native: _NativeDictionary(capacity: asCocoa.count)) return } #endif @@ -459,7 +427,7 @@ extension Dictionary._Variant { keysAndValues, isUnique: true, uniquingKeysWith: combine) - self = .native(native) + self = .init(native: native) return } #endif diff --git a/stdlib/public/core/Set.swift b/stdlib/public/core/Set.swift index 8bd67031371b1..371dc6cbbef2f 100644 --- a/stdlib/public/core/Set.swift +++ b/stdlib/public/core/Set.swift @@ -163,19 +163,19 @@ public struct Set { /// storage buffer. public // FIXME(reserveCapacity): Should be inlinable init(minimumCapacity: Int) { - _variant = .native(_NativeSet(capacity: minimumCapacity)) + _variant = _Variant(native: _NativeSet(capacity: minimumCapacity)) } /// Private initializer. @inlinable internal init(_native: __owned _NativeSet) { - _variant = .native(_native) + _variant = _Variant(native: _native) } #if _runtime(_ObjC) @inlinable internal init(_cocoa: __owned _CocoaSet) { - _variant = .cocoa(_cocoa) + _variant = _Variant(cocoa: _cocoa) } /// Private initializer used for bridging. @@ -421,45 +421,37 @@ extension Set: Equatable { /// `false`. @inlinable public static func == (lhs: Set, rhs: Set) -> Bool { - switch (lhs._variant, rhs._variant) { - case (.native(let lhsNative), .native(let rhsNative)): + switch (lhs._variant.isNative, rhs._variant.isNative) { + case (true, true): + let lhs = lhs._variant.asNative + let rhs = rhs._variant.asNative - if lhsNative._storage === rhsNative._storage { - return true - } + if lhs._storage === rhs._storage { return true } + if lhs.count != rhs.count { return false } - if lhsNative.count != rhsNative.count { - return false - } - - for member in lhsNative { - guard rhsNative.find(member).found else { - return false - } + for member in lhs { + guard rhs.find(member).found else { return false } } return true +#if _runtime(_ObjC) + case (false, false): + return lhs._variant.asCocoa == rhs._variant.asCocoa - #if _runtime(_ObjC) - case (.cocoa(let lhsCocoa), .cocoa(let rhsCocoa)): - return lhsCocoa == rhsCocoa + case (true, false): + let lhs = lhs._variant.asNative + let rhs = rhs._variant.asCocoa - case (.native(let lhsNative), .cocoa(let rhsCocoa)): - if lhsNative.count != rhsCocoa.count { - return false - } + if lhs.count != rhs.count { return false } - defer { _fixLifetime(lhsNative) } - for bucket in lhsNative.hashTable { - let key = lhsNative.uncheckedElement(at: bucket) + defer { _fixLifetime(lhs) } + for bucket in lhs.hashTable { + let key = lhs.uncheckedElement(at: bucket) let bridgedKey = _bridgeAnythingToObjectiveC(key) - if rhsCocoa.contains(bridgedKey) { - continue - } - return false + guard rhs.contains(bridgedKey) else { return false } } return true - case (.cocoa, .native): + case (false, true): return rhs == lhs #endif } diff --git a/stdlib/public/core/SetVariant.swift b/stdlib/public/core/SetVariant.swift index 2ee712554c9f7..643a52e3f7692 100644 --- a/stdlib/public/core/SetVariant.swift +++ b/stdlib/public/core/SetVariant.swift @@ -28,12 +28,22 @@ internal protocol _SetBuffer { extension Set { @usableFromInline - @_frozen - internal enum _Variant { - case native(_NativeSet) -#if _runtime(_ObjC) - case cocoa(_CocoaSet) -#endif + @_fixed_layout + internal struct _Variant { + @usableFromInline + internal var object: _BridgeStorage<_RawSetStorage, _NSSet> + + @inlinable + @inline(__always) + init(native: __owned _NativeSet) { + self.object = _BridgeStorage(native: native._storage) + } + + @inlinable + @inline(__always) + init(cocoa: __owned _CocoaSet) { + self.object = _BridgeStorage(objC: cocoa.object) + } } } @@ -44,71 +54,34 @@ extension Set._Variant { internal var guaranteedNative: Bool { return _canBeClass(Element.self) == 0 } - - /// Allow the optimizer to consider the surrounding code unreachable if - /// Set is guaranteed to be native. - @usableFromInline - @_transparent - internal func cocoaPath() { - if guaranteedNative { - _conditionallyUnreachable() - } - } #endif @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) + return object.isUniquelyReferencedNative() } #if _runtime(_ObjC) @usableFromInline @_transparent internal var isNative: Bool { - switch self { - case .native: - return true - case .cocoa: - cocoaPath() - return false - } + return guaranteedNative || object.isNative } #endif @usableFromInline @_transparent internal var asNative: _NativeSet { get { - switch self { - case .native(let nativeSet): - return nativeSet -#if _runtime(_ObjC) - case .cocoa: - _sanityCheckFailure("internal error: not backed by native buffer") -#endif - } + return _NativeSet(object.nativeInstance) } set { - self = .native(newValue) + self = .init(native: newValue) } } #if _runtime(_ObjC) @inlinable internal var asCocoa: _CocoaSet { - switch self { - case .native: - _sanityCheckFailure("internal error: not backed by NSSet") - case .cocoa(let cocoa): - return cocoa - } + return _CocoaSet(object.objCInstance) } #endif @@ -119,7 +92,7 @@ extension Set._Variant { guard isNative else { let cocoa = asCocoa let capacity = Swift.max(cocoa.count, capacity) - self = .native(_NativeSet(cocoa, capacity: capacity)) + self = .init(native: _NativeSet(cocoa, capacity: capacity)) return } #endif @@ -181,16 +154,14 @@ extension Set._Variant: _SetBuffer { @inlinable internal func formIndex(after index: inout Index) { - switch self { - case .native(let native): - index = native.index(after: index) #if _runtime(_ObjC) - case .cocoa(let cocoa): - cocoaPath() + guard isNative else { let isUnique = index._isUniquelyReferenced() - cocoa.formIndex(after: &index._asCocoa, isUnique: isUnique) -#endif + asCocoa.formIndex(after: &index._asCocoa, isUnique: isUnique) + return } +#endif + index = asNative.index(after: index) } @inlinable @@ -251,7 +222,7 @@ extension Set._Variant { // Make sure we have space for an extra element. var native = _NativeSet(asCocoa, capacity: asCocoa.count + 1) let old = native.update(with: value, isUnique: true) - self = .native(native) + self = .init(native: native) return old } #endif @@ -273,7 +244,7 @@ extension Set._Variant { } var native = _NativeSet(cocoa, capacity: cocoa.count + 1) native.insertNew(element, isUnique: true) - self = .native(native) + self = .init(native: native) return (true, element) } #endif @@ -334,7 +305,7 @@ extension Set._Variant { _precondition(found, "Bridging did not preserve equality") let old = native.uncheckedRemove(at: bucket, isUnique: true) _precondition(member == old, "Bridging did not preserve equality") - self = .native(native) + self = .init(native: native) return old } #endif @@ -342,14 +313,14 @@ extension Set._Variant { @inlinable internal mutating func removeAll(keepingCapacity keepCapacity: Bool) { if !keepCapacity { - self = .native(_NativeSet()) + self = .init(native: _NativeSet()) return } guard count > 0 else { return } #if _runtime(_ObjC) guard isNative else { - self = .native(_NativeSet(capacity: asCocoa.count)) + self = .init(native: _NativeSet(capacity: asCocoa.count)) return } #endif From 363b74d1f29ff4e8ee9b6a0db94b05070a7cfc42 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 3 Oct 2018 17:36:10 +0100 Subject: [PATCH 03/18] [stdlib] Set, Dictionary: Implement modify accessor for _variant.asNative --- stdlib/public/core/BridgeStorage.swift | 8 +++++++- stdlib/public/core/DictionaryVariant.swift | 6 ++++++ stdlib/public/core/SetVariant.swift | 6 ++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/BridgeStorage.swift b/stdlib/public/core/BridgeStorage.swift index df6bc83ef5358..3fe56a522cf7b 100644 --- a/stdlib/public/core/BridgeStorage.swift +++ b/stdlib/public/core/BridgeStorage.swift @@ -60,7 +60,13 @@ struct _BridgeStorage< _sanityCheck(_usesNativeSwiftReferenceCounting(NativeClass.self)) rawValue = Builtin.reinterpretCast(native) } - + + @inlinable + @inline(__always) + internal init(taggedPayload: UInt) { + rawValue = _bridgeObject(taggingPayload: taggedPayload) + } + @inlinable // FIXME(sil-serialize-all) public // @testable var spareBits: Int { diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index a0bd30849b685..ec621acd2c319 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -79,6 +79,12 @@ extension Dictionary._Variant { set { self = .init(native: newValue) } + _modify { + var native = _NativeDictionary(object.nativeInstance) + object = .init(taggedPayload: 0) + yield &native + object = .init(native: native._storage) + } } #if _runtime(_ObjC) diff --git a/stdlib/public/core/SetVariant.swift b/stdlib/public/core/SetVariant.swift index 643a52e3f7692..8091500b1b6cd 100644 --- a/stdlib/public/core/SetVariant.swift +++ b/stdlib/public/core/SetVariant.swift @@ -76,6 +76,12 @@ extension Set._Variant { set { self = .init(native: newValue) } + _modify { + var native = _NativeSet(object.nativeInstance) + object = .init(taggedPayload: 0) + yield &native + object = .init(native: native._storage) + } } #if _runtime(_ObjC) From 3c60042b6cdec9f18cdee5c6184123a2395c934e Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 3 Oct 2018 18:37:42 +0100 Subject: [PATCH 04/18] [stdlib] Fix building on platforms without an Objective-C runtime --- stdlib/public/core/DictionaryVariant.swift | 7 +++++++ stdlib/public/core/SetVariant.swift | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index ec621acd2c319..c20015345ced6 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -34,8 +34,13 @@ extension Dictionary { @usableFromInline @_fixed_layout internal struct _Variant { +#if _runtime(_ObjC) @usableFromInline internal var object: _BridgeStorage<_RawDictionaryStorage, _NSDictionary> +#else + @usableFromInline + internal var object: _BridgeStorage<_RawDictionaryStorage, AnyObject> +#endif @inlinable @inline(__always) @@ -43,11 +48,13 @@ extension Dictionary { self.object = _BridgeStorage(native: native._storage) } +#if _runtime(_ObjC) @inlinable @inline(__always) init(cocoa: __owned _CocoaDictionary) { self.object = _BridgeStorage(objC: cocoa.object) } +#endif } } diff --git a/stdlib/public/core/SetVariant.swift b/stdlib/public/core/SetVariant.swift index 8091500b1b6cd..737f3e328660f 100644 --- a/stdlib/public/core/SetVariant.swift +++ b/stdlib/public/core/SetVariant.swift @@ -30,8 +30,13 @@ extension Set { @usableFromInline @_fixed_layout internal struct _Variant { +#if _runtime(_ObjC) @usableFromInline internal var object: _BridgeStorage<_RawSetStorage, _NSSet> +#else + @usableFromInline + internal var object: _BridgeStorage<_RawSetStorage, AnyObject> +#endif @inlinable @inline(__always) @@ -39,11 +44,13 @@ extension Set { self.object = _BridgeStorage(native: native._storage) } +#if _runtime(_ObjC) @inlinable @inline(__always) init(cocoa: __owned _CocoaSet) { self.object = _BridgeStorage(objC: cocoa.object) } +#endif } } From 12cad748d404bdf4c9a785ff03ac2016ff782c1f Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 3 Oct 2018 19:24:31 +0100 Subject: [PATCH 05/18] [stdlib] Dictionary.Values.swapAt: Fix uniqueness check --- stdlib/public/core/Dictionary.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index 2557c0cb9fffc..4c4718e1646c5 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -1534,10 +1534,10 @@ extension Dictionary { _variant = .init(native: _NativeDictionary(_variant.asCocoa)) } #endif + let isUnique = _variant.isUniquelyReferenced() 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) } } From 73771b1ca9b8901690cb93db21181f3001ea175f Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 3 Oct 2018 19:25:01 +0100 Subject: [PATCH 06/18] [stdlib] Rework Dictionary.values._modify --- stdlib/public/core/Dictionary.swift | 8 ++++++-- stdlib/public/core/DictionaryVariant.swift | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index 4c4718e1646c5..ddf28f91f6883 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -1316,8 +1316,12 @@ extension Dictionary { return Values(_dictionary: self) } _modify { - var values = Values(_dictionary: self) - _variant = _Variant(native: _NativeDictionary()) + var values: Values + do { + var temp = _Variant(dummy: ()) + swap(&temp, &_variant) + values = Values(_variant: temp) + } yield &values self._variant = values._variant } diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index c20015345ced6..de4d47d8ed5e7 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -48,6 +48,12 @@ extension Dictionary { self.object = _BridgeStorage(native: native._storage) } + @inlinable + @inline(__always) + init(dummy: Void) { + self.object = _BridgeStorage(taggedPayload: 0) + } + #if _runtime(_ObjC) @inlinable @inline(__always) @@ -88,7 +94,7 @@ extension Dictionary._Variant { } _modify { var native = _NativeDictionary(object.nativeInstance) - object = .init(taggedPayload: 0) + self = .init(dummy: ()) yield &native object = .init(native: native._storage) } From e112de3efae0d1aebdd8bdb7fd1a0741722a1079 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 3 Oct 2018 19:44:15 +0100 Subject: [PATCH 07/18] [test] Update Set/Dictionary tests --- test/stdlib/Inputs/DictionaryKeyValueTypesObjC.swift | 7 +------ validation-test/stdlib/Set.swift | 9 +++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/test/stdlib/Inputs/DictionaryKeyValueTypesObjC.swift b/test/stdlib/Inputs/DictionaryKeyValueTypesObjC.swift index 73bbff4f33a9b..a13fb2bb84938 100644 --- a/test/stdlib/Inputs/DictionaryKeyValueTypesObjC.swift +++ b/test/stdlib/Inputs/DictionaryKeyValueTypesObjC.swift @@ -20,12 +20,7 @@ public func convertNSDictionaryToDictionary< func isNativeDictionary( _ d: Dictionary) -> Bool { - switch d._variant { - case .native: - return true - case .cocoa: - return false - } + return d._variant.isNative } func isCocoaDictionary( diff --git a/validation-test/stdlib/Set.swift b/validation-test/stdlib/Set.swift index 336370235ab01..115d5896e022b 100644 --- a/validation-test/stdlib/Set.swift +++ b/validation-test/stdlib/Set.swift @@ -110,14 +110,11 @@ func equalsUnordered(_ lhs: Set, _ rhs: Set) -> Bool { } func isNativeSet(_ s: Set) -> Bool { - switch s._variant { - case .native: - return true #if _runtime(_ObjC) - case .cocoa: - return false + return s._variant.isNative +#else + return true #endif - } } #if _runtime(_ObjC) From 3665294d1e813d35594d6bcdc0a61983caa6e0cd Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 3 Oct 2018 20:12:15 +0100 Subject: [PATCH 08/18] [test] Set and Dictionary now has an extra inhabitant --- validation-test/Reflection/reflect_Dictionary.swift | 2 +- validation-test/Reflection/reflect_Set.swift | 2 +- validation-test/Reflection/reflect_multiple_types.swift | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/validation-test/Reflection/reflect_Dictionary.swift b/validation-test/Reflection/reflect_Dictionary.swift index 769a9e1574557..3e79625b6ae87 100644 --- a/validation-test/Reflection/reflect_Dictionary.swift +++ b/validation-test/Reflection/reflect_Dictionary.swift @@ -28,7 +28,7 @@ reflect(object: obj) // CHECK-64: Type info: // CHECK-64: (class_instance size=24 alignment=8 stride=24 num_extra_inhabitants=0 // CHECK-64: (field name=t offset=16 -// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=1 // (unstable implementation details omitted) // CHECK-32: Reflecting an object. diff --git a/validation-test/Reflection/reflect_Set.swift b/validation-test/Reflection/reflect_Set.swift index 97b71418efa43..467525ae682c6 100644 --- a/validation-test/Reflection/reflect_Set.swift +++ b/validation-test/Reflection/reflect_Set.swift @@ -28,7 +28,7 @@ reflect(object: obj) // CHECK-64: Type info: // CHECK-64: (class_instance size=24 alignment=8 stride=24 num_extra_inhabitants=0 // CHECK-64: (field name=t offset=16 -// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 +// CHECK-64: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=1 // (unstable implementation details omitted) // CHECK-32: Reflecting an object. diff --git a/validation-test/Reflection/reflect_multiple_types.swift b/validation-test/Reflection/reflect_multiple_types.swift index f8050c93d5716..0bd12f0b238a7 100644 --- a/validation-test/Reflection/reflect_multiple_types.swift +++ b/validation-test/Reflection/reflect_multiple_types.swift @@ -132,7 +132,7 @@ reflect(object: obj) // CHECK-64-NEXT: (field name=large offset=0 // CHECK-64-NEXT: (reference kind=strong refcounting=native)))))) // CHECK-64-NEXT: (field name=t03 offset=40 -// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=1 // (unstable implementation details omitted) // CHECK-64: (field name=t04 offset=48 // CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 @@ -171,7 +171,7 @@ reflect(object: obj) // CHECK-64-NEXT: (field name=t14 offset=120 // CHECK-64-NEXT: (reference kind=strong refcounting=unknown)) // CHECK-64-NEXT: (field name=t15 offset=128 -// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 +// CHECK-64-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=1 // (unstable implementation details omitted) // CHECK-64: (field name=t16 offset=136 // CHECK-64-NEXT: (struct size=16 alignment=8 stride=16 num_extra_inhabitants=1 From 5c0d27036a0aa20ddbeed623f1385307e11be1fd Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 3 Oct 2018 20:13:11 +0100 Subject: [PATCH 09/18] [stdlib] Set._Variant: Add a dummy initializer for use in _modify Dictionary already does this. --- stdlib/public/core/SetVariant.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/SetVariant.swift b/stdlib/public/core/SetVariant.swift index 737f3e328660f..605b4904159f8 100644 --- a/stdlib/public/core/SetVariant.swift +++ b/stdlib/public/core/SetVariant.swift @@ -38,6 +38,12 @@ extension Set { internal var object: _BridgeStorage<_RawSetStorage, AnyObject> #endif + @inlinable + @inline(__always) + init(dummy: ()) { + self.object = _BridgeStorage(taggedPayload: 0) + } + @inlinable @inline(__always) init(native: __owned _NativeSet) { @@ -78,14 +84,14 @@ extension Set._Variant { @usableFromInline @_transparent internal var asNative: _NativeSet { get { - return _NativeSet(object.nativeInstance) + return _NativeSet(object.nativeInstance) } set { self = .init(native: newValue) } _modify { var native = _NativeSet(object.nativeInstance) - object = .init(taggedPayload: 0) + self = .init(dummy: ()) yield &native object = .init(native: native._storage) } From 697ad69d24aa0b5615cd521356683f468d321639 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 3 Oct 2018 21:08:55 +0100 Subject: [PATCH 10/18] [stdlib] Set, Dictionary: Reorganize equality checks to fix Linux builds --- stdlib/public/core/Dictionary.swift | 49 ++++++--------------- stdlib/public/core/DictionaryBridging.swift | 9 ++-- stdlib/public/core/NativeDictionary.swift | 37 ++++++++++++++++ stdlib/public/core/NativeSet.swift | 29 ++++++++++++ stdlib/public/core/Set.swift | 36 ++++----------- stdlib/public/core/SetBridging.swift | 6 +-- 6 files changed, 93 insertions(+), 73 deletions(-) diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index ddf28f91f6883..8596710f2961a 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -1408,6 +1408,7 @@ extension Dictionary { @inlinable public static func ==(lhs: Keys, rhs: Keys) -> Bool { // Equal if the two dictionaries share storage. +#if _runtime(_ObjC) if lhs._variant.isNative, rhs._variant.isNative, @@ -1415,7 +1416,6 @@ extension Dictionary { { return true } -#if _runtime(_ObjC) if !lhs._variant.isNative, !rhs._variant.isNative, @@ -1423,6 +1423,10 @@ extension Dictionary { { return true } +#else + if lhs._variant.asNative._storage === rhs._variant.asNative._storage { + return true + } #endif // Not equal if the dictionaries are different sizes. @@ -1616,47 +1620,20 @@ extension Dictionary.Values { extension Dictionary: Equatable where Value: Equatable { @inlinable public static func == (lhs: [Key: Value], rhs: [Key: Value]) -> Bool { +#if _runtime(_ObjC) switch (lhs._variant.isNative, rhs._variant.isNative) { case (true, true): - let lhs = lhs._variant.asNative - let rhs = rhs._variant.asNative - - if lhs._storage === rhs._storage { return true } - if lhs.count != rhs.count { return false } - - for (k, v) in lhs { - let (bucket, found) = rhs.find(k) - guard found, rhs.uncheckedValue(at: bucket) == v else { return false } - } - return true - -#if _runtime(_ObjC) + return lhs._variant.asNative.isEqual(to: rhs._variant.asNative) case (false, false): - return lhs._variant.asCocoa == rhs._variant.asCocoa - + return lhs._variant.asCocoa.isEqual(to: rhs._variant.asCocoa) case (true, false): - let lhs = lhs._variant.asNative - let rhs = rhs._variant.asCocoa - - if lhs.count != rhs.count { return false } - - defer { _fixLifetime(lhs) } - for bucket in lhs.hashTable { - let key = lhs.uncheckedKey(at: bucket) - let value = lhs.uncheckedValue(at: bucket) - guard - let cocoaValue = rhs.lookup(_bridgeAnythingToObjectiveC(key)), - value == _forceBridgeFromObjectiveC(cocoaValue, Value.self) - else { - return false - } - } - return true - + return lhs._variant.asNative.isEqual(to: rhs._variant.asCocoa) case (false, true): - return rhs == lhs - #endif + return rhs._variant.asNative.isEqual(to: lhs._variant.asCocoa) } +#else + return lhs._variant.asNative.isEqual(to: rhs._variant.asNative) +#endif } } diff --git a/stdlib/public/core/DictionaryBridging.swift b/stdlib/public/core/DictionaryBridging.swift index b0e9236419991..1808ef97f8de6 100644 --- a/stdlib/public/core/DictionaryBridging.swift +++ b/stdlib/public/core/DictionaryBridging.swift @@ -434,13 +434,10 @@ internal struct _CocoaDictionary { } } -extension _CocoaDictionary: Equatable { +extension _CocoaDictionary { @usableFromInline - internal static func ==( - lhs: _CocoaDictionary, - rhs: _CocoaDictionary - ) -> Bool { - return _stdlib_NSObject_isEqual(lhs.object, rhs.object) + internal func isEqual(to other: _CocoaDictionary) -> Bool { + return _stdlib_NSObject_isEqual(self.object, other.object) } } diff --git a/stdlib/public/core/NativeDictionary.swift b/stdlib/public/core/NativeDictionary.swift index afe2a65a826bb..4947eaf5e071f 100644 --- a/stdlib/public/core/NativeDictionary.swift +++ b/stdlib/public/core/NativeDictionary.swift @@ -526,6 +526,43 @@ extension _NativeDictionary { } } +extension _NativeDictionary where Value: Equatable { + @inlinable + @inline(__always) + func isEqual(to other: _NativeDictionary) -> Bool { + if self._storage === other._storage { return true } + if self.count != other.count { return false } + + for (key, value) in self { + let (bucket, found) = other.find(key) + guard found, other.uncheckedValue(at: bucket) == value else { + return false + } + } + return true + } + +#if _runtime(_ObjC) + @inlinable + func isEqual(to other: _CocoaDictionary) -> Bool { + if self.count != other.count { return false } + + defer { _fixLifetime(self) } + for bucket in self.hashTable { + let key = self.uncheckedKey(at: bucket) + let value = self.uncheckedValue(at: bucket) + guard + let cocoaValue = other.lookup(_bridgeAnythingToObjectiveC(key)), + value == _forceBridgeFromObjectiveC(cocoaValue, Value.self) + else { + return false + } + } + return true + } +#endif +} + extension _NativeDictionary: _HashTableDelegate { @inlinable @inline(__always) diff --git a/stdlib/public/core/NativeSet.swift b/stdlib/public/core/NativeSet.swift index 37566ac9c415e..a1610f5e36b1b 100644 --- a/stdlib/public/core/NativeSet.swift +++ b/stdlib/public/core/NativeSet.swift @@ -424,6 +424,35 @@ extension _NativeSet { // Insertions } } +extension _NativeSet { + @inlinable + @inline(__always) + func isEqual(to other: _NativeSet) -> Bool { + if self._storage === other._storage { return true } + if self.count != other.count { return false } + + for member in self { + guard other.find(member).found else { return false } + } + return true + } + +#if _runtime(_ObjC) + @inlinable + func isEqual(to other: _CocoaSet) -> Bool { + if self.count != other.count { return false } + + defer { _fixLifetime(self) } + for bucket in self.hashTable { + let key = self.uncheckedElement(at: bucket) + let bridgedKey = _bridgeAnythingToObjectiveC(key) + guard other.contains(bridgedKey) else { return false } + } + return true + } +#endif +} + extension _NativeSet: _HashTableDelegate { @inlinable @inline(__always) diff --git a/stdlib/public/core/Set.swift b/stdlib/public/core/Set.swift index 371dc6cbbef2f..cdb917e29052d 100644 --- a/stdlib/public/core/Set.swift +++ b/stdlib/public/core/Set.swift @@ -421,40 +421,20 @@ extension Set: Equatable { /// `false`. @inlinable public static func == (lhs: Set, rhs: Set) -> Bool { +#if _runtime(_ObjC) switch (lhs._variant.isNative, rhs._variant.isNative) { case (true, true): - let lhs = lhs._variant.asNative - let rhs = rhs._variant.asNative - - if lhs._storage === rhs._storage { return true } - if lhs.count != rhs.count { return false } - - for member in lhs { - guard rhs.find(member).found else { return false } - } - return true -#if _runtime(_ObjC) + return lhs._variant.asNative.isEqual(to: rhs._variant.asNative) case (false, false): - return lhs._variant.asCocoa == rhs._variant.asCocoa - + return lhs._variant.asCocoa.isEqual(to: rhs._variant.asCocoa) case (true, false): - let lhs = lhs._variant.asNative - let rhs = rhs._variant.asCocoa - - if lhs.count != rhs.count { return false } - - defer { _fixLifetime(lhs) } - for bucket in lhs.hashTable { - let key = lhs.uncheckedElement(at: bucket) - let bridgedKey = _bridgeAnythingToObjectiveC(key) - guard rhs.contains(bridgedKey) else { return false } - } - return true - + return lhs._variant.asNative.isEqual(to: rhs._variant.asCocoa) case (false, true): - return rhs == lhs - #endif + return rhs._variant.asNative.isEqual(to: lhs._variant.asCocoa) } +#else + return lhs._variant.asNative.isEqual(to: rhs._variant.asNative) +#endif } } diff --git a/stdlib/public/core/SetBridging.swift b/stdlib/public/core/SetBridging.swift index d793b971abdba..8b9387a22d296 100644 --- a/stdlib/public/core/SetBridging.swift +++ b/stdlib/public/core/SetBridging.swift @@ -313,10 +313,10 @@ extension _CocoaSet { } } -extension _CocoaSet: Equatable { +extension _CocoaSet { @usableFromInline - internal static func ==(lhs: _CocoaSet, rhs: _CocoaSet) -> Bool { - return _stdlib_NSObject_isEqual(lhs.object, rhs.object) + internal func isEqual(to other: _CocoaSet) -> Bool { + return _stdlib_NSObject_isEqual(self.object, other.object) } } From 21da7da9d06174e7e48a127c9f896c6d3cbcc28b Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Wed, 3 Oct 2018 22:49:46 +0100 Subject: [PATCH 11/18] [stdlib] Fix build on 32-bit platforms --- stdlib/public/core/BridgeStorage.swift | 2 ++ stdlib/public/core/DictionaryVariant.swift | 4 ++++ stdlib/public/core/SetVariant.swift | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/stdlib/public/core/BridgeStorage.swift b/stdlib/public/core/BridgeStorage.swift index 3fe56a522cf7b..b01d9d7434a60 100644 --- a/stdlib/public/core/BridgeStorage.swift +++ b/stdlib/public/core/BridgeStorage.swift @@ -61,11 +61,13 @@ struct _BridgeStorage< rawValue = Builtin.reinterpretCast(native) } +#if !(arch(i386) || arch(arm)) @inlinable @inline(__always) internal init(taggedPayload: UInt) { rawValue = _bridgeObject(taggingPayload: taggedPayload) } +#endif @inlinable // FIXME(sil-serialize-all) public // @testable diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index de4d47d8ed5e7..cd5a11f638aa6 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -51,7 +51,11 @@ extension Dictionary { @inlinable @inline(__always) init(dummy: Void) { +#if arch(i386) || arch(arm) + self.init(native: _NativeDictionary()) +#else self.object = _BridgeStorage(taggedPayload: 0) +#endif } #if _runtime(_ObjC) diff --git a/stdlib/public/core/SetVariant.swift b/stdlib/public/core/SetVariant.swift index 605b4904159f8..6cd7f1fc101aa 100644 --- a/stdlib/public/core/SetVariant.swift +++ b/stdlib/public/core/SetVariant.swift @@ -41,7 +41,11 @@ extension Set { @inlinable @inline(__always) init(dummy: ()) { +#if arch(i386) || arch(arm) + self.init(native: _NativeSet()) +#else self.object = _BridgeStorage(taggedPayload: 0) +#endif } @inlinable From be0abbbafb6731016e05125bbfc1a0a5db269d40 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Thu, 4 Oct 2018 11:55:36 +0100 Subject: [PATCH 12/18] [stdlib] _Bitset: Reduce unnecessary overflow checking This is mostly a code size improvement. --- stdlib/public/core/Bitset.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/Bitset.swift b/stdlib/public/core/Bitset.swift index 3a0d97106c7a1..69045d88f4099 100644 --- a/stdlib/public/core/Bitset.swift +++ b/stdlib/public/core/Bitset.swift @@ -64,7 +64,7 @@ extension _UnsafeBitset { @inline(__always) internal static func join(word: Int, bit: Int) -> Int { _sanityCheck(bit >= 0 && bit < Word.capacity) - return word &* Word.capacity + bit + return word &* Word.capacity &+ bit } } @@ -72,7 +72,7 @@ extension _UnsafeBitset { @inlinable @inline(__always) internal static func wordCount(forCapacity capacity: Int) -> Int { - return word(for: capacity + Word.capacity - 1) + return word(for: capacity &+ Word.capacity &- 1) } @inlinable From 84d8cedaaea8562d98fd89a1c7d31bf8e06cb9d1 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Thu, 4 Oct 2018 11:56:47 +0100 Subject: [PATCH 13/18] [stdlib] _SetBuilder, _DictionaryBuilder: Assume target has enough capacity The compiled code included unnecessary references to resize(). --- stdlib/public/core/DictionaryBuilder.swift | 4 +++- stdlib/public/core/SetBuilder.swift | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/DictionaryBuilder.swift b/stdlib/public/core/DictionaryBuilder.swift index a6b1c65d46a9b..68ec578c66110 100644 --- a/stdlib/public/core/DictionaryBuilder.swift +++ b/stdlib/public/core/DictionaryBuilder.swift @@ -30,7 +30,9 @@ struct _DictionaryBuilder { @inlinable public mutating func add(key newKey: Key, value: Value) { - _target.insertNew(key: newKey, value: value) + _precondition(_target.count < _requestedCount, + "Can't add more members than promised") + _target._unsafeInsertNew(key: newKey, value: value) } @inlinable diff --git a/stdlib/public/core/SetBuilder.swift b/stdlib/public/core/SetBuilder.swift index 7ad37896232c9..abf5b4a98dceb 100644 --- a/stdlib/public/core/SetBuilder.swift +++ b/stdlib/public/core/SetBuilder.swift @@ -29,10 +29,11 @@ struct _SetBuilder { } @inlinable + @inline(__always) public mutating func add(member: Element) { _precondition(_target.count < _requestedCount, "Can't add more members than promised") - _target.insertNew(member, isUnique: true) + _target._unsafeInsertNew(member) } @inlinable From bb2cb064ae5377f5fb6c7b93c5a6d234d42c7fce Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Thu, 4 Oct 2018 11:59:10 +0100 Subject: [PATCH 14/18] [stdlib] Eliminate more overflow checks This is a tiny code size improvement. --- stdlib/public/core/HashTable.swift | 2 +- stdlib/public/core/NativeDictionary.swift | 2 +- stdlib/public/core/NativeSet.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/public/core/HashTable.swift b/stdlib/public/core/HashTable.swift index 39ee521250826..8b09edcf75197 100644 --- a/stdlib/public/core/HashTable.swift +++ b/stdlib/public/core/HashTable.swift @@ -392,7 +392,7 @@ extension _HashTable { } var wrap = false while true { - word += 1 + word &+= 1 if word == wordCount { _precondition(!wrap, "Hash table has no holes") wrap = true diff --git a/stdlib/public/core/NativeDictionary.swift b/stdlib/public/core/NativeDictionary.swift index 4947eaf5e071f..da0cd449276a5 100644 --- a/stdlib/public/core/NativeDictionary.swift +++ b/stdlib/public/core/NativeDictionary.swift @@ -426,7 +426,7 @@ extension _NativeDictionary { // Insertions let bucket = hashTable.insertNew(hashValue: hashValue) uncheckedInitialize(at: bucket, toKey: key, value: value) } - _storage._count += 1 + _storage._count &+= 1 } /// Insert a new entry into uniquely held storage. diff --git a/stdlib/public/core/NativeSet.swift b/stdlib/public/core/NativeSet.swift index a1610f5e36b1b..a45475dfbf574 100644 --- a/stdlib/public/core/NativeSet.swift +++ b/stdlib/public/core/NativeSet.swift @@ -360,7 +360,7 @@ extension _NativeSet { // Insertions let bucket = hashTable.insertNew(hashValue: hashValue) uncheckedInitialize(at: bucket, to: element) } - _storage._count += 1 + _storage._count &+= 1 } /// Insert a new element into uniquely held storage. From 35502ef4766f64ba7b8fa16a630c97c016d59e12 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Thu, 4 Oct 2018 14:56:39 +0100 Subject: [PATCH 15/18] =?UTF-8?q?[stdlib]=20Acknowledge=20there=20are=20no?= =?UTF-8?q?=20spare=20bits=20in=20Set/Dictionary=E2=80=99s=20BridgeObject?= =?UTF-8?q?=20storage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We really need to do a pass on the _BridgeStorage API at some point soon. --- stdlib/public/core/DictionaryVariant.swift | 10 ++++++---- stdlib/public/core/SetVariant.swift | 9 +++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/stdlib/public/core/DictionaryVariant.swift b/stdlib/public/core/DictionaryVariant.swift index cd5a11f638aa6..4e218e8cd59fd 100644 --- a/stdlib/public/core/DictionaryVariant.swift +++ b/stdlib/public/core/DictionaryVariant.swift @@ -78,26 +78,28 @@ extension Dictionary._Variant { @inlinable internal mutating func isUniquelyReferenced() -> Bool { - return object.isUniquelyReferencedNative() + return object.isUniquelyReferenced_native_noSpareBits() } #if _runtime(_ObjC) @usableFromInline @_transparent internal var isNative: Bool { - return guaranteedNative || object.isNative + if guaranteedNative { return true } + return object.isNativeWithClearedSpareBits(0) } #endif @usableFromInline @_transparent internal var asNative: _NativeDictionary { get { - return _NativeDictionary(object.nativeInstance) + return _NativeDictionary(object.nativeInstance_noSpareBits) } set { self = .init(native: newValue) } _modify { - var native = _NativeDictionary(object.nativeInstance) + var native = _NativeDictionary( + object.nativeInstance_noSpareBits) self = .init(dummy: ()) yield &native object = .init(native: native._storage) diff --git a/stdlib/public/core/SetVariant.swift b/stdlib/public/core/SetVariant.swift index 6cd7f1fc101aa..746b9ca808b55 100644 --- a/stdlib/public/core/SetVariant.swift +++ b/stdlib/public/core/SetVariant.swift @@ -75,26 +75,27 @@ extension Set._Variant { @inlinable internal mutating func isUniquelyReferenced() -> Bool { - return object.isUniquelyReferencedNative() + return object.isUniquelyReferenced_native_noSpareBits() } #if _runtime(_ObjC) @usableFromInline @_transparent internal var isNative: Bool { - return guaranteedNative || object.isNative + if guaranteedNative { return true } + return object.isNativeWithClearedSpareBits(0) } #endif @usableFromInline @_transparent internal var asNative: _NativeSet { get { - return _NativeSet(object.nativeInstance) + return _NativeSet(object.nativeInstance_noSpareBits) } set { self = .init(native: newValue) } _modify { - var native = _NativeSet(object.nativeInstance) + var native = _NativeSet(object.nativeInstance_noSpareBits) self = .init(dummy: ()) yield &native object = .init(native: native._storage) From d904b46554ef22699ffba564502d507d420e20c9 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Thu, 4 Oct 2018 16:51:29 +0100 Subject: [PATCH 16/18] [stdlib] Dictionary.Index: Remove duplicate definition for _isNative --- stdlib/public/core/Dictionary.swift | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/stdlib/public/core/Dictionary.swift b/stdlib/public/core/Dictionary.swift index 8596710f2961a..57c436c1bcdc2 100644 --- a/stdlib/public/core/Dictionary.swift +++ b/stdlib/public/core/Dictionary.swift @@ -1856,19 +1856,6 @@ extension Dictionary.Index { } #endif - @usableFromInline @_transparent - internal var _isNative: Bool { - switch _variant { - case .native: - return true -#if _runtime(_ObjC) - case .cocoa: - _cocoaPath() - return false -#endif - } - } - @usableFromInline @_transparent internal var _asNative: _HashTable.Index { switch _variant { From c47b922421ead33dcca8381c349be48e26535368 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Fri, 5 Oct 2018 14:07:10 +0100 Subject: [PATCH 17/18] [test] Confess Set/Dictionary ABI changes --- .../stability-stdlib-abi.swift.expected | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/api-digester/Outputs/stability-stdlib-abi.swift.expected b/test/api-digester/Outputs/stability-stdlib-abi.swift.expected index e548788af5466..4799a4bd1ec25 100644 --- a/test/api-digester/Outputs/stability-stdlib-abi.swift.expected +++ b/test/api-digester/Outputs/stability-stdlib-abi.swift.expected @@ -4,12 +4,32 @@ /* RawRepresentable Changes */ /* Removed Decls */ +EnumElement Dictionary._Variant.cocoa has been removed +EnumElement Set._Variant.cocoa has been removed +Func Dictionary._Variant.cocoaPath() has been removed +Func Set._Variant.cocoaPath() has been removed +Func _CocoaDictionary.==(_:_:) has been removed +Func _CocoaSet.==(_:_:) has been removed /* Moved Decls */ +Enum Dictionary._Variant has been changed to a Struct +Enum Set._Variant has been changed to a Struct /* Renamed Decls */ +EnumElement Dictionary._Variant.native has been renamed to Var Dictionary._Variant.isNative +EnumElement Set._Variant.native has been renamed to Var Set._Variant.isNative /* Type Changes */ +Enum Dictionary._Variant is now with @_fixed_layout +Enum Dictionary._Variant is now without @_frozen +Enum Set._Variant is now with @_fixed_layout +Enum Set._Variant is now without @_frozen +EnumElement Dictionary._Variant.native is no longer a stored property +EnumElement Set._Variant.native is no longer a stored property +Var Dictionary._Variant.object is added to a non-resilient type +Var Set._Variant.object is added to a non-resilient type +Struct _CocoaDictionary has removed conformance to Equatable +Struct _CocoaSet has removed conformance to Equatable /* Decl Attribute changes */ From aba15a11516e6e4fe1b17761e567254b0fded33f Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Sat, 6 Oct 2018 01:47:09 +0100 Subject: [PATCH 18/18] [test] Set and Dictionary now has an extra inhabitant (32-bit) --- validation-test/Reflection/reflect_Dictionary.swift | 2 +- validation-test/Reflection/reflect_Set.swift | 2 +- validation-test/Reflection/reflect_multiple_types.swift | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/validation-test/Reflection/reflect_Dictionary.swift b/validation-test/Reflection/reflect_Dictionary.swift index 3e79625b6ae87..aa6efd9e27496 100644 --- a/validation-test/Reflection/reflect_Dictionary.swift +++ b/validation-test/Reflection/reflect_Dictionary.swift @@ -39,7 +39,7 @@ reflect(object: obj) // CHECK-32: Type info: // CHECK-32: (class_instance size=12 alignment=4 stride=12 num_extra_inhabitants=0 // CHECK-32: (field name=t offset=8 -// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=1 // (unstable implementation details omitted) doneReflecting() diff --git a/validation-test/Reflection/reflect_Set.swift b/validation-test/Reflection/reflect_Set.swift index 467525ae682c6..5d0593422d9cb 100644 --- a/validation-test/Reflection/reflect_Set.swift +++ b/validation-test/Reflection/reflect_Set.swift @@ -39,7 +39,7 @@ reflect(object: obj) // CHECK-32: Type info: // CHECK-32: (class_instance size=12 alignment=4 stride=12 num_extra_inhabitants=0 // CHECK-32: (field name=t offset=8 -// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 +// CHECK-32: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=1 // (unstable implementation details omitted) doneReflecting() diff --git a/validation-test/Reflection/reflect_multiple_types.swift b/validation-test/Reflection/reflect_multiple_types.swift index 0bd12f0b238a7..9bc2f4ff603d9 100644 --- a/validation-test/Reflection/reflect_multiple_types.swift +++ b/validation-test/Reflection/reflect_multiple_types.swift @@ -220,7 +220,7 @@ reflect(object: obj) // CHECK-32-NEXT: (field name=large offset=0 // CHECK-32-NEXT: (reference kind=strong refcounting=native)))))) // CHECK-32-NEXT: (field name=t03 offset=24 -// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=1 // (unstable implementation details omitted) // CHECK-32: (field name=t04 offset=32 // CHECK-32-NEXT: (struct size=8 alignment=8 stride=8 num_extra_inhabitants=0 @@ -259,7 +259,7 @@ reflect(object: obj) // CHECK-32-NEXT: (field name=t14 offset=80 // CHECK-32-NEXT: (reference kind=strong refcounting=unknown)) // CHECK-32-NEXT: (field name=t15 offset=84 -// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=0 +// CHECK-32-NEXT: (struct size=4 alignment=4 stride=4 num_extra_inhabitants=1 // (unstable implementation details omitted) // CHECK-32: (field name=t16 offset=88 // CHECK-32-NEXT: (struct size=12 alignment=4 stride=12 num_extra_inhabitants=4092