From d80bea648f1c3bd6072fe5d4453f294fc3877bb7 Mon Sep 17 00:00:00 2001 From: Morten Bek Ditlevsen Date: Sat, 10 Feb 2018 19:32:04 +0100 Subject: [PATCH 01/19] [WIP] Conditional conformance to Hashable for Optional, Dictionary and Array types. (#14247) * Add conditional Hashable conformance to Optional, Dictionary, Array, ArraySlice and ContiguousArray * Modified hashValue implementations The hashValues are now calculated similar to the automatically synthesized values when conforming to Hashable. This entails using _combineHashValues as values of the collections are iterated - as well as calling _mixInt before returning the hash. * Added FIXMEs as suggested by Max Moiseev * Use checkHashable to check Hashable conformance * Use 2 space indentation * Hashing of Dictionary is now independent of traversal order * Added a test to proof failure of (previous) wrong implementation of Dictionary hashValue. Unfortunately it does not work. * Removed '_mixInt' from 'hashValue' implementation of Optional and Array types based on recommendations from lorentey * Another attempt at detecting bad hashing due to traversal order * Dictionary Hashable validation tests now detect bad hashing due to dependence on traversal order * Removed superfluous initial _mixInt call for Dictionary hashValue implementation. * Add more elements to dictionary in test to increase the number of possible permutations - making it more likely to detect order-dependent hashes * Added Hashable conformance to CollectionOfOne, EmptyCollection and Range types * Fix indirect referral to the only member of CollectionOfOne * Re-added Hashable conformance to Range after merge from master * Change hashValue based on comment from @lorentey * Remove tests for conditional Hashable conformance for Range types. This is left for a followup PR * Added tests for CollectionOfOne and EmptyCollection * Added conditional conformance fo Equatable and Hashable for DictionaryLiteral. Added tests too. * Added conditional Equatable and Hashable conformance to Slice * Use 'elementsEqual' for Slice equality operator * Fixed documentation comment and indentation * Fix DictionaryLiteral equality implementation * Revert "Fix DictionaryLiteral equality implementation" This reverts commit 7fc1510bc3c17add09ecd7ae1444fb38410b2115. * Fix DictionaryLiteral equality implementation * Use equalElements(:by:) to compare DictionaryLiteral elements * Added conditional conformance for Equatable and Hashable to AnyCollection * Revert "Use 'elementsEqual' for Slice equality operator" This reverts commit 0ba2278b968c03d96930dd116c415d4e9a67616d. * Revert "Added conditional Equatable and Hashable conformance to Slice" This reverts commit 84f9934bb44172471f041920abfa00e8c3174563. * Added conditional conformance for Equatable and Hashable for ClosedRange --- stdlib/public/core/Arrays.swift.gyb | 18 +++++++++++ stdlib/public/core/ClosedRange.swift | 13 ++++++++ stdlib/public/core/CollectionOfOne.swift | 21 +++++++++++++ stdlib/public/core/EmptyCollection.swift | 7 +++++ .../core/ExistentialCollection.swift.gyb | 29 ++++++++++++++++++ .../public/core/HashedCollections.swift.gyb | 19 ++++++++++++ stdlib/public/core/Mirror.swift | 30 +++++++++++++++++++ stdlib/public/core/Optional.swift | 21 +++++++++++++ stdlib/public/core/Range.swift | 13 ++++++++ test/stdlib/DictionaryLiteral.swift | 9 ++++++ test/stdlib/Optional.swift | 13 ++++++++ validation-test/stdlib/Arrays.swift.gyb | 19 ++++++++++++ validation-test/stdlib/Dictionary.swift | 25 ++++++++++++++++ validation-test/stdlib/Lazy.swift.gyb | 19 ++++++++++++ 14 files changed, 256 insertions(+) diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index 00aeab8401f0b..5746ebbb73b52 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -2250,6 +2250,24 @@ extension ${Self} : Equatable where Element : Equatable { } } +extension ${Self} : Hashable where Element : Hashable { + /// The hash value for the array. + /// + /// Two arrays that are equal will always have equal hash values. + /// + /// Hash values are not guaranteed to be equal across different executions of + /// your program. Do not save hash values to use during a future execution. + @_inlineable // FIXME(sil-serialize-all) + public var hashValue: Int { + // FIXME(ABI)#177: Issue applies to Array too + var result: Int = 0 + for element in self { + result = _combineHashValues(result, element.hashValue) + } + return result + } +} + extension ${Self} { /// Calls the given closure with a pointer to the underlying bytes of the /// array's mutable contiguous storage. diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index bd75d05d438ee..9f94b66eb8db4 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -376,6 +376,19 @@ extension ClosedRange: Equatable { } } +extension ClosedRange : Hashable where Bound : Hashable { + /// The hash value for the range. + /// + /// Two ranges that are equal will always have equal hash values. + /// + /// Hash values are not guaranteed to be equal across different executions of + /// your program. Do not save hash values to use during a future execution. + @_inlineable // FIXME(sil-serialize-all) + public var hashValue: Int { + return _combineHashValues(lowerBound.hashValue, upperBound.hashValue) + } +} + extension ClosedRange : CustomStringConvertible { /// A textual representation of the range. @_inlineable // FIXME(sil-serialize-all)...\( diff --git a/stdlib/public/core/CollectionOfOne.swift b/stdlib/public/core/CollectionOfOne.swift index ba345fa59fd49..a108980f90ce4 100644 --- a/stdlib/public/core/CollectionOfOne.swift +++ b/stdlib/public/core/CollectionOfOne.swift @@ -136,6 +136,27 @@ extension CollectionOfOne: RandomAccessCollection, MutableCollection { } } +extension CollectionOfOne : Equatable where Element : Equatable { + /// Returns a Boolean value indicating whether two collections are equal. + @_inlineable // FIXME(sil-serialize-all) + public static func == (lhs: CollectionOfOne, rhs: CollectionOfOne) -> Bool { + return lhs._element == rhs._element + } +} + +extension CollectionOfOne : Hashable where Element : Hashable { + /// The hash value for the collection. + /// + /// Two collection that are equal will always have equal hash values. + /// + /// Hash values are not guaranteed to be equal across different executions of + /// your program. Do not save hash values to use during a future execution. + @_inlineable // FIXME(sil-serialize-all) + public var hashValue: Int { + return _element.hashValue + } +} + extension CollectionOfOne : CustomDebugStringConvertible { /// A textual representation of `self`, suitable for debugging. @_inlineable // FIXME(sil-serialize-all) diff --git a/stdlib/public/core/EmptyCollection.swift b/stdlib/public/core/EmptyCollection.swift index 4fb6d8d737aa3..ca85ff26fd009 100644 --- a/stdlib/public/core/EmptyCollection.swift +++ b/stdlib/public/core/EmptyCollection.swift @@ -173,5 +173,12 @@ extension EmptyCollection : Equatable { } } +extension EmptyCollection : Hashable { + @_inlineable // FIXME(sil-serialize-all) + public var hashValue: Int { + return 0 + } +} + // @available(*, deprecated, renamed: "EmptyCollection.Iterator") public typealias EmptyIterator = EmptyCollection.Iterator diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index 49fd2b0485464..9c6aae223374d 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -1243,3 +1243,32 @@ extension ${Self}: _AnyCollectionProtocol { } } % end + +extension AnyCollection : Equatable where Element : Equatable { + @_inlineable // FIXME(sil-serialize-all) + public static func ==(lhs: AnyCollection, rhs: AnyCollection) -> Bool { + let lhsCount = lhs.count + if lhs.count != rhs.count { + return false + } + return lhs.elementsEqual(rhs) + } +} + +extension AnyCollection : Hashable where Element : Hashable { + /// The hash value for the collection. + /// + /// Two `AnyCollection` values that are equal will always have equal hash values. + /// + /// Hash values are not guaranteed to be equal across different executions of + /// your program. Do not save hash values to use during a future execution. + @_inlineable // FIXME(sil-serialize-all) + public var hashValue: Int { + // FIXME(ABI)#177: Issue applies to AnyCollection too + var result: Int = 0 + for element in self { + result = _combineHashValues(result, element.hashValue) + } + return result + } +} diff --git a/stdlib/public/core/HashedCollections.swift.gyb b/stdlib/public/core/HashedCollections.swift.gyb index 8045b6a43d565..8ecac4861a1f0 100644 --- a/stdlib/public/core/HashedCollections.swift.gyb +++ b/stdlib/public/core/HashedCollections.swift.gyb @@ -2786,6 +2786,25 @@ extension Dictionary : Equatable where Value : Equatable { } } +extension Dictionary : Hashable where Value : Hashable { + /// The hash value for the dictionary. + /// + /// Two dictionaries that are equal will always have equal hash values. + /// + /// Hash values are not guaranteed to be equal across different executions of + /// your program. Do not save hash values to use during a future execution. + @_inlineable // FIXME(sil-serialize-all) + public var hashValue: Int { + // FIXME(ABI)#177: Issue applies to Dictionary too + var result: Int = 0 + for (k, v) in self { + let combined = _combineHashValues(k.hashValue, v.hashValue) + result ^= _mixInt(combined) + } + return result + } +} + extension Dictionary : CustomStringConvertible, CustomDebugStringConvertible { @_inlineable // FIXME(sil-serialize-all) @_versioned // FIXME(sil-serialize-all) diff --git a/stdlib/public/core/Mirror.swift b/stdlib/public/core/Mirror.swift index a8aee9408fa0b..f7bd28c457483 100644 --- a/stdlib/public/core/Mirror.swift +++ b/stdlib/public/core/Mirror.swift @@ -905,6 +905,36 @@ extension DictionaryLiteral : RandomAccessCollection { } } +extension DictionaryLiteral : Equatable where Key: Equatable, Value : Equatable { + @_inlineable // FIXME(sil-serialize-all) + public static func ==(lhs: DictionaryLiteral, rhs: DictionaryLiteral) -> Bool { + if lhs.count != rhs.count { + return false + } + + return lhs.elementsEqual(rhs, by: ==) + } +} + +extension DictionaryLiteral : Hashable where Key: Hashable, Value : Hashable { + /// The hash value for the collection. + /// + /// Two `DictionaryLiteral` values that are equal will always have equal hash values. + /// + /// Hash values are not guaranteed to be equal across different executions of + /// your program. Do not save hash values to use during a future execution. + @_inlineable // FIXME(sil-serialize-all) + public var hashValue: Int { + // FIXME(ABI)#177: Issue applies to DictionaryLiteral too + var result: Int = 0 + for element in self { + let elementHashValue = _combineHashValues(element.key.hashValue, element.value.hashValue) + result = _combineHashValues(result, elementHashValue) + } + return result + } +} + extension String { /// Creates a string representing the given value. /// diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index 459b249ad2822..0f3832cfecc77 100644 --- a/stdlib/public/core/Optional.swift +++ b/stdlib/public/core/Optional.swift @@ -409,6 +409,27 @@ extension Optional : Equatable where Wrapped : Equatable { } } +extension Optional : Hashable where Wrapped : Hashable { + /// The hash value for the optional. + /// + /// Two optionals that are equal will always have equal hash values. + /// + /// Hash values are not guaranteed to be equal across different executions of + /// your program. Do not save hash values to use during a future execution. + @_inlineable // FIXME(sil-serialize-all) + public var hashValue: Int { + var result: Int + switch self { + case .none: + result = 0 + case .some(let wrapped): + result = 1 + result = _combineHashValues(result, wrapped.hashValue) + } + return result + } +} + // Enable pattern matching against the nil literal, even if the element type // isn't equatable. @_fixed_layout diff --git a/stdlib/public/core/Range.swift b/stdlib/public/core/Range.swift index fbd59ba9e9f78..a83ee66783a8e 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -358,6 +358,19 @@ extension Range: Equatable { } } +extension Range : Hashable where Bound : Hashable { + /// The hash value for the range. + /// + /// Two ranges that are equal will always have equal hash values. + /// + /// Hash values are not guaranteed to be equal across different executions of + /// your program. Do not save hash values to use during a future execution. + @_inlineable // FIXME(sil-serialize-all) + public var hashValue: Int { + return _combineHashValues(lowerBound.hashValue, upperBound.hashValue) + } +} + /// A partial half-open interval up to, but not including, an upper bound. /// /// You create `PartialRangeUpTo` instances by using the prefix half-open range diff --git a/test/stdlib/DictionaryLiteral.swift b/test/stdlib/DictionaryLiteral.swift index 8fc3c8875060d..ceb2c0f18a647 100644 --- a/test/stdlib/DictionaryLiteral.swift +++ b/test/stdlib/DictionaryLiteral.swift @@ -54,3 +54,12 @@ expectType(DictionaryLiteral.self, &hetero1) var hetero2: DictionaryLiteral = ["a": 1 as NSNumber, "b": "Foo" as NSString] expectType(DictionaryLiteral.self, &hetero2) + +let instances: [DictionaryLiteral] = [ + [1: "a", 1: "a", 2: "b"], + [1: "a", 2: "b", 1: "a"], + [2: "b", 1: "a", 1: "a"], + [1: "a", 1: "a", 1: "a"] +] +checkEquatable(instances, oracle: { $0 == $1 }) +checkHashable(instances, equalityOracle: { $0 == $1 }) diff --git a/test/stdlib/Optional.swift b/test/stdlib/Optional.swift index 30c4b94270e0d..5b714716695a6 100644 --- a/test/stdlib/Optional.swift +++ b/test/stdlib/Optional.swift @@ -69,6 +69,19 @@ OptionalTests.test("Equatable") { expectEqual([false, true, true, true, true, false], testRelation(!=)) } +OptionalTests.test("Hashable") { + let o1: Optional = .some(1010) + let o2: Optional = .some(2020) + let o3: Optional = .none + checkHashable([o1, o2, o3], equalityOracle: { $0 == $1 }) + + let oo1: Optional> = .some(.some(1010)) + let oo2: Optional> = .some(.some(2010)) + let oo3: Optional> = .some(.none) + let oo4: Optional> = .none + checkHashable([oo1, oo2, oo3, oo4], equalityOracle: { $0 == $1 }) +} + OptionalTests.test("CustomReflectable") { // Test with a non-refcountable type. do { diff --git a/validation-test/stdlib/Arrays.swift.gyb b/validation-test/stdlib/Arrays.swift.gyb index ea11358d1299f..48ad8bb6d2916 100644 --- a/validation-test/stdlib/Arrays.swift.gyb +++ b/validation-test/stdlib/Arrays.swift.gyb @@ -189,6 +189,25 @@ ArrayTestSuite.test("init(repeating:count:)") { } } +ArrayTestSuite.test("Hashable") { + let a1: Array = [1, 2, 3] + let a2: Array = [1, 3, 2] + let a3: Array = [3, 1, 2] + let a4: Array = [1, 2] + let a5: Array = [1] + let a6: Array = [] + let a7: Array = [1, 1, 1] + checkHashable([a1, a2, a3, a4, a5, a6, a7], equalityOracle: { $0 == $1 }) + + let aa1: Array> = [[], [1], [1, 2], [2, 1]] + let aa2: Array> = [[], [1], [2, 1], [2, 1]] + let aa3: Array> = [[1], [], [2, 1], [2, 1]] + let aa4: Array> = [[1], [], [2, 1], [2]] + let aa5: Array> = [[1], [], [2, 1]] + checkHashable([aa1, aa2, aa3, aa4, aa5], equalityOracle: { $0 == $1 }) +} + + #if _runtime(_ObjC) //===----------------------------------------------------------------------===// // NSArray -> Array bridging tests. diff --git a/validation-test/stdlib/Dictionary.swift b/validation-test/stdlib/Dictionary.swift index b2117d6597376..6fd57d0f03b0c 100644 --- a/validation-test/stdlib/Dictionary.swift +++ b/validation-test/stdlib/Dictionary.swift @@ -83,6 +83,31 @@ DictionaryTestSuite.test("Index.Hashable") { expectNotNil(e[d.startIndex]) } +DictionaryTestSuite.test("Hashable") { + let d1: Dictionary = [1: "meow", 2: "meow", 3: "meow"] + let d2: Dictionary = [1: "meow", 2: "meow", 3: "mooo"] + let d3: Dictionary = [1: "meow", 2: "meow", 4: "meow"] + let d4: Dictionary = [1: "meow", 2: "meow", 4: "mooo"] + checkHashable([d1, d2, d3, d4], equalityOracle: { $0 == $1 }) + + let dd1: Dictionary> = [1: [2: "meow"]] + let dd2: Dictionary> = [2: [1: "meow"]] + let dd3: Dictionary> = [2: [2: "meow"]] + let dd4: Dictionary> = [1: [1: "meow"]] + let dd5: Dictionary> = [2: [2: "mooo"]] + let dd6: Dictionary> = [2: [:]] + let dd7: Dictionary> = [:] + checkHashable([dd1, dd2, dd3, dd4, dd5, dd6, dd7], equalityOracle: { $0 == $1 }) + + // Check that hash is equal even though dictionary is traversed differently + var d5: Dictionary = [1: "meow", 2: "meow", 3: "mooo", 4: "woof", 5: "baah", 6: "mooo"] + let expected = d5.hashValue + for capacity in [4, 8, 16, 32, 64, 128, 256] { + d5.reserveCapacity(capacity) + expectEqual(d5.hashValue, expected) + } +} + DictionaryTestSuite.test("valueDestruction") { var d1 = Dictionary() for i in 100...110 { diff --git a/validation-test/stdlib/Lazy.swift.gyb b/validation-test/stdlib/Lazy.swift.gyb index 44b29ac5f7ed4..4ce172f47897e 100644 --- a/validation-test/stdlib/Lazy.swift.gyb +++ b/validation-test/stdlib/Lazy.swift.gyb @@ -204,6 +204,20 @@ LazyTestSuite.test("CollectionOfOne/{CustomDebugStringConvertible,CustomReflecta c) } +LazyTestSuite.test("CollectionOfOne/Equatable") { + let c = CollectionOfOne(42) + let d = CollectionOfOne(43) + let instances = [ c, d ] + checkEquatable(instances, oracle: { $0 == $1 }) +} + +LazyTestSuite.test("CollectionOfOne/Hashable") { + let c = CollectionOfOne(42) + let d = CollectionOfOne(43) + let instances = [ c, d ] + checkHashable(instances, equalityOracle: { $0 == $1 }) +} + //===----------------------------------------------------------------------===// // EmptyCollection //===----------------------------------------------------------------------===// @@ -238,6 +252,11 @@ LazyTestSuite.test("EmptyCollection/Equatable") { checkEquatable(instances, oracle: { $0 == $1 }) } +LazyTestSuite.test("EmptyCollection/Equatable") { + let instances = [ EmptyCollection>() ] + checkHashable(instances, equalityOracle: { $0 == $1 }) +} + LazyTestSuite.test("EmptyCollection/AssociatedTypes") { typealias Subject = EmptyCollection> expectRandomAccessCollectionAssociatedTypes( From 86ffc4294c86318ea7b5e4dafadb135d9545be4d Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sat, 10 Feb 2018 12:43:59 -0600 Subject: [PATCH 02/19] Silence warning and break to 80 characters --- .../public/core/ExistentialCollection.swift.gyb | 8 +++++--- stdlib/public/core/Mirror.swift | 15 +++++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index 9c6aae223374d..ef87aed867600 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -1246,8 +1246,9 @@ extension ${Self}: _AnyCollectionProtocol { extension AnyCollection : Equatable where Element : Equatable { @_inlineable // FIXME(sil-serialize-all) - public static func ==(lhs: AnyCollection, rhs: AnyCollection) -> Bool { - let lhsCount = lhs.count + public static func == ( + lhs: AnyCollection, rhs: AnyCollection + ) -> Bool { if lhs.count != rhs.count { return false } @@ -1258,7 +1259,8 @@ extension AnyCollection : Equatable where Element : Equatable { extension AnyCollection : Hashable where Element : Hashable { /// The hash value for the collection. /// - /// Two `AnyCollection` values that are equal will always have equal hash values. + /// Two `AnyCollection` values that are equal will always have equal hash + /// values. /// /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. diff --git a/stdlib/public/core/Mirror.swift b/stdlib/public/core/Mirror.swift index f7bd28c457483..526590d1c9eda 100644 --- a/stdlib/public/core/Mirror.swift +++ b/stdlib/public/core/Mirror.swift @@ -905,21 +905,23 @@ extension DictionaryLiteral : RandomAccessCollection { } } -extension DictionaryLiteral : Equatable where Key: Equatable, Value : Equatable { +extension DictionaryLiteral: Equatable where Key: Equatable, Value: Equatable { @_inlineable // FIXME(sil-serialize-all) - public static func ==(lhs: DictionaryLiteral, rhs: DictionaryLiteral) -> Bool { + public static func == ( + lhs: DictionaryLiteral, rhs: DictionaryLiteral + ) -> Bool { if lhs.count != rhs.count { return false } - return lhs.elementsEqual(rhs, by: ==) } } -extension DictionaryLiteral : Hashable where Key: Hashable, Value : Hashable { +extension DictionaryLiteral: Hashable where Key: Hashable, Value: Hashable { /// The hash value for the collection. /// - /// Two `DictionaryLiteral` values that are equal will always have equal hash values. + /// Two `DictionaryLiteral` values that are equal will always have equal hash + /// values. /// /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @@ -928,7 +930,8 @@ extension DictionaryLiteral : Hashable where Key: Hashable, Value : Hashable { // FIXME(ABI)#177: Issue applies to DictionaryLiteral too var result: Int = 0 for element in self { - let elementHashValue = _combineHashValues(element.key.hashValue, element.value.hashValue) + let elementHashValue = + _combineHashValues(element.key.hashValue, element.value.hashValue) result = _combineHashValues(result, elementHashValue) } return result From bd88afc3db8b640b64b94561eaa09d87fc6dd2e1 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Sat, 10 Feb 2018 23:00:57 +0000 Subject: [PATCH 03/19] [Foundation] NSArray, NSDictionary: Add custom AnyHashable representations Now that Array and Dictionary conform to Hashable, we need to make sure that their bridged counterparts provide the same hash values when converted to AnyHashable. --- stdlib/public/SDK/Foundation/NSArray.swift | 8 ++ .../public/SDK/Foundation/NSDictionary.swift | 8 ++ validation-test/stdlib/AnyHashable.swift.gyb | 84 +++++++++++++++++++ .../stdlib/AnyHashableDiagnostics.swift | 13 --- 4 files changed, 100 insertions(+), 13 deletions(-) delete mode 100644 validation-test/stdlib/AnyHashableDiagnostics.swift diff --git a/stdlib/public/SDK/Foundation/NSArray.swift b/stdlib/public/SDK/Foundation/NSArray.swift index adecca6c4bf72..b5825b13f49cb 100644 --- a/stdlib/public/SDK/Foundation/NSArray.swift +++ b/stdlib/public/SDK/Foundation/NSArray.swift @@ -106,6 +106,14 @@ extension Array : _ObjectiveCBridgeable { } } +extension NSArray : _HasCustomAnyHashableRepresentation { + // Must be @nonobjc to avoid infinite recursion during bridging + @nonobjc + public func _toCustomAnyHashable() -> AnyHashable? { + return AnyHashable(self as! Array) + } +} + extension NSArray : Sequence { /// Return an *iterator* over the elements of this *sequence*. /// diff --git a/stdlib/public/SDK/Foundation/NSDictionary.swift b/stdlib/public/SDK/Foundation/NSDictionary.swift index 57eb4cdf40926..eeb3de3d6c94d 100644 --- a/stdlib/public/SDK/Foundation/NSDictionary.swift +++ b/stdlib/public/SDK/Foundation/NSDictionary.swift @@ -138,6 +138,14 @@ extension Dictionary : _ObjectiveCBridgeable { } } +extension NSDictionary : _HasCustomAnyHashableRepresentation { + // Must be @nonobjc to avoid infinite recursion during bridging + @nonobjc + public func _toCustomAnyHashable() -> AnyHashable? { + return AnyHashable(self as! Dictionary) + } +} + extension NSDictionary : Sequence { // FIXME: A class because we can't pass a struct with class fields through an // [objc] interface without prematurely destroying the references. diff --git a/validation-test/stdlib/AnyHashable.swift.gyb b/validation-test/stdlib/AnyHashable.swift.gyb index 4a5ee24863f57..fcdefb0876b7a 100644 --- a/validation-test/stdlib/AnyHashable.swift.gyb +++ b/validation-test/stdlib/AnyHashable.swift.gyb @@ -767,6 +767,90 @@ AnyHashableTests.test("AnyHashable(Wrappers)/Hashable") { allowBrokenTransitivity: true) } +AnyHashableTests.test("AnyHashable(Set)/Hashable") { + let values: [AnyHashable] = [ + Set([1, 2, 3]), + NSSet(set: [1, 2, 3]), + Set([2, 3, 4]), + NSSet(set: [2, 3, 4]), + Set([Set([1, 2]), Set([3, 4])]), + NSSet(set: [NSSet(set: [1, 2]), NSSet(set: [3, 4])]), + Set([Set([1, 3]), Set([2, 4])]), + NSSet(set: [NSSet(set: [1, 3]), NSSet(set: [2, 4])]), + ] + + func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool { + switch (lhs, rhs) { + case (0...1, 0...1): return true + case (2...3, 2...3): return true + case (4...5, 4...5): return true + case (6...7, 6...7): return true + default: return false + } + } + + checkHashable(values, equalityOracle: equalityOracle, + allowBrokenTransitivity: true) +} + +AnyHashableTests.test("AnyHashable(Array)/Hashable") { + let values: [AnyHashable] = [ + [1, 2, 3], + NSArray(array: [1, 2, 3]), + [3, 2, 1], + NSArray(array: [3, 2, 1]), + [[1, 2], [3, 4]], + NSArray(array: [NSArray(array: [1, 2]), NSArray(array: [3, 4])]), + [[3, 4], [1, 2]], + NSArray(array: [NSArray(array: [3, 4]), NSArray(array: [1, 2])]), + ] + + func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool { + switch (lhs, rhs) { + case (0...1, 0...1): return true + case (2...3, 2...3): return true + case (4...5, 4...5): return true + case (6...7, 6...7): return true + default: return false + } + } + + checkHashable(values, equalityOracle: equalityOracle, + allowBrokenTransitivity: true) +} + +AnyHashableTests.test("AnyHashable(Dictionary)/Hashable") { + let values: [AnyHashable] = [ + ["hello": 1, "world": 2], + NSDictionary(dictionary: ["hello": 1, "world": 2]), + ["hello": 2, "world": 1], + NSDictionary(dictionary: ["hello": 2, "world": 1]), + ["hello": ["foo": 1, "bar": 2], + "world": ["foo": 2, "bar": 1]], + NSDictionary(dictionary: [ + "hello": ["foo": 1, "bar": 2], + "world": ["foo": 2, "bar": 1]]), + ["hello": ["foo": 2, "bar": 1], + "world": ["foo": 1, "bar": 2]], + NSDictionary(dictionary: [ + "hello": ["foo": 2, "bar": 1], + "world": ["foo": 1, "bar": 2]]), + ] + + func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool { + switch (lhs, rhs) { + case (0...1, 0...1): return true + case (2...3, 2...3): return true + case (4...5, 4...5): return true + case (6...7, 6...7): return true + default: return false + } + } + + checkHashable(values, equalityOracle: equalityOracle, + allowBrokenTransitivity: true) +} + AnyHashableTests.test("AnyHashable(_SwiftNativeNSError(MinimalHashablePODSwiftError))/Hashable") { let swiftErrors: [MinimalHashablePODSwiftError] = [ .caseA, .caseA, diff --git a/validation-test/stdlib/AnyHashableDiagnostics.swift b/validation-test/stdlib/AnyHashableDiagnostics.swift deleted file mode 100644 index be5bb5e4615e9..0000000000000 --- a/validation-test/stdlib/AnyHashableDiagnostics.swift +++ /dev/null @@ -1,13 +0,0 @@ -// RUN: %target-typecheck-verify-swift - -// If this test fails, the following types started to conditionally conform to -// `Hashable`. When that happens, please add a custom `AnyHashable` -// representation to corresponding Objective-C types. - -func isHashable(_: T.Type) {} - -isHashable(Int.self) // no-error // Test that `isHashable(_:)` works. - -isHashable(Array.self) // expected-error {{'Array' does not conform to expected type 'Hashable'}} -isHashable(Dictionary.self) // expected-error {{'Dictionary' does not conform to expected type 'Hashable'}} - From 6e6a16f5bc83e6a81b5735b14662ccee482b0ba3 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Sun, 11 Feb 2018 01:38:20 +0000 Subject: [PATCH 04/19] [stdlib] Describe availability of new conformances and members This assumes these will land in Swift 4.1; the attributes need to be adjusted if that turns out not to be the case. It seems @available for protocol conformances is not yet functional. I added attributes for those anyway, marked with FIXME(conformance-availability). # Conflicts: # stdlib/public/core/ExistentialCollection.swift.gyb # stdlib/public/core/Mirror.swift --- stdlib/public/core/Arrays.swift.gyb | 4 +- stdlib/public/core/ClosedRange.swift | 4 +- stdlib/public/core/CollectionOfOne.swift | 4 ++ stdlib/public/core/EmptyCollection.swift | 2 + .../core/ExistentialCollection.swift.gyb | 4 ++ .../public/core/HashedCollections.swift.gyb | 2 + stdlib/public/core/Mirror.swift | 4 ++ stdlib/public/core/Optional.swift | 2 + stdlib/public/core/Range.swift | 2 + validation-test/stdlib/Dictionary.swift | 25 ------------ validation-test/stdlib/Dictionary4.swift | 39 +++++++++++++++++++ 11 files changed, 65 insertions(+), 27 deletions(-) create mode 100644 validation-test/stdlib/Dictionary4.swift diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index 5746ebbb73b52..b7b02e3f47772 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -2250,7 +2250,8 @@ extension ${Self} : Equatable where Element : Equatable { } } -extension ${Self} : Hashable where Element : Hashable { +@available(swift, introduced: 4.1) // FIXME(conformance-availability) +extension ${Self}: Hashable where Element : Hashable { /// The hash value for the array. /// /// Two arrays that are equal will always have equal hash values. @@ -2258,6 +2259,7 @@ extension ${Self} : Hashable where Element : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to Array too var result: Int = 0 diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index 9f94b66eb8db4..622cbaec49e4a 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -167,8 +167,10 @@ extension ClosedRange.Index : Comparable { } } -extension ClosedRange.Index: Hashable +@available(swift, introduced: 4.1) // FIXME(conformance-availability) +extension ClosedRange.Index: Hashable where Bound: Strideable, Bound.Stride: SignedInteger, Bound: Hashable { + @available(swift, introduced: 4.1) public var hashValue: Int { switch self { case .inRange(let value): diff --git a/stdlib/public/core/CollectionOfOne.swift b/stdlib/public/core/CollectionOfOne.swift index a108980f90ce4..41c8c06c812f5 100644 --- a/stdlib/public/core/CollectionOfOne.swift +++ b/stdlib/public/core/CollectionOfOne.swift @@ -136,14 +136,17 @@ extension CollectionOfOne: RandomAccessCollection, MutableCollection { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension CollectionOfOne : Equatable where Element : Equatable { /// Returns a Boolean value indicating whether two collections are equal. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public static func == (lhs: CollectionOfOne, rhs: CollectionOfOne) -> Bool { return lhs._element == rhs._element } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension CollectionOfOne : Hashable where Element : Hashable { /// The hash value for the collection. /// @@ -152,6 +155,7 @@ extension CollectionOfOne : Hashable where Element : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return _element.hashValue } diff --git a/stdlib/public/core/EmptyCollection.swift b/stdlib/public/core/EmptyCollection.swift index ca85ff26fd009..fb1d5411ffb24 100644 --- a/stdlib/public/core/EmptyCollection.swift +++ b/stdlib/public/core/EmptyCollection.swift @@ -173,8 +173,10 @@ extension EmptyCollection : Equatable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension EmptyCollection : Hashable { @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return 0 } diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index ef87aed867600..3f3cdc2a2f577 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -1244,8 +1244,10 @@ extension ${Self}: _AnyCollectionProtocol { } % end +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension AnyCollection : Equatable where Element : Equatable { @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public static func == ( lhs: AnyCollection, rhs: AnyCollection ) -> Bool { @@ -1256,6 +1258,7 @@ extension AnyCollection : Equatable where Element : Equatable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension AnyCollection : Hashable where Element : Hashable { /// The hash value for the collection. /// @@ -1265,6 +1268,7 @@ extension AnyCollection : Hashable where Element : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to AnyCollection too var result: Int = 0 diff --git a/stdlib/public/core/HashedCollections.swift.gyb b/stdlib/public/core/HashedCollections.swift.gyb index 8ecac4861a1f0..525ea4ba5f2f4 100644 --- a/stdlib/public/core/HashedCollections.swift.gyb +++ b/stdlib/public/core/HashedCollections.swift.gyb @@ -2786,6 +2786,7 @@ extension Dictionary : Equatable where Value : Equatable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension Dictionary : Hashable where Value : Hashable { /// The hash value for the dictionary. /// @@ -2794,6 +2795,7 @@ extension Dictionary : Hashable where Value : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to Dictionary too var result: Int = 0 diff --git a/stdlib/public/core/Mirror.swift b/stdlib/public/core/Mirror.swift index 526590d1c9eda..8dfac3b8e8c32 100644 --- a/stdlib/public/core/Mirror.swift +++ b/stdlib/public/core/Mirror.swift @@ -905,8 +905,10 @@ extension DictionaryLiteral : RandomAccessCollection { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension DictionaryLiteral: Equatable where Key: Equatable, Value: Equatable { @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public static func == ( lhs: DictionaryLiteral, rhs: DictionaryLiteral ) -> Bool { @@ -917,6 +919,7 @@ extension DictionaryLiteral: Equatable where Key: Equatable, Value: Equatable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension DictionaryLiteral: Hashable where Key: Hashable, Value: Hashable { /// The hash value for the collection. /// @@ -926,6 +929,7 @@ extension DictionaryLiteral: Hashable where Key: Hashable, Value: Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to DictionaryLiteral too var result: Int = 0 diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index 0f3832cfecc77..4297ebdb6a6bc 100644 --- a/stdlib/public/core/Optional.swift +++ b/stdlib/public/core/Optional.swift @@ -409,6 +409,7 @@ extension Optional : Equatable where Wrapped : Equatable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension Optional : Hashable where Wrapped : Hashable { /// The hash value for the optional. /// @@ -417,6 +418,7 @@ extension Optional : Hashable where Wrapped : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { var result: Int switch self { diff --git a/stdlib/public/core/Range.swift b/stdlib/public/core/Range.swift index a83ee66783a8e..98b312f242dfa 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -358,6 +358,7 @@ extension Range: Equatable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension Range : Hashable where Bound : Hashable { /// The hash value for the range. /// @@ -366,6 +367,7 @@ extension Range : Hashable where Bound : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return _combineHashValues(lowerBound.hashValue, upperBound.hashValue) } diff --git a/validation-test/stdlib/Dictionary.swift b/validation-test/stdlib/Dictionary.swift index 6fd57d0f03b0c..b2117d6597376 100644 --- a/validation-test/stdlib/Dictionary.swift +++ b/validation-test/stdlib/Dictionary.swift @@ -83,31 +83,6 @@ DictionaryTestSuite.test("Index.Hashable") { expectNotNil(e[d.startIndex]) } -DictionaryTestSuite.test("Hashable") { - let d1: Dictionary = [1: "meow", 2: "meow", 3: "meow"] - let d2: Dictionary = [1: "meow", 2: "meow", 3: "mooo"] - let d3: Dictionary = [1: "meow", 2: "meow", 4: "meow"] - let d4: Dictionary = [1: "meow", 2: "meow", 4: "mooo"] - checkHashable([d1, d2, d3, d4], equalityOracle: { $0 == $1 }) - - let dd1: Dictionary> = [1: [2: "meow"]] - let dd2: Dictionary> = [2: [1: "meow"]] - let dd3: Dictionary> = [2: [2: "meow"]] - let dd4: Dictionary> = [1: [1: "meow"]] - let dd5: Dictionary> = [2: [2: "mooo"]] - let dd6: Dictionary> = [2: [:]] - let dd7: Dictionary> = [:] - checkHashable([dd1, dd2, dd3, dd4, dd5, dd6, dd7], equalityOracle: { $0 == $1 }) - - // Check that hash is equal even though dictionary is traversed differently - var d5: Dictionary = [1: "meow", 2: "meow", 3: "mooo", 4: "woof", 5: "baah", 6: "mooo"] - let expected = d5.hashValue - for capacity in [4, 8, 16, 32, 64, 128, 256] { - d5.reserveCapacity(capacity) - expectEqual(d5.hashValue, expected) - } -} - DictionaryTestSuite.test("valueDestruction") { var d1 = Dictionary() for i in 100...110 { diff --git a/validation-test/stdlib/Dictionary4.swift b/validation-test/stdlib/Dictionary4.swift new file mode 100644 index 0000000000000..aba0cc5977cb0 --- /dev/null +++ b/validation-test/stdlib/Dictionary4.swift @@ -0,0 +1,39 @@ +// RUN: rm -rf %t ; mkdir -p %t +// RUN: %target-build-swift %s -o %t/a.out -swift-version 4 && %target-run %t/a.out + +// REQUIRES: executable_test + +import StdlibUnittest +import StdlibCollectionUnittest + +var DictionaryTestSuite = TestSuite("Dictionary4") + +DictionaryTestSuite.test("Hashable") { + let d1: Dictionary = [1: "meow", 2: "meow", 3: "meow"] + let d2: Dictionary = [1: "meow", 2: "meow", 3: "mooo"] + let d3: Dictionary = [1: "meow", 2: "meow", 4: "meow"] + let d4: Dictionary = [1: "meow", 2: "meow", 4: "mooo"] + checkHashable([d1, d2, d3, d4], equalityOracle: { $0 == $1 }) + + let dd1: Dictionary> = [1: [2: "meow"]] + let dd2: Dictionary> = [2: [1: "meow"]] + let dd3: Dictionary> = [2: [2: "meow"]] + let dd4: Dictionary> = [1: [1: "meow"]] + let dd5: Dictionary> = [2: [2: "mooo"]] + let dd6: Dictionary> = [2: [:]] + let dd7: Dictionary> = [:] + checkHashable( + [dd1, dd2, dd3, dd4, dd5, dd6, dd7], + equalityOracle: { $0 == $1 }) + + // Check that hash is equal even though dictionary is traversed differently + var d5: Dictionary = + [1: "meow", 2: "meow", 3: "mooo", 4: "woof", 5: "baah", 6: "mooo"] + let expected = d5.hashValue + for capacity in [4, 8, 16, 32, 64, 128, 256] { + d5.reserveCapacity(capacity) + expectEqual(d5.hashValue, expected) + } +} + +runAllTests() From ef9319e21395fec48968017d7ce19dee033892c6 Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Sun, 11 Feb 2018 01:49:02 +0000 Subject: [PATCH 05/19] =?UTF-8?q?[ClangImporter]=20Don=E2=80=99t=20import?= =?UTF-8?q?=20Array/Dictionary=20as=20such=20in=20Hashable=20contexts=20ye?= =?UTF-8?q?t?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Array and Dictionary are now conditionally Hashable, so the importer wants to use them when importing NSArray and NSDictionary types in hashable contexts. Unfortunately, this currently means that a type like NSSet *> * gets imported as Set>, which is invalid — Dictionary.Value needs to be Hashable, too: Set> For now, work around this by explicitly turning NSArray and NSDictionary into AnyHashable when they are used as the first type parameter of NSSet or NSDictionary, ignoring Hashable conformance in this case. This reverts to the previous behavior. --- lib/ClangImporter/ImportType.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 00c7b69c4212f..7c206bfb51a7c 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -998,7 +998,14 @@ namespace { if (unboundDecl == Impl.SwiftContext.getDictionaryDecl() || unboundDecl == Impl.SwiftContext.getSetDecl()) { auto &keyType = importedTypeArgs[0]; - if (!Impl.matchesHashableBound(keyType)) { + if (!Impl.matchesHashableBound(keyType) || + // Dictionary and Array conditionally conform to Hashable, + // but the conformance doesn't necessarily apply with the + // imported versions of their type arguments. + // FIXME: Import their non-Hashable type parameters as + // AnyHashable in this context. + keyType->getStructOrBoundGenericStruct() == Impl.SwiftContext.getDictionaryDecl() || + keyType->getStructOrBoundGenericStruct() == Impl.SwiftContext.getArrayDecl()) { if (auto anyHashable = Impl.SwiftContext.getAnyHashableDecl()) keyType = anyHashable->getDeclaredType(); else From 1f3989651c0290ce31eef90efdb60ebf6a57a90c Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sat, 10 Feb 2018 20:21:12 -0600 Subject: [PATCH 06/19] [stdlib] Add availability annotation to other new conformances and members --- stdlib/public/core/DropWhile.swift.gyb | 2 ++ stdlib/public/core/Flatten.swift | 2 ++ stdlib/public/core/PrefixWhile.swift.gyb | 2 ++ stdlib/public/core/Reverse.swift | 2 ++ 4 files changed, 8 insertions(+) diff --git a/stdlib/public/core/DropWhile.swift.gyb b/stdlib/public/core/DropWhile.swift.gyb index c15e285fcc09b..216e17152a6cd 100644 --- a/stdlib/public/core/DropWhile.swift.gyb +++ b/stdlib/public/core/DropWhile.swift.gyb @@ -140,7 +140,9 @@ public struct LazyDropWhileIndex : Comparable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension LazyDropWhileIndex : Hashable where Base.Index : Hashable { + @available(swift, introduced: 4.1) public var hashValue: Int { return base.hashValue } diff --git a/stdlib/public/core/Flatten.swift b/stdlib/public/core/Flatten.swift index a3c806c65365a..b7e4382b23c74 100644 --- a/stdlib/public/core/Flatten.swift +++ b/stdlib/public/core/Flatten.swift @@ -230,8 +230,10 @@ extension FlattenCollection.Index : Comparable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension FlattenCollection.Index : Hashable where Base.Index : Hashable, Base.Element.Index : Hashable { + @available(swift, introduced: 4.1) public var hashValue: Int { return _mixInt(_inner?.hashValue ?? 0) ^ _outer.hashValue } diff --git a/stdlib/public/core/PrefixWhile.swift.gyb b/stdlib/public/core/PrefixWhile.swift.gyb index 91bc14a61cc12..c4ffbe035631f 100644 --- a/stdlib/public/core/PrefixWhile.swift.gyb +++ b/stdlib/public/core/PrefixWhile.swift.gyb @@ -162,7 +162,9 @@ public struct LazyPrefixWhileIndex : Comparable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension LazyPrefixWhileIndex : Hashable where Base.Index : Hashable { + @available(swift, introduced: 4.1) public var hashValue: Int { switch _value { case .index(let value): diff --git a/stdlib/public/core/Reverse.swift b/stdlib/public/core/Reverse.swift index ad08943de8241..faae54b370669 100644 --- a/stdlib/public/core/Reverse.swift +++ b/stdlib/public/core/Reverse.swift @@ -193,7 +193,9 @@ extension ReversedCollection.Index: Comparable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ReversedCollection.Index: Hashable where Base.Index: Hashable { + @available(swift, introduced: 4.1) public var hashValue: Int { return base.hashValue } From 0582667f3d79cd3c31075774a7bf35b24378028f Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Sun, 11 Feb 2018 02:44:50 +0000 Subject: [PATCH 07/19] [stdlib] ClosedRange: Describe availability of Hashable conformance --- stdlib/public/core/ClosedRange.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index 622cbaec49e4a..7f2f4b97c7fe8 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -378,6 +378,7 @@ extension ClosedRange: Equatable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ClosedRange : Hashable where Bound : Hashable { /// The hash value for the range. /// @@ -386,6 +387,7 @@ extension ClosedRange : Hashable where Bound : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return _combineHashValues(lowerBound.hashValue, upperBound.hashValue) } From b89a9ff3a68b049fb075b37d7adee73927b764fa Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sat, 10 Feb 2018 21:19:20 -0600 Subject: [PATCH 08/19] [docs] Update CHANGELOG.md for conditional conformance --- CHANGELOG.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3928c93936f6..f7bfa6eeb8a1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,15 @@ Swift 5.0 Swift 4.1 --------- +* [SE-0143][] + + The standard library types `Optional`, `Array`, `ArraySlice`, + `ContiguousArray`, `Dictionary`, `DictionaryLiteral`, `Range`, `ClosedRange`, + `AnyCollection`, and `CollectionOfOne` now conform to the `Hashable` protocol + when their element or bound types (as the case may be) conform to `Hashable`. + This makes synthesized `Hashable` implementations available for types that + include stored properties of these types. + * [SE-0189][] If an initializer is declared in a different module from a struct, it must @@ -74,12 +83,12 @@ Swift 4.1 s[keyPath: p] // "H" ``` -* [SE-0143][] The standard library types `Optional`, `Array`, and - `Dictionary` now conform to the `Equatable` protocol when their element types - conform to `Equatable`. This allows the `==` operator to compose (e.g., one - can compare two values of type `[Int : [Int?]?]` with `==`), as well as use - various algorthims defined for `Equatable` element types, such as - `index(of:)`. +* [SE-0143][] The standard library types `Optional`, `Array`, `ArraySlice`, + `ContiguousArray`, and `Dictionary` now conform to the `Equatable` protocol + when their element types conform to `Equatable`. This allows the `==` operator + to compose (e.g., one can compare two values of type `[Int : [Int?]?]` with + `==`), as well as use various algorthims defined for `Equatable` element + types, such as `index(of:)`. * [SE-0157][] is implemented. Associated types can now declare "recursive" constraints, which require that the associated type conform to the enclosing From b0f38e0b18687061a1f36b87dac3427083e277e2 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sat, 10 Feb 2018 23:59:37 -0600 Subject: [PATCH 09/19] Use @_implements to avoid source breakage in conditional conformances --- include/swift/AST/KnownIdentifiers.def | 4 ++-- .../DerivedConformanceEquatableHashable.cpp | 20 +++++++++---------- lib/Sema/TypeCheckStmt.cpp | 4 ++-- stdlib/public/core/Arrays.swift.gyb | 1 - stdlib/public/core/ClosedRange.swift | 5 ++--- stdlib/public/core/CollectionOfOne.swift | 7 ++++--- stdlib/public/core/DropWhile.swift.gyb | 2 +- stdlib/public/core/EmptyCollection.swift | 1 - .../core/ExistentialCollection.swift.gyb | 7 +++---- stdlib/public/core/Flatten.swift | 2 +- .../public/core/HashedCollections.swift.gyb | 1 - stdlib/public/core/Mirror.swift | 7 +++---- stdlib/public/core/Optional.swift | 1 - stdlib/public/core/PrefixWhile.swift.gyb | 2 +- stdlib/public/core/Range.swift | 1 - stdlib/public/core/Reverse.swift | 2 +- test/IDE/print_ast_tc_decls.swift | 4 ++-- 17 files changed, 32 insertions(+), 39 deletions(-) diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 0b7f4d4a36dd9..47bbe871edd17 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -114,8 +114,8 @@ IDENTIFIER_WITH_NAME(NativeClassLayout, "_NativeClass") // Operators IDENTIFIER_WITH_NAME(MatchOperator, "~=") IDENTIFIER_WITH_NAME(EqualsOperator, "==") -IDENTIFIER_WITH_NAME(derived_enum_equals, "__derived_enum_equals") -IDENTIFIER_WITH_NAME(derived_struct_equals, "__derived_struct_equals") +IDENTIFIER_WITH_NAME(derived_conformance__equals, "__derived_conformance__equals") +IDENTIFIER_WITH_NAME(conditional_conformance__equals, "__conditional_conformance__equals") // Precedence groups IDENTIFIER(AssignmentPrecedence) diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 0ea6c331fe1a8..0e25b5ba550a7 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -326,7 +326,7 @@ static GuardStmt *returnIfNotEqualGuard(ASTContext &C, /// values. This generates code that converts each value to its integer ordinal /// and compares them, which produces an optimal single icmp instruction. static void -deriveBodyEquatable_enum_noAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { +deriveBodyEquatable_enumWithoutAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { auto parentDC = eqDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); @@ -381,7 +381,7 @@ deriveBodyEquatable_enum_noAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { /// Derive the body for an '==' operator for an enum where at least one of the /// cases has associated values. static void -deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { +deriveBodyEquatable_enumWithAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { auto parentDC = eqDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); @@ -555,8 +555,8 @@ deriveEquatable_eq(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, // // @derived // @_implements(Equatable, ==(_:_:)) - // func __derived_enum_equals(a: SomeEnum, - // b: SomeEnum) -> Bool { + // func __derived_conformance__equals(a: SomeEnum, + // b: SomeEnum) -> Bool { // switch (a, b) { // case (.A, .A): // return true @@ -577,8 +577,8 @@ deriveEquatable_eq(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, // // @derived // @_implements(Equatable, ==(_:_:)) - // func __derived_struct_equals(a: SomeStruct, - // b: SomeStruct) -> Bool { + // func __derived_conformance__equals(a: SomeStruct, + // b: SomeStruct) -> Bool { // guard a.x == b.x else { return false; } // guard a.y == b.y else { return false; } // return true; @@ -712,15 +712,15 @@ ValueDecl *DerivedConformance::deriveEquatable(TypeChecker &tc, if (theEnum) { auto bodySynthesizer = theEnum->hasOnlyCasesWithoutAssociatedValues() - ? &deriveBodyEquatable_enum_noAssociatedValues_eq - : &deriveBodyEquatable_enum_hasAssociatedValues_eq; + ? &deriveBodyEquatable_enumWithoutAssociatedValues_eq + : &deriveBodyEquatable_enumWithAssociatedValues_eq; return deriveEquatable_eq(tc, parentDecl, theEnum, - tc.Context.Id_derived_enum_equals, + tc.Context.Id_derived_conformance__equals, bodySynthesizer); } else if (auto theStruct = dyn_cast(type)) return deriveEquatable_eq(tc, parentDecl, theStruct, - tc.Context.Id_derived_struct_equals, + tc.Context.Id_derived_conformance__equals, &deriveBodyEquatable_struct_eq); else llvm_unreachable("todo"); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index d9fb64b259b2a..5bcd8b2d1cafe 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1255,8 +1255,8 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { auto &ctx = callee->getASTContext(); if (callee->isImplicit()) { // Translate calls to implicit functions to their user-facing names - if (callee->getBaseName() == ctx.Id_derived_enum_equals || - callee->getBaseName() == ctx.Id_derived_struct_equals) { + if (callee->getBaseName() == ctx.Id_derived_conformance__equals || + callee->getBaseName() == ctx.Id_conditional_conformance__equals) { diagnose(fn->getLoc(), diag::expression_unused_result_operator, ctx.Id_EqualsOperator) .highlight(SR1).highlight(SR2); diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index b7b02e3f47772..814c9a6f2948e 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -2259,7 +2259,6 @@ extension ${Self}: Hashable where Element : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to Array too var result: Int = 0 diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index 7f2f4b97c7fe8..e8b3a47d2f3ae 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -170,7 +170,7 @@ extension ClosedRange.Index : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ClosedRange.Index: Hashable where Bound: Strideable, Bound.Stride: SignedInteger, Bound: Hashable { - @available(swift, introduced: 4.1) + @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { switch self { case .inRange(let value): @@ -387,7 +387,6 @@ extension ClosedRange : Hashable where Bound : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { return _combineHashValues(lowerBound.hashValue, upperBound.hashValue) } @@ -395,7 +394,7 @@ extension ClosedRange : Hashable where Bound : Hashable { extension ClosedRange : CustomStringConvertible { /// A textual representation of the range. - @_inlineable // FIXME(sil-serialize-all)...\( + @_inlineable // FIXME(sil-serialize-all) public var description: String { return "\(lowerBound)...\(upperBound)" } diff --git a/stdlib/public/core/CollectionOfOne.swift b/stdlib/public/core/CollectionOfOne.swift index 41c8c06c812f5..e451b1186af6e 100644 --- a/stdlib/public/core/CollectionOfOne.swift +++ b/stdlib/public/core/CollectionOfOne.swift @@ -140,8 +140,10 @@ extension CollectionOfOne: RandomAccessCollection, MutableCollection { extension CollectionOfOne : Equatable where Element : Equatable { /// Returns a Boolean value indicating whether two collections are equal. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) - public static func == (lhs: CollectionOfOne, rhs: CollectionOfOne) -> Bool { + @_implements(Equatable, ==(_:_:)) + public static func __conditional_conformance__equals( + _ lhs: CollectionOfOne, _ rhs: CollectionOfOne + ) -> Bool { return lhs._element == rhs._element } } @@ -155,7 +157,6 @@ extension CollectionOfOne : Hashable where Element : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { return _element.hashValue } diff --git a/stdlib/public/core/DropWhile.swift.gyb b/stdlib/public/core/DropWhile.swift.gyb index 216e17152a6cd..e117c0b81e52e 100644 --- a/stdlib/public/core/DropWhile.swift.gyb +++ b/stdlib/public/core/DropWhile.swift.gyb @@ -142,7 +142,7 @@ public struct LazyDropWhileIndex : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension LazyDropWhileIndex : Hashable where Base.Index : Hashable { - @available(swift, introduced: 4.1) + @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { return base.hashValue } diff --git a/stdlib/public/core/EmptyCollection.swift b/stdlib/public/core/EmptyCollection.swift index fb1d5411ffb24..01bd1c3d22e16 100644 --- a/stdlib/public/core/EmptyCollection.swift +++ b/stdlib/public/core/EmptyCollection.swift @@ -176,7 +176,6 @@ extension EmptyCollection : Equatable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension EmptyCollection : Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { return 0 } diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index 3f3cdc2a2f577..06cdb6f92415a 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -1247,9 +1247,9 @@ extension ${Self}: _AnyCollectionProtocol { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension AnyCollection : Equatable where Element : Equatable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) - public static func == ( - lhs: AnyCollection, rhs: AnyCollection + @_implements(Equatable, ==(_:_:)) + public static func __conditional_conformance__equals( + _ lhs: AnyCollection, _ rhs: AnyCollection ) -> Bool { if lhs.count != rhs.count { return false @@ -1268,7 +1268,6 @@ extension AnyCollection : Hashable where Element : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to AnyCollection too var result: Int = 0 diff --git a/stdlib/public/core/Flatten.swift b/stdlib/public/core/Flatten.swift index b7e4382b23c74..e9d4d7242755f 100644 --- a/stdlib/public/core/Flatten.swift +++ b/stdlib/public/core/Flatten.swift @@ -233,7 +233,7 @@ extension FlattenCollection.Index : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension FlattenCollection.Index : Hashable where Base.Index : Hashable, Base.Element.Index : Hashable { - @available(swift, introduced: 4.1) + @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { return _mixInt(_inner?.hashValue ?? 0) ^ _outer.hashValue } diff --git a/stdlib/public/core/HashedCollections.swift.gyb b/stdlib/public/core/HashedCollections.swift.gyb index 525ea4ba5f2f4..eef55d48b1be4 100644 --- a/stdlib/public/core/HashedCollections.swift.gyb +++ b/stdlib/public/core/HashedCollections.swift.gyb @@ -2795,7 +2795,6 @@ extension Dictionary : Hashable where Value : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to Dictionary too var result: Int = 0 diff --git a/stdlib/public/core/Mirror.swift b/stdlib/public/core/Mirror.swift index 8dfac3b8e8c32..d051ca72fb6bf 100644 --- a/stdlib/public/core/Mirror.swift +++ b/stdlib/public/core/Mirror.swift @@ -908,9 +908,9 @@ extension DictionaryLiteral : RandomAccessCollection { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension DictionaryLiteral: Equatable where Key: Equatable, Value: Equatable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) - public static func == ( - lhs: DictionaryLiteral, rhs: DictionaryLiteral + @_implements(Equatable, ==(_:_:)) + public static func __conditional_conformance__equals( + _ lhs: DictionaryLiteral, _ rhs: DictionaryLiteral ) -> Bool { if lhs.count != rhs.count { return false @@ -929,7 +929,6 @@ extension DictionaryLiteral: Hashable where Key: Hashable, Value: Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to DictionaryLiteral too var result: Int = 0 diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index 4297ebdb6a6bc..69826f2a5328c 100644 --- a/stdlib/public/core/Optional.swift +++ b/stdlib/public/core/Optional.swift @@ -418,7 +418,6 @@ extension Optional : Hashable where Wrapped : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { var result: Int switch self { diff --git a/stdlib/public/core/PrefixWhile.swift.gyb b/stdlib/public/core/PrefixWhile.swift.gyb index c4ffbe035631f..65de0945d37b8 100644 --- a/stdlib/public/core/PrefixWhile.swift.gyb +++ b/stdlib/public/core/PrefixWhile.swift.gyb @@ -164,7 +164,7 @@ public struct LazyPrefixWhileIndex : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension LazyPrefixWhileIndex : Hashable where Base.Index : Hashable { - @available(swift, introduced: 4.1) + @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { switch _value { case .index(let value): diff --git a/stdlib/public/core/Range.swift b/stdlib/public/core/Range.swift index 98b312f242dfa..a75d81f96150e 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -367,7 +367,6 @@ extension Range : Hashable where Bound : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { return _combineHashValues(lowerBound.hashValue, upperBound.hashValue) } diff --git a/stdlib/public/core/Reverse.swift b/stdlib/public/core/Reverse.swift index faae54b370669..379b0834b1d41 100644 --- a/stdlib/public/core/Reverse.swift +++ b/stdlib/public/core/Reverse.swift @@ -195,7 +195,7 @@ extension ReversedCollection.Index: Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ReversedCollection.Index: Hashable where Base.Index: Hashable { - @available(swift, introduced: 4.1) + @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { return base.hashValue } diff --git a/test/IDE/print_ast_tc_decls.swift b/test/IDE/print_ast_tc_decls.swift index 11b19f17bd11b..071e444dd20ae 100644 --- a/test/IDE/print_ast_tc_decls.swift +++ b/test/IDE/print_ast_tc_decls.swift @@ -585,7 +585,7 @@ struct d0200_EscapedIdentifiers { } // PASS_COMMON-NEXT: {{^}} enum `enum` {{{$}} // PASS_COMMON-NEXT: {{^}} case `case`{{$}} -// PASS_COMMON-NEXT: {{^}} {{.*}}static func __derived_enum_equals(_ a: d0200_EscapedIdentifiers.`enum`, _ b: d0200_EscapedIdentifiers.`enum`) -> Bool +// PASS_COMMON-NEXT: {{^}} {{.*}}static func __derived_conformance__equals(_ a: d0200_EscapedIdentifiers.`enum`, _ b: d0200_EscapedIdentifiers.`enum`) -> Bool // PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}} // PASS_COMMON-NEXT: {{^}} }{{$}} @@ -1013,7 +1013,7 @@ enum d2000_EnumDecl1 { // PASS_COMMON: {{^}}enum d2000_EnumDecl1 {{{$}} // PASS_COMMON-NEXT: {{^}} case ED1_First{{$}} // PASS_COMMON-NEXT: {{^}} case ED1_Second{{$}} -// PASS_COMMON-NEXT: {{^}} {{.*}}static func __derived_enum_equals(_ a: d2000_EnumDecl1, _ b: d2000_EnumDecl1) -> Bool +// PASS_COMMON-NEXT: {{^}} {{.*}}static func __derived_conformance__equals(_ a: d2000_EnumDecl1, _ b: d2000_EnumDecl1) -> Bool // PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}} // PASS_COMMON-NEXT: {{^}}}{{$}} From 9b5c23c2127d9cd626c865215929452ec472e141 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sun, 11 Feb 2018 00:57:47 -0600 Subject: [PATCH 10/19] Revert "Merge pull request #14536 from xwu/conditional-hashable-at-implements" This reverts commit b5a1d4b33c653b315043343860f2a3c3b5066ce2, reversing changes made to d03f0b366536477be201bf6e1f87be515d9e055e. --- include/swift/AST/KnownIdentifiers.def | 4 ++-- .../DerivedConformanceEquatableHashable.cpp | 20 +++++++++---------- lib/Sema/TypeCheckStmt.cpp | 4 ++-- stdlib/public/core/Arrays.swift.gyb | 1 + stdlib/public/core/ClosedRange.swift | 5 +++-- stdlib/public/core/CollectionOfOne.swift | 7 +++---- stdlib/public/core/DropWhile.swift.gyb | 2 +- stdlib/public/core/EmptyCollection.swift | 1 + .../core/ExistentialCollection.swift.gyb | 7 ++++--- stdlib/public/core/Flatten.swift | 2 +- .../public/core/HashedCollections.swift.gyb | 1 + stdlib/public/core/Mirror.swift | 7 ++++--- stdlib/public/core/Optional.swift | 1 + stdlib/public/core/PrefixWhile.swift.gyb | 2 +- stdlib/public/core/Range.swift | 1 + stdlib/public/core/Reverse.swift | 2 +- test/IDE/print_ast_tc_decls.swift | 4 ++-- 17 files changed, 39 insertions(+), 32 deletions(-) diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 47bbe871edd17..0b7f4d4a36dd9 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -114,8 +114,8 @@ IDENTIFIER_WITH_NAME(NativeClassLayout, "_NativeClass") // Operators IDENTIFIER_WITH_NAME(MatchOperator, "~=") IDENTIFIER_WITH_NAME(EqualsOperator, "==") -IDENTIFIER_WITH_NAME(derived_conformance__equals, "__derived_conformance__equals") -IDENTIFIER_WITH_NAME(conditional_conformance__equals, "__conditional_conformance__equals") +IDENTIFIER_WITH_NAME(derived_enum_equals, "__derived_enum_equals") +IDENTIFIER_WITH_NAME(derived_struct_equals, "__derived_struct_equals") // Precedence groups IDENTIFIER(AssignmentPrecedence) diff --git a/lib/Sema/DerivedConformanceEquatableHashable.cpp b/lib/Sema/DerivedConformanceEquatableHashable.cpp index 0e25b5ba550a7..0ea6c331fe1a8 100644 --- a/lib/Sema/DerivedConformanceEquatableHashable.cpp +++ b/lib/Sema/DerivedConformanceEquatableHashable.cpp @@ -326,7 +326,7 @@ static GuardStmt *returnIfNotEqualGuard(ASTContext &C, /// values. This generates code that converts each value to its integer ordinal /// and compares them, which produces an optimal single icmp instruction. static void -deriveBodyEquatable_enumWithoutAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { +deriveBodyEquatable_enum_noAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { auto parentDC = eqDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); @@ -381,7 +381,7 @@ deriveBodyEquatable_enumWithoutAssociatedValues_eq(AbstractFunctionDecl *eqDecl) /// Derive the body for an '==' operator for an enum where at least one of the /// cases has associated values. static void -deriveBodyEquatable_enumWithAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { +deriveBodyEquatable_enum_hasAssociatedValues_eq(AbstractFunctionDecl *eqDecl) { auto parentDC = eqDecl->getDeclContext(); ASTContext &C = parentDC->getASTContext(); @@ -555,8 +555,8 @@ deriveEquatable_eq(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, // // @derived // @_implements(Equatable, ==(_:_:)) - // func __derived_conformance__equals(a: SomeEnum, - // b: SomeEnum) -> Bool { + // func __derived_enum_equals(a: SomeEnum, + // b: SomeEnum) -> Bool { // switch (a, b) { // case (.A, .A): // return true @@ -577,8 +577,8 @@ deriveEquatable_eq(TypeChecker &tc, Decl *parentDecl, NominalTypeDecl *typeDecl, // // @derived // @_implements(Equatable, ==(_:_:)) - // func __derived_conformance__equals(a: SomeStruct, - // b: SomeStruct) -> Bool { + // func __derived_struct_equals(a: SomeStruct, + // b: SomeStruct) -> Bool { // guard a.x == b.x else { return false; } // guard a.y == b.y else { return false; } // return true; @@ -712,15 +712,15 @@ ValueDecl *DerivedConformance::deriveEquatable(TypeChecker &tc, if (theEnum) { auto bodySynthesizer = theEnum->hasOnlyCasesWithoutAssociatedValues() - ? &deriveBodyEquatable_enumWithoutAssociatedValues_eq - : &deriveBodyEquatable_enumWithAssociatedValues_eq; + ? &deriveBodyEquatable_enum_noAssociatedValues_eq + : &deriveBodyEquatable_enum_hasAssociatedValues_eq; return deriveEquatable_eq(tc, parentDecl, theEnum, - tc.Context.Id_derived_conformance__equals, + tc.Context.Id_derived_enum_equals, bodySynthesizer); } else if (auto theStruct = dyn_cast(type)) return deriveEquatable_eq(tc, parentDecl, theStruct, - tc.Context.Id_derived_conformance__equals, + tc.Context.Id_derived_struct_equals, &deriveBodyEquatable_struct_eq); else llvm_unreachable("todo"); diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index 5bcd8b2d1cafe..d9fb64b259b2a 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -1255,8 +1255,8 @@ void TypeChecker::checkIgnoredExpr(Expr *E) { auto &ctx = callee->getASTContext(); if (callee->isImplicit()) { // Translate calls to implicit functions to their user-facing names - if (callee->getBaseName() == ctx.Id_derived_conformance__equals || - callee->getBaseName() == ctx.Id_conditional_conformance__equals) { + if (callee->getBaseName() == ctx.Id_derived_enum_equals || + callee->getBaseName() == ctx.Id_derived_struct_equals) { diagnose(fn->getLoc(), diag::expression_unused_result_operator, ctx.Id_EqualsOperator) .highlight(SR1).highlight(SR2); diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index 814c9a6f2948e..b7b02e3f47772 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -2259,6 +2259,7 @@ extension ${Self}: Hashable where Element : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to Array too var result: Int = 0 diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index e8b3a47d2f3ae..7f2f4b97c7fe8 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -170,7 +170,7 @@ extension ClosedRange.Index : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ClosedRange.Index: Hashable where Bound: Strideable, Bound.Stride: SignedInteger, Bound: Hashable { - @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { switch self { case .inRange(let value): @@ -387,6 +387,7 @@ extension ClosedRange : Hashable where Bound : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return _combineHashValues(lowerBound.hashValue, upperBound.hashValue) } @@ -394,7 +395,7 @@ extension ClosedRange : Hashable where Bound : Hashable { extension ClosedRange : CustomStringConvertible { /// A textual representation of the range. - @_inlineable // FIXME(sil-serialize-all) + @_inlineable // FIXME(sil-serialize-all)...\( public var description: String { return "\(lowerBound)...\(upperBound)" } diff --git a/stdlib/public/core/CollectionOfOne.swift b/stdlib/public/core/CollectionOfOne.swift index e451b1186af6e..41c8c06c812f5 100644 --- a/stdlib/public/core/CollectionOfOne.swift +++ b/stdlib/public/core/CollectionOfOne.swift @@ -140,10 +140,8 @@ extension CollectionOfOne: RandomAccessCollection, MutableCollection { extension CollectionOfOne : Equatable where Element : Equatable { /// Returns a Boolean value indicating whether two collections are equal. @_inlineable // FIXME(sil-serialize-all) - @_implements(Equatable, ==(_:_:)) - public static func __conditional_conformance__equals( - _ lhs: CollectionOfOne, _ rhs: CollectionOfOne - ) -> Bool { + @available(swift, introduced: 4.1) + public static func == (lhs: CollectionOfOne, rhs: CollectionOfOne) -> Bool { return lhs._element == rhs._element } } @@ -157,6 +155,7 @@ extension CollectionOfOne : Hashable where Element : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return _element.hashValue } diff --git a/stdlib/public/core/DropWhile.swift.gyb b/stdlib/public/core/DropWhile.swift.gyb index e117c0b81e52e..216e17152a6cd 100644 --- a/stdlib/public/core/DropWhile.swift.gyb +++ b/stdlib/public/core/DropWhile.swift.gyb @@ -142,7 +142,7 @@ public struct LazyDropWhileIndex : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension LazyDropWhileIndex : Hashable where Base.Index : Hashable { - @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return base.hashValue } diff --git a/stdlib/public/core/EmptyCollection.swift b/stdlib/public/core/EmptyCollection.swift index 01bd1c3d22e16..fb1d5411ffb24 100644 --- a/stdlib/public/core/EmptyCollection.swift +++ b/stdlib/public/core/EmptyCollection.swift @@ -176,6 +176,7 @@ extension EmptyCollection : Equatable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension EmptyCollection : Hashable { @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return 0 } diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index 06cdb6f92415a..3f3cdc2a2f577 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -1247,9 +1247,9 @@ extension ${Self}: _AnyCollectionProtocol { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension AnyCollection : Equatable where Element : Equatable { @_inlineable // FIXME(sil-serialize-all) - @_implements(Equatable, ==(_:_:)) - public static func __conditional_conformance__equals( - _ lhs: AnyCollection, _ rhs: AnyCollection + @available(swift, introduced: 4.1) + public static func == ( + lhs: AnyCollection, rhs: AnyCollection ) -> Bool { if lhs.count != rhs.count { return false @@ -1268,6 +1268,7 @@ extension AnyCollection : Hashable where Element : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to AnyCollection too var result: Int = 0 diff --git a/stdlib/public/core/Flatten.swift b/stdlib/public/core/Flatten.swift index e9d4d7242755f..b7e4382b23c74 100644 --- a/stdlib/public/core/Flatten.swift +++ b/stdlib/public/core/Flatten.swift @@ -233,7 +233,7 @@ extension FlattenCollection.Index : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension FlattenCollection.Index : Hashable where Base.Index : Hashable, Base.Element.Index : Hashable { - @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return _mixInt(_inner?.hashValue ?? 0) ^ _outer.hashValue } diff --git a/stdlib/public/core/HashedCollections.swift.gyb b/stdlib/public/core/HashedCollections.swift.gyb index eef55d48b1be4..525ea4ba5f2f4 100644 --- a/stdlib/public/core/HashedCollections.swift.gyb +++ b/stdlib/public/core/HashedCollections.swift.gyb @@ -2795,6 +2795,7 @@ extension Dictionary : Hashable where Value : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to Dictionary too var result: Int = 0 diff --git a/stdlib/public/core/Mirror.swift b/stdlib/public/core/Mirror.swift index d051ca72fb6bf..8dfac3b8e8c32 100644 --- a/stdlib/public/core/Mirror.swift +++ b/stdlib/public/core/Mirror.swift @@ -908,9 +908,9 @@ extension DictionaryLiteral : RandomAccessCollection { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension DictionaryLiteral: Equatable where Key: Equatable, Value: Equatable { @_inlineable // FIXME(sil-serialize-all) - @_implements(Equatable, ==(_:_:)) - public static func __conditional_conformance__equals( - _ lhs: DictionaryLiteral, _ rhs: DictionaryLiteral + @available(swift, introduced: 4.1) + public static func == ( + lhs: DictionaryLiteral, rhs: DictionaryLiteral ) -> Bool { if lhs.count != rhs.count { return false @@ -929,6 +929,7 @@ extension DictionaryLiteral: Hashable where Key: Hashable, Value: Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to DictionaryLiteral too var result: Int = 0 diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index 69826f2a5328c..4297ebdb6a6bc 100644 --- a/stdlib/public/core/Optional.swift +++ b/stdlib/public/core/Optional.swift @@ -418,6 +418,7 @@ extension Optional : Hashable where Wrapped : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { var result: Int switch self { diff --git a/stdlib/public/core/PrefixWhile.swift.gyb b/stdlib/public/core/PrefixWhile.swift.gyb index 65de0945d37b8..c4ffbe035631f 100644 --- a/stdlib/public/core/PrefixWhile.swift.gyb +++ b/stdlib/public/core/PrefixWhile.swift.gyb @@ -164,7 +164,7 @@ public struct LazyPrefixWhileIndex : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension LazyPrefixWhileIndex : Hashable where Base.Index : Hashable { - @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { switch _value { case .index(let value): diff --git a/stdlib/public/core/Range.swift b/stdlib/public/core/Range.swift index a75d81f96150e..98b312f242dfa 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -367,6 +367,7 @@ extension Range : Hashable where Bound : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return _combineHashValues(lowerBound.hashValue, upperBound.hashValue) } diff --git a/stdlib/public/core/Reverse.swift b/stdlib/public/core/Reverse.swift index 379b0834b1d41..faae54b370669 100644 --- a/stdlib/public/core/Reverse.swift +++ b/stdlib/public/core/Reverse.swift @@ -195,7 +195,7 @@ extension ReversedCollection.Index: Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ReversedCollection.Index: Hashable where Base.Index: Hashable { - @_inlineable // FIXME(sil-serialize-all) + @available(swift, introduced: 4.1) public var hashValue: Int { return base.hashValue } diff --git a/test/IDE/print_ast_tc_decls.swift b/test/IDE/print_ast_tc_decls.swift index 071e444dd20ae..11b19f17bd11b 100644 --- a/test/IDE/print_ast_tc_decls.swift +++ b/test/IDE/print_ast_tc_decls.swift @@ -585,7 +585,7 @@ struct d0200_EscapedIdentifiers { } // PASS_COMMON-NEXT: {{^}} enum `enum` {{{$}} // PASS_COMMON-NEXT: {{^}} case `case`{{$}} -// PASS_COMMON-NEXT: {{^}} {{.*}}static func __derived_conformance__equals(_ a: d0200_EscapedIdentifiers.`enum`, _ b: d0200_EscapedIdentifiers.`enum`) -> Bool +// PASS_COMMON-NEXT: {{^}} {{.*}}static func __derived_enum_equals(_ a: d0200_EscapedIdentifiers.`enum`, _ b: d0200_EscapedIdentifiers.`enum`) -> Bool // PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}} // PASS_COMMON-NEXT: {{^}} }{{$}} @@ -1013,7 +1013,7 @@ enum d2000_EnumDecl1 { // PASS_COMMON: {{^}}enum d2000_EnumDecl1 {{{$}} // PASS_COMMON-NEXT: {{^}} case ED1_First{{$}} // PASS_COMMON-NEXT: {{^}} case ED1_Second{{$}} -// PASS_COMMON-NEXT: {{^}} {{.*}}static func __derived_conformance__equals(_ a: d2000_EnumDecl1, _ b: d2000_EnumDecl1) -> Bool +// PASS_COMMON-NEXT: {{^}} {{.*}}static func __derived_enum_equals(_ a: d2000_EnumDecl1, _ b: d2000_EnumDecl1) -> Bool // PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}} // PASS_COMMON-NEXT: {{^}}}{{$}} From 6120d562dccf7d15ca4568f48bb704ac02b6ab17 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sun, 11 Feb 2018 02:46:58 -0600 Subject: [PATCH 11/19] [stdlib] Add @_inlineable attribute with FIXME to hashValue implementations --- stdlib/public/core/ClosedRange.swift | 5 +++-- stdlib/public/core/DropWhile.swift.gyb | 1 + stdlib/public/core/Flatten.swift | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index 7f2f4b97c7fe8..82120aa7f783c 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -169,7 +169,8 @@ extension ClosedRange.Index : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ClosedRange.Index: Hashable -where Bound: Strideable, Bound.Stride: SignedInteger, Bound: Hashable { + where Bound: Strideable, Bound.Stride: SignedInteger, Bound: Hashable { + @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { switch self { @@ -395,7 +396,7 @@ extension ClosedRange : Hashable where Bound : Hashable { extension ClosedRange : CustomStringConvertible { /// A textual representation of the range. - @_inlineable // FIXME(sil-serialize-all)...\( + @_inlineable // FIXME(sil-serialize-all) public var description: String { return "\(lowerBound)...\(upperBound)" } diff --git a/stdlib/public/core/DropWhile.swift.gyb b/stdlib/public/core/DropWhile.swift.gyb index 216e17152a6cd..ed96365037ecd 100644 --- a/stdlib/public/core/DropWhile.swift.gyb +++ b/stdlib/public/core/DropWhile.swift.gyb @@ -142,6 +142,7 @@ public struct LazyDropWhileIndex : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension LazyDropWhileIndex : Hashable where Base.Index : Hashable { + @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { return base.hashValue diff --git a/stdlib/public/core/Flatten.swift b/stdlib/public/core/Flatten.swift index b7e4382b23c74..dafe68cef7d87 100644 --- a/stdlib/public/core/Flatten.swift +++ b/stdlib/public/core/Flatten.swift @@ -233,6 +233,7 @@ extension FlattenCollection.Index : Comparable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension FlattenCollection.Index : Hashable where Base.Index : Hashable, Base.Element.Index : Hashable { + @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { return _mixInt(_inner?.hashValue ?? 0) ^ _outer.hashValue From 3efd628fa853d2f11c895007ab367067eabfce8c Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sun, 11 Feb 2018 03:00:00 -0600 Subject: [PATCH 12/19] [stdlib] Remove unnecessary doc comments from hashValue implementations When implementing a protocol requirement, additional doc comments are unnecessary unless there are additional semantics peculiar to the specific implementation. The manually propagated documentation had several problems, including copypasta and inconsistent terminology. Therefore, if `T.==` doesn't have any doc comments and the implementation of `T.hashValue` is unremarkable, remove the unnecessary doc comment. --- stdlib/public/core/Arrays.swift.gyb | 8 +------- stdlib/public/core/ClosedRange.swift | 6 ------ stdlib/public/core/CollectionOfOne.swift | 6 ------ stdlib/public/core/ExistentialCollection.swift.gyb | 7 ------- stdlib/public/core/HashedCollections.swift.gyb | 8 ++++---- stdlib/public/core/Mirror.swift | 7 ------- stdlib/public/core/Optional.swift | 2 +- stdlib/public/core/Range.swift | 6 ------ 8 files changed, 6 insertions(+), 44 deletions(-) diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index b7b02e3f47772..4d60877cb64cf 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -2251,13 +2251,7 @@ extension ${Self} : Equatable where Element : Equatable { } @available(swift, introduced: 4.1) // FIXME(conformance-availability) -extension ${Self}: Hashable where Element : Hashable { - /// The hash value for the array. - /// - /// Two arrays that are equal will always have equal hash values. - /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. +extension ${Self} : Hashable where Element : Hashable { @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index 82120aa7f783c..ac465b4fb9a43 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -381,12 +381,6 @@ extension ClosedRange: Equatable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ClosedRange : Hashable where Bound : Hashable { - /// The hash value for the range. - /// - /// Two ranges that are equal will always have equal hash values. - /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { diff --git a/stdlib/public/core/CollectionOfOne.swift b/stdlib/public/core/CollectionOfOne.swift index 41c8c06c812f5..8a27928716462 100644 --- a/stdlib/public/core/CollectionOfOne.swift +++ b/stdlib/public/core/CollectionOfOne.swift @@ -148,12 +148,6 @@ extension CollectionOfOne : Equatable where Element : Equatable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension CollectionOfOne : Hashable where Element : Hashable { - /// The hash value for the collection. - /// - /// Two collection that are equal will always have equal hash values. - /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index 3f3cdc2a2f577..c3b109f6fdfc6 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -1260,13 +1260,6 @@ extension AnyCollection : Equatable where Element : Equatable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension AnyCollection : Hashable where Element : Hashable { - /// The hash value for the collection. - /// - /// Two `AnyCollection` values that are equal will always have equal hash - /// values. - /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { diff --git a/stdlib/public/core/HashedCollections.swift.gyb b/stdlib/public/core/HashedCollections.swift.gyb index 525ea4ba5f2f4..f7b5e843811ac 100644 --- a/stdlib/public/core/HashedCollections.swift.gyb +++ b/stdlib/public/core/HashedCollections.swift.gyb @@ -37,9 +37,9 @@ import SwiftShims // // Dictionary (a struct) // +------------------------------------------------+ -// | _VariantDictionaryBuffer (an enum) | +// | _VariantDictionaryBuffer (an enum) | // | +--------------------------------------------+ | -// | | [_NativeDictionaryBuffer (a struct)] | | +// | | [_NativeDictionaryBuffer (a struct)] | | // | +---|----------------------------------------+ | // +----/-------------------------------------------+ // / @@ -61,9 +61,9 @@ import SwiftShims // // Dictionary (a struct) // +----------------------------------------------+ -// | _VariantDictionaryBuffer (an enum) | +// | _VariantDictionaryBuffer (an enum) | // | +----------------------------------------+ | -// | | [ _CocoaDictionaryBuffer (a struct) ] | | +// | | [ _CocoaDictionaryBuffer (a struct) ] | | // | +---|------------------------------------+ | // +-----|----------------------------------------+ // | diff --git a/stdlib/public/core/Mirror.swift b/stdlib/public/core/Mirror.swift index 8dfac3b8e8c32..e03b37e72735b 100644 --- a/stdlib/public/core/Mirror.swift +++ b/stdlib/public/core/Mirror.swift @@ -921,13 +921,6 @@ extension DictionaryLiteral: Equatable where Key: Equatable, Value: Equatable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension DictionaryLiteral: Hashable where Key: Hashable, Value: Hashable { - /// The hash value for the collection. - /// - /// Two `DictionaryLiteral` values that are equal will always have equal hash - /// values. - /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index 4297ebdb6a6bc..1c4dc370b6cab 100644 --- a/stdlib/public/core/Optional.swift +++ b/stdlib/public/core/Optional.swift @@ -411,7 +411,7 @@ extension Optional : Equatable where Wrapped : Equatable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension Optional : Hashable where Wrapped : Hashable { - /// The hash value for the optional. + /// The hash value for the optional instance. /// /// Two optionals that are equal will always have equal hash values. /// diff --git a/stdlib/public/core/Range.swift b/stdlib/public/core/Range.swift index 98b312f242dfa..0e603ee0aca8b 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -360,12 +360,6 @@ extension Range: Equatable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension Range : Hashable where Bound : Hashable { - /// The hash value for the range. - /// - /// Two ranges that are equal will always have equal hash values. - /// - /// Hash values are not guaranteed to be equal across different executions of - /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { From a9b1e9a91433cf344b4a92aa9d2d2072d2f6dd9b Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sun, 11 Feb 2018 03:16:39 -0600 Subject: [PATCH 13/19] [stdlib] Remove unnecessary type annotations in hashValue --- stdlib/public/core/Arrays.swift.gyb | 2 +- stdlib/public/core/ExistentialCollection.swift.gyb | 2 +- stdlib/public/core/HashedCollections.swift.gyb | 2 +- stdlib/public/core/Mirror.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index 4d60877cb64cf..e3ccd5c8b5ab8 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -2256,7 +2256,7 @@ extension ${Self} : Hashable where Element : Hashable { @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to Array too - var result: Int = 0 + var result = 0 for element in self { result = _combineHashValues(result, element.hashValue) } diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index c3b109f6fdfc6..fa54e760442b0 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -1264,7 +1264,7 @@ extension AnyCollection : Hashable where Element : Hashable { @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to AnyCollection too - var result: Int = 0 + var result = 0 for element in self { result = _combineHashValues(result, element.hashValue) } diff --git a/stdlib/public/core/HashedCollections.swift.gyb b/stdlib/public/core/HashedCollections.swift.gyb index f7b5e843811ac..115171054dba6 100644 --- a/stdlib/public/core/HashedCollections.swift.gyb +++ b/stdlib/public/core/HashedCollections.swift.gyb @@ -2798,7 +2798,7 @@ extension Dictionary : Hashable where Value : Hashable { @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to Dictionary too - var result: Int = 0 + var result = 0 for (k, v) in self { let combined = _combineHashValues(k.hashValue, v.hashValue) result ^= _mixInt(combined) diff --git a/stdlib/public/core/Mirror.swift b/stdlib/public/core/Mirror.swift index e03b37e72735b..8bd1d370f578c 100644 --- a/stdlib/public/core/Mirror.swift +++ b/stdlib/public/core/Mirror.swift @@ -925,7 +925,7 @@ extension DictionaryLiteral: Hashable where Key: Hashable, Value: Hashable { @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to DictionaryLiteral too - var result: Int = 0 + var result = 0 for element in self { let elementHashValue = _combineHashValues(element.key.hashValue, element.value.hashValue) From 0e0dbc035e4fcb45a77e3f0c7a6bf27f261e20d6 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sun, 11 Feb 2018 03:20:24 -0600 Subject: [PATCH 14/19] [stdlib] Mix lowerBound a little in Range.hashValue and ClosedRange.hashValue --- stdlib/public/core/ClosedRange.swift | 5 ++++- stdlib/public/core/Range.swift | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index ac465b4fb9a43..b1d9a3c0a59c0 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -384,7 +384,10 @@ extension ClosedRange : Hashable where Bound : Hashable { @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { - return _combineHashValues(lowerBound.hashValue, upperBound.hashValue) + var result = 0 + result = _combineHashValues(result, lowerBound.hashValue) + result = _combineHashValues(result, upperBound.hashValue) + return result } } diff --git a/stdlib/public/core/Range.swift b/stdlib/public/core/Range.swift index 0e603ee0aca8b..0f6795b20d8d8 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -363,7 +363,10 @@ extension Range : Hashable where Bound : Hashable { @_inlineable // FIXME(sil-serialize-all) @available(swift, introduced: 4.1) public var hashValue: Int { - return _combineHashValues(lowerBound.hashValue, upperBound.hashValue) + var result = 0 + result = _combineHashValues(result, lowerBound.hashValue) + result = _combineHashValues(result, upperBound.hashValue) + return result } } From 427d7714a92697e40437d12a6c40793aafc5b4ef Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sun, 11 Feb 2018 14:53:41 -0600 Subject: [PATCH 15/19] [stdlib] Remove unnecessary attributes and clarify comments --- stdlib/public/core/Arrays.swift.gyb | 2 -- stdlib/public/core/ClosedRange.swift | 4 ---- stdlib/public/core/CollectionOfOne.swift | 4 +--- stdlib/public/core/DropWhile.swift.gyb | 2 -- stdlib/public/core/EmptyCollection.swift | 2 -- stdlib/public/core/ExistentialCollection.swift.gyb | 6 ++---- stdlib/public/core/Flatten.swift | 2 -- stdlib/public/core/HashedCollections.swift.gyb | 4 +--- stdlib/public/core/Mirror.swift | 3 +-- stdlib/public/core/Optional.swift | 2 -- stdlib/public/core/PrefixWhile.swift.gyb | 2 -- stdlib/public/core/Range.swift | 2 -- stdlib/public/core/Reverse.swift | 3 +-- 13 files changed, 6 insertions(+), 32 deletions(-) diff --git a/stdlib/public/core/Arrays.swift.gyb b/stdlib/public/core/Arrays.swift.gyb index e3ccd5c8b5ab8..24c1bd4d54abe 100644 --- a/stdlib/public/core/Arrays.swift.gyb +++ b/stdlib/public/core/Arrays.swift.gyb @@ -2250,10 +2250,8 @@ extension ${Self} : Equatable where Element : Equatable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ${Self} : Hashable where Element : Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to Array too var result = 0 diff --git a/stdlib/public/core/ClosedRange.swift b/stdlib/public/core/ClosedRange.swift index b1d9a3c0a59c0..89cef2232d728 100644 --- a/stdlib/public/core/ClosedRange.swift +++ b/stdlib/public/core/ClosedRange.swift @@ -167,11 +167,9 @@ extension ClosedRange.Index : Comparable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ClosedRange.Index: Hashable where Bound: Strideable, Bound.Stride: SignedInteger, Bound: Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { switch self { case .inRange(let value): @@ -379,10 +377,8 @@ extension ClosedRange: Equatable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ClosedRange : Hashable where Bound : Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { var result = 0 result = _combineHashValues(result, lowerBound.hashValue) diff --git a/stdlib/public/core/CollectionOfOne.swift b/stdlib/public/core/CollectionOfOne.swift index 8a27928716462..75a59cce57db3 100644 --- a/stdlib/public/core/CollectionOfOne.swift +++ b/stdlib/public/core/CollectionOfOne.swift @@ -138,9 +138,8 @@ extension CollectionOfOne: RandomAccessCollection, MutableCollection { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension CollectionOfOne : Equatable where Element : Equatable { - /// Returns a Boolean value indicating whether two collections are equal. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) + @available(swift, introduced: 4.1) // FIXME(conformance-availability) public static func == (lhs: CollectionOfOne, rhs: CollectionOfOne) -> Bool { return lhs._element == rhs._element } @@ -149,7 +148,6 @@ extension CollectionOfOne : Equatable where Element : Equatable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension CollectionOfOne : Hashable where Element : Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { return _element.hashValue } diff --git a/stdlib/public/core/DropWhile.swift.gyb b/stdlib/public/core/DropWhile.swift.gyb index ed96365037ecd..e9d223a56a569 100644 --- a/stdlib/public/core/DropWhile.swift.gyb +++ b/stdlib/public/core/DropWhile.swift.gyb @@ -140,10 +140,8 @@ public struct LazyDropWhileIndex : Comparable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension LazyDropWhileIndex : Hashable where Base.Index : Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { return base.hashValue } diff --git a/stdlib/public/core/EmptyCollection.swift b/stdlib/public/core/EmptyCollection.swift index fb1d5411ffb24..ca85ff26fd009 100644 --- a/stdlib/public/core/EmptyCollection.swift +++ b/stdlib/public/core/EmptyCollection.swift @@ -173,10 +173,8 @@ extension EmptyCollection : Equatable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension EmptyCollection : Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { return 0 } diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index fa54e760442b0..f6065e26485c1 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -1247,7 +1247,7 @@ extension ${Self}: _AnyCollectionProtocol { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension AnyCollection : Equatable where Element : Equatable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) + @available(swift, introduced: 4.1) // FIXME(conformance-availability) public static func == ( lhs: AnyCollection, rhs: AnyCollection ) -> Bool { @@ -1258,12 +1258,10 @@ extension AnyCollection : Equatable where Element : Equatable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension AnyCollection : Hashable where Element : Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { - // FIXME(ABI)#177: Issue applies to AnyCollection too + // FIXME(ABI)#177: Cache AnyCollection hashValue var result = 0 for element in self { result = _combineHashValues(result, element.hashValue) diff --git a/stdlib/public/core/Flatten.swift b/stdlib/public/core/Flatten.swift index dafe68cef7d87..bea9e21c21252 100644 --- a/stdlib/public/core/Flatten.swift +++ b/stdlib/public/core/Flatten.swift @@ -230,11 +230,9 @@ extension FlattenCollection.Index : Comparable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension FlattenCollection.Index : Hashable where Base.Index : Hashable, Base.Element.Index : Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { return _mixInt(_inner?.hashValue ?? 0) ^ _outer.hashValue } diff --git a/stdlib/public/core/HashedCollections.swift.gyb b/stdlib/public/core/HashedCollections.swift.gyb index 115171054dba6..068adca0cbd30 100644 --- a/stdlib/public/core/HashedCollections.swift.gyb +++ b/stdlib/public/core/HashedCollections.swift.gyb @@ -2786,7 +2786,6 @@ extension Dictionary : Equatable where Value : Equatable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension Dictionary : Hashable where Value : Hashable { /// The hash value for the dictionary. /// @@ -2795,9 +2794,8 @@ extension Dictionary : Hashable where Value : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { - // FIXME(ABI)#177: Issue applies to Dictionary too + // FIXME(ABI)#177: Cache Dictionary hashValue var result = 0 for (k, v) in self { let combined = _combineHashValues(k.hashValue, v.hashValue) diff --git a/stdlib/public/core/Mirror.swift b/stdlib/public/core/Mirror.swift index 8bd1d370f578c..2401b5e050414 100644 --- a/stdlib/public/core/Mirror.swift +++ b/stdlib/public/core/Mirror.swift @@ -908,7 +908,7 @@ extension DictionaryLiteral : RandomAccessCollection { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension DictionaryLiteral: Equatable where Key: Equatable, Value: Equatable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) + @available(swift, introduced: 4.1) // FIXME(conformance-availability) public static func == ( lhs: DictionaryLiteral, rhs: DictionaryLiteral ) -> Bool { @@ -922,7 +922,6 @@ extension DictionaryLiteral: Equatable where Key: Equatable, Value: Equatable { @available(swift, introduced: 4.1) // FIXME(conformance-availability) extension DictionaryLiteral: Hashable where Key: Hashable, Value: Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to DictionaryLiteral too var result = 0 diff --git a/stdlib/public/core/Optional.swift b/stdlib/public/core/Optional.swift index 1c4dc370b6cab..00279a2f9b931 100644 --- a/stdlib/public/core/Optional.swift +++ b/stdlib/public/core/Optional.swift @@ -409,7 +409,6 @@ extension Optional : Equatable where Wrapped : Equatable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension Optional : Hashable where Wrapped : Hashable { /// The hash value for the optional instance. /// @@ -418,7 +417,6 @@ extension Optional : Hashable where Wrapped : Hashable { /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { var result: Int switch self { diff --git a/stdlib/public/core/PrefixWhile.swift.gyb b/stdlib/public/core/PrefixWhile.swift.gyb index c4ffbe035631f..91bc14a61cc12 100644 --- a/stdlib/public/core/PrefixWhile.swift.gyb +++ b/stdlib/public/core/PrefixWhile.swift.gyb @@ -162,9 +162,7 @@ public struct LazyPrefixWhileIndex : Comparable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension LazyPrefixWhileIndex : Hashable where Base.Index : Hashable { - @available(swift, introduced: 4.1) public var hashValue: Int { switch _value { case .index(let value): diff --git a/stdlib/public/core/Range.swift b/stdlib/public/core/Range.swift index 0f6795b20d8d8..c925d3decea04 100644 --- a/stdlib/public/core/Range.swift +++ b/stdlib/public/core/Range.swift @@ -358,10 +358,8 @@ extension Range: Equatable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension Range : Hashable where Bound : Hashable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) public var hashValue: Int { var result = 0 result = _combineHashValues(result, lowerBound.hashValue) diff --git a/stdlib/public/core/Reverse.swift b/stdlib/public/core/Reverse.swift index faae54b370669..60f72c4393513 100644 --- a/stdlib/public/core/Reverse.swift +++ b/stdlib/public/core/Reverse.swift @@ -193,9 +193,8 @@ extension ReversedCollection.Index: Comparable { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension ReversedCollection.Index: Hashable where Base.Index: Hashable { - @available(swift, introduced: 4.1) + @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { return base.hashValue } From 08302c6ea975f266d5e01cf79e78ae0a980ae993 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sun, 11 Feb 2018 14:58:56 -0600 Subject: [PATCH 16/19] [stdlib] Restore an attribute for now --- stdlib/public/core/ExistentialCollection.swift.gyb | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index f6065e26485c1..bfafc95fef5a9 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -1258,6 +1258,7 @@ extension AnyCollection : Equatable where Element : Equatable { } } +@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension AnyCollection : Hashable where Element : Hashable { @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { From 3d3ed0ae94f02e80426e0d55a0f53b26199bd5ca Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sun, 11 Feb 2018 15:01:21 -0600 Subject: [PATCH 17/19] [stdlib] Update FlattenCollection.Index hashValue First, make sure combining hash values isn't done with xor. Second, Optional is now conditionally Hashable, so let's use it! --- stdlib/public/core/Flatten.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stdlib/public/core/Flatten.swift b/stdlib/public/core/Flatten.swift index bea9e21c21252..1b16fd16dec03 100644 --- a/stdlib/public/core/Flatten.swift +++ b/stdlib/public/core/Flatten.swift @@ -234,7 +234,10 @@ extension FlattenCollection.Index : Hashable where Base.Index : Hashable, Base.Element.Index : Hashable { @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { - return _mixInt(_inner?.hashValue ?? 0) ^ _outer.hashValue + var result = 0 + result = _combineHashValues(result, _inner.hashValue) + result = _combineHashValues(result, _outer.hashValue) + return result } } From 7b3bc6c08380cbaf0c4e83344d4e87e1300ce099 Mon Sep 17 00:00:00 2001 From: Xiaodi Wu Date: Sun, 11 Feb 2018 15:40:58 -0600 Subject: [PATCH 18/19] [stdlib] Attempt alternative design for conditional conformance to Equatable --- stdlib/public/core/CollectionOfOne.swift | 9 ++--- stdlib/public/core/Equatable.swift | 40 +++++++++++++++++++ .../core/ExistentialCollection.swift.gyb | 15 ++----- stdlib/public/core/Mirror.swift | 18 +++------ 4 files changed, 53 insertions(+), 29 deletions(-) diff --git a/stdlib/public/core/CollectionOfOne.swift b/stdlib/public/core/CollectionOfOne.swift index 75a59cce57db3..b4c8d45f2cf44 100644 --- a/stdlib/public/core/CollectionOfOne.swift +++ b/stdlib/public/core/CollectionOfOne.swift @@ -136,16 +136,13 @@ extension CollectionOfOne: RandomAccessCollection, MutableCollection { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) -extension CollectionOfOne : Equatable where Element : Equatable { +extension CollectionOfOne : Equatable, _Equatable where Element : Equatable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) // FIXME(conformance-availability) - public static func == (lhs: CollectionOfOne, rhs: CollectionOfOne) -> Bool { - return lhs._element == rhs._element + public func _isEqual(to other: CollectionOfOne) -> Bool { + return _element == other._element } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension CollectionOfOne : Hashable where Element : Hashable { @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { diff --git a/stdlib/public/core/Equatable.swift b/stdlib/public/core/Equatable.swift index e8dbe6fa11d30..ebaec7964f49e 100644 --- a/stdlib/public/core/Equatable.swift +++ b/stdlib/public/core/Equatable.swift @@ -195,6 +195,46 @@ extension Equatable { } } +// FIXME(ABI): Remove this protocol when `@_implements` is supported in +// conditional conformances. +// +// Ordinarily, we don't have to worry about adding new methods to types in the +// standard library. If end users have added identically named methods in their +// own extensions, those will seamlessly shadow our methods without any source +// breakage. +// +// However, if we add new methods that are spelled as _operators_ to types in +// the standard library, then any identically named methods implemented by end +// users will cause ambiguity at the call site. This prevents the addition of +// conditional conformance to `Equatable` to types where users may have already +// defined `==` for themselves. +// +// The underscored attribute `@_implements` was created to work around these +// limitations for derived conformances. However, at present, we can't use the +// same attribute for conditional conformances because the attribute doesn't +// work inside extensions (SR-NNNN). +// +// What _does_ work, however, is a conceptually similar design where we add only +// methods that aren't spelled as operators to the concrete type. Meanwhile, the +// protocol itself would provide a default implementation of the method that +// _is_ spelled as an operator, which in turn calls the non-operator method. +// With such a design, any operator method of the same name defined by the end +// user is selected preferentially over the protocol's default implementation. +// +// Such is the purpose of the following underscored protocol. + +public protocol _Equatable: Equatable { + func _isEqual(to other: Self) -> Bool +} + +extension _Equatable { + @_inlineable // FIXME(sil-serialize-all) + @_transparent + public static func == (lhs: Self, rhs: Self) -> Bool { + return lhs._isEqual(to: rhs) + } +} + //===----------------------------------------------------------------------===// // Reference comparison //===----------------------------------------------------------------------===// diff --git a/stdlib/public/core/ExistentialCollection.swift.gyb b/stdlib/public/core/ExistentialCollection.swift.gyb index bfafc95fef5a9..3cfc64191a4b9 100644 --- a/stdlib/public/core/ExistentialCollection.swift.gyb +++ b/stdlib/public/core/ExistentialCollection.swift.gyb @@ -1244,21 +1244,14 @@ extension ${Self}: _AnyCollectionProtocol { } % end -@available(swift, introduced: 4.1) // FIXME(conformance-availability) -extension AnyCollection : Equatable where Element : Equatable { +extension AnyCollection : Equatable, _Equatable where Element : Equatable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) // FIXME(conformance-availability) - public static func == ( - lhs: AnyCollection, rhs: AnyCollection - ) -> Bool { - if lhs.count != rhs.count { - return false - } - return lhs.elementsEqual(rhs) + public func _isEqual(to other: AnyCollection) -> Bool { + if count != other.count { return false } + return elementsEqual(other) } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) extension AnyCollection : Hashable where Element : Hashable { @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { diff --git a/stdlib/public/core/Mirror.swift b/stdlib/public/core/Mirror.swift index 2401b5e050414..1ba9424eb8e1e 100644 --- a/stdlib/public/core/Mirror.swift +++ b/stdlib/public/core/Mirror.swift @@ -905,22 +905,16 @@ extension DictionaryLiteral : RandomAccessCollection { } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) -extension DictionaryLiteral: Equatable where Key: Equatable, Value: Equatable { +extension DictionaryLiteral : Equatable, _Equatable + where Key : Equatable, Value : Equatable { @_inlineable // FIXME(sil-serialize-all) - @available(swift, introduced: 4.1) // FIXME(conformance-availability) - public static func == ( - lhs: DictionaryLiteral, rhs: DictionaryLiteral - ) -> Bool { - if lhs.count != rhs.count { - return false - } - return lhs.elementsEqual(rhs, by: ==) + public func _isEqual(to other: DictionaryLiteral) -> Bool { + if count != other.count { return false } + return elementsEqual(other, by: ==) } } -@available(swift, introduced: 4.1) // FIXME(conformance-availability) -extension DictionaryLiteral: Hashable where Key: Hashable, Value: Hashable { +extension DictionaryLiteral : Hashable where Key : Hashable, Value : Hashable { @_inlineable // FIXME(sil-serialize-all) public var hashValue: Int { // FIXME(ABI)#177: Issue applies to DictionaryLiteral too From 81d3aac327ad02a466bb049fc7cc7b1aa1ab096d Mon Sep 17 00:00:00 2001 From: Karoy Lorentey Date: Mon, 12 Feb 2018 12:01:04 +0000 Subject: [PATCH 19/19] [ClangImporter] Break to 80 columns (NFC) --- lib/ClangImporter/ImportType.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/ClangImporter/ImportType.cpp b/lib/ClangImporter/ImportType.cpp index 7c206bfb51a7c..251c03360c9be 100644 --- a/lib/ClangImporter/ImportType.cpp +++ b/lib/ClangImporter/ImportType.cpp @@ -998,14 +998,15 @@ namespace { if (unboundDecl == Impl.SwiftContext.getDictionaryDecl() || unboundDecl == Impl.SwiftContext.getSetDecl()) { auto &keyType = importedTypeArgs[0]; + auto keyStructDecl = keyType->getStructOrBoundGenericStruct(); if (!Impl.matchesHashableBound(keyType) || // Dictionary and Array conditionally conform to Hashable, // but the conformance doesn't necessarily apply with the // imported versions of their type arguments. // FIXME: Import their non-Hashable type parameters as // AnyHashable in this context. - keyType->getStructOrBoundGenericStruct() == Impl.SwiftContext.getDictionaryDecl() || - keyType->getStructOrBoundGenericStruct() == Impl.SwiftContext.getArrayDecl()) { + keyStructDecl == Impl.SwiftContext.getDictionaryDecl() || + keyStructDecl == Impl.SwiftContext.getArrayDecl()) { if (auto anyHashable = Impl.SwiftContext.getAnyHashableDecl()) keyType = anyHashable->getDeclaredType(); else