Skip to content

Commit c22c0b8

Browse files
lorenteyairspeedswift
authored andcommitted
[5.0][Sema] Emit a deprecation warning if a Hashable type only implements hashValue (#21445)
* [Sema] Emit a deprecation warning when hashValue is provided by hash(into:) isn’t SE-206 deprecated hashValue as a protocol requirement. We should gently encourage people to migrate to hash(into:), for its more secure, easier and faster hashing. Emit a compiler warning whenever hashValue has an explicit implementation, but hash(into:) doesn’t. (cherry picked from commit e0495a7) * [stdlib] Document that `hashValue` is deprecated (cherry picked from commit 1485404) * [test] Test new deprecation warning for hashValue implementations (cherry picked from commit cb3cff5) * Doc fix Co-Authored-By: lorentey <[email protected]> (cherry picked from commit 646849e) * [test] StdlibUnittest: Add missing hash(into:) implementations (cherry picked from commit 8e77a26) * [test] Modernize hashing throughout the test suite (cherry picked from commit 666a22f)
1 parent 80a3a6f commit c22c0b8

File tree

68 files changed

+313
-203
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+313
-203
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4280,6 +4280,10 @@ WARNING(non_exhaustive_switch_warn,none, "switch must be exhaustive", ())
42804280
WARNING(override_nsobject_hashvalue,none,
42814281
"override of 'NSObject.hashValue' is deprecated; "
42824282
"override 'NSObject.hash' to get consistent hashing behavior", ())
4283+
WARNING(hashvalue_implementation,none,
4284+
"'Hashable.hashValue' is deprecated as a protocol requirement; "
4285+
"conform type %0 to 'Hashable' by implementing 'hash(into:)' instead",
4286+
(Type))
42834287

42844288
#ifndef DIAG_NO_UNDEF
42854289
# if defined(DIAG)

lib/Sema/DerivedConformanceEquatableHashable.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,7 @@ ValueDecl *DerivedConformance::deriveHashable(ValueDecl *requirement) {
11751175
// The hashValue failure will produce a diagnostic elsewhere.
11761176
return nullptr;
11771177
}
1178-
if (hashValueDecl && hashValueDecl->isImplicit()) {
1178+
if (hashValueDecl->isImplicit()) {
11791179
// Neither hashValue nor hash(into:) is explicitly defined; we need to do
11801180
// a full Hashable derivation.
11811181

@@ -1209,8 +1209,11 @@ ValueDecl *DerivedConformance::deriveHashable(ValueDecl *requirement) {
12091209
llvm_unreachable("Attempt to derive Hashable for a type other "
12101210
"than a struct or enum");
12111211
} else {
1212-
// We can always derive hash(into:) if hashValue has an explicit
1213-
// implementation.
1212+
// hashValue has an explicit implementation, but hash(into:) doesn't.
1213+
// Emit a deprecation warning, then derive hash(into:) in terms of
1214+
// hashValue.
1215+
TC.diagnose(hashValueDecl->getLoc(), diag::hashvalue_implementation,
1216+
Nominal->getDeclaredType());
12141217
return deriveHashable_hashInto(*this,
12151218
&deriveBodyHashable_compat_hashInto);
12161219
}

stdlib/private/StdlibCollectionUnittest/MinimalCollections.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,8 +305,8 @@ public struct _CollectionState : Equatable, Hashable {
305305
}
306306
}
307307

308-
public var hashValue: Int {
309-
return _id.hashValue
308+
public func hash(into hasher: inout Hasher) {
309+
hasher.combine(_id)
310310
}
311311
}
312312

stdlib/private/StdlibUnicodeUnittest/Collation.swift

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -181,42 +181,11 @@ let ducetExtractData: [CollationTableEntry] = [
181181
CollationTableEntry([0xE01EF], [0x0000_0000_0000], "VARIATION SELECTOR-256"),
182182
]
183183

184-
public struct HashableArray<Element : Hashable> : Hashable {
185-
internal var _elements: [Element]
186-
187-
public init(_ elements: [Element]) {
188-
_elements = elements
189-
}
190-
191-
public var hashValue: Int {
192-
// FIXME: this is a bad approach to combining hash values.
193-
var result = 0
194-
for x in _elements {
195-
result ^= x.hashValue
196-
result = result &* 997
197-
}
198-
return result
199-
}
200-
}
201-
202-
public func == <Element>(
203-
lhs: HashableArray<Element>,
204-
rhs: HashableArray<Element>
205-
) -> Bool {
206-
return lhs._elements.elementsEqual(rhs._elements)
207-
}
208-
209-
extension HashableArray : ExpressibleByArrayLiteral {
210-
public init(arrayLiteral elements: Element...) {
211-
self._elements = elements
212-
}
213-
}
214-
215-
let ducetExtract: [HashableArray<Unicode.Scalar> : CollationTableEntry] = {
184+
let ducetExtract: [[Unicode.Scalar]: CollationTableEntry] = {
216185
() in
217-
var result: [HashableArray<Unicode.Scalar> : CollationTableEntry] = [:]
186+
var result: [[Unicode.Scalar]: CollationTableEntry] = [:]
218187
for entry in ducetExtractData {
219-
result[HashableArray(entry.scalars)] = entry
188+
result[entry.scalars] = entry
220189
}
221190
return result
222191
}()
@@ -232,7 +201,7 @@ extension String {
232201
internal var _collationElements: [UInt64] {
233202
var result: [UInt64] = []
234203
for us in self.unicodeScalars {
235-
let scalars: HashableArray<Unicode.Scalar> = [us]
204+
let scalars: [Unicode.Scalar] = [us]
236205
let collationElements = ducetExtract[scalars]!.collationElements
237206
if collationElements[0] != 0 {
238207
result += collationElements

stdlib/private/StdlibUnittest/LifetimeTracked.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ extension LifetimeTracked : Hashable {
5151
public var hashValue: Int {
5252
return value
5353
}
54+
public func hash(into hasher: inout Hasher) {
55+
hasher.combine(value)
56+
}
5457
}
5558

5659
extension LifetimeTracked : Strideable {

stdlib/private/StdlibUnittest/StdlibCoreExtras.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,20 @@ public func <=> <T: Comparable>(lhs: T, rhs: T) -> ExpectedComparisonResult {
110110
}
111111

112112
public struct TypeIdentifier : Hashable, Comparable {
113+
public var value: Any.Type
114+
113115
public init(_ value: Any.Type) {
114116
self.value = value
115117
}
116118

117119
public var hashValue: Int { return objectID.hashValue }
118-
public var value: Any.Type
119-
120-
internal var objectID : ObjectIdentifier { return ObjectIdentifier(value) }
120+
public func hash(into hasher: inout Hasher) {
121+
hasher.combine(objectID)
122+
}
123+
124+
internal var objectID : ObjectIdentifier {
125+
return ObjectIdentifier(value)
126+
}
121127
}
122128

123129
public func < (lhs: TypeIdentifier, rhs: TypeIdentifier) -> Bool {

stdlib/private/StdlibUnittest/StringConvertible.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ public struct CustomPrintableValue
6565
return value.hashValue
6666
}
6767

68+
public func hash(into hasher: inout Hasher) {
69+
hasher.combine(value)
70+
}
71+
6872
public typealias Stride = Int
6973

7074
public func distance(to other: CustomPrintableValue) -> Stride {

stdlib/public/core/Hashable.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ public protocol Hashable : Equatable {
106106
///
107107
/// Hash values are not guaranteed to be equal across different executions of
108108
/// your program. Do not save hash values to use during a future execution.
109+
///
110+
/// - Important: `hashValue` is deprecated as a `Hashable` requirement. To
111+
/// conform to `Hashable`, implement the `hash(into:)` requirement instead.
109112
var hashValue: Int { get }
110113

111114
/// Hashes the essential components of this value by feeding them into the

test/ClangImporter/objc_override.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ class CallbackSubC : CallbackBase {
110110

111111
//
112112
class MyHashableNSObject: NSObject {
113-
override var hashValue: Int { // expected-warning{{override of 'NSObject.hashValue' is deprecated}}
113+
override var hashValue: Int { // expected-error{{overriding non-open property outside of its defining module}} expected-error{{overriding non-@objc declarations from extensions is not supported}}
114114
return 0
115115
}
116116
}

test/Constraints/bridging-nsnumber-and-nsvalue.swift.gyb

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@ extension Equatable {
3838
fatalError("hella cray")
3939
}
4040
}
41-
extension Hashable { public var hashValue: Int { fatalError("trill hiphy") } }
41+
extension Hashable {
42+
public func hash(into hasher: inout Hasher) {
43+
fatalError("trill hiphy")
44+
}
45+
}
46+
4247
extension CGSize: Hashable {}
4348
extension CGPoint: Hashable {}
4449
extension CGRect: Hashable {}

0 commit comments

Comments
 (0)