Skip to content

Commit ab0c74b

Browse files
authored
Merge pull request #14913 from lorentey/resilient-hashing
Switch to a resilient hashing interface, currently implementing SipHash-1-3. Compiler-synthesized Hashable conformances still use the old _combineHashValues interface for now.
2 parents 51eb34b + e03cb13 commit ab0c74b

24 files changed

+563
-499
lines changed

lib/SILGen/SILGenExpr.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3613,8 +3613,7 @@ getOrCreateKeyPathEqualsAndHash(SILGenModule &SGM,
36133613

36143614
SILValue hashCode;
36153615

3616-
// TODO: Combine hashes of the indexes. There isn't a great hash combining
3617-
// interface in the standard library to do this yet.
3616+
// TODO: Combine hashes of the indexes using an inout _Hasher
36183617
{
36193618
auto &index = indexes[0];
36203619

stdlib/private/StdlibUnittest/StdlibUnittest.swift.gyb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,15 @@ struct PersistentState {
10891089
static var runNoTestsWasCalled: Bool = false
10901090
static var ranSomething: Bool = false
10911091
static var complaintInstalled = false
1092+
static var hashingKeyOverridden = false
1093+
1094+
static func overrideHashingKey() {
1095+
if !hashingKeyOverridden {
1096+
// FIXME(hasher): This has to run before creating the first Set/Dictionary
1097+
_Hasher._secretKey = (0, 0)
1098+
hashingKeyOverridden = true
1099+
}
1100+
}
10921101

10931102
static func complainIfNothingRuns() {
10941103
if !complaintInstalled {
@@ -1200,6 +1209,7 @@ func stopTrackingObjects(_: UnsafePointer<CChar>) -> Int
12001209

12011210
public final class TestSuite {
12021211
public init(_ name: String) {
1212+
PersistentState.overrideHashingKey()
12031213
self.name = name
12041214
_precondition(
12051215
_testNameToIndex[name] == nil,

stdlib/public/core/AnyHashable.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ internal protocol _AnyHashableBox {
4848
/// no comparison is possible. Otherwise, contains the result of `==`.
4949
func _isEqual(to: _AnyHashableBox) -> Bool?
5050
var _hashValue: Int { get }
51+
func _hash(_into hasher: inout _Hasher)
5152

5253
var _base: Any { get }
5354
func _downCastConditional<T>(into result: UnsafeMutablePointer<T>) -> Bool
@@ -93,6 +94,12 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
9394
return _baseHashable.hashValue
9495
}
9596

97+
@_inlineable // FIXME(sil-serialize-all)
98+
@_versioned // FIXME(sil-serialize-all)
99+
func _hash(_into hasher: inout _Hasher) {
100+
_baseHashable._hash(into: &hasher)
101+
}
102+
96103
@_inlineable // FIXME(sil-serialize-all)
97104
@_versioned // FIXME(sil-serialize-all)
98105
internal var _base: Any {
@@ -295,6 +302,11 @@ extension AnyHashable : Hashable {
295302
public var hashValue: Int {
296303
return _box._hashValue
297304
}
305+
306+
@_inlineable // FIXME(sil-serialize-all)
307+
public func _hash(into hasher: inout _Hasher) {
308+
_box._hash(_into: &hasher)
309+
}
298310
}
299311

300312
extension AnyHashable : CustomStringConvertible {

stdlib/public/core/Bool.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,13 @@ extension Bool : Equatable, Hashable {
154154
/// invocations of the same program. Do not persist the hash value across
155155
/// program runs.
156156
@_inlineable // FIXME(sil-serialize-all)
157-
@_transparent
158157
public var hashValue: Int {
159-
return self ? 1 : 0
158+
return _hashValue(for: self)
159+
}
160+
161+
@_inlineable // FIXME(sil-serialize-all)
162+
public func _hash(into hasher: inout _Hasher) {
163+
hasher.append((self ? 1 : 0) as UInt8)
160164
}
161165

162166
@_inlineable // FIXME(sil-serialize-all)

stdlib/public/core/CTypes.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,12 @@ extension OpaquePointer: Hashable {
182182
/// program runs.
183183
@_inlineable // FIXME(sil-serialize-all)
184184
public var hashValue: Int {
185-
return Int(Builtin.ptrtoint_Word(_rawValue))
185+
return _hashValue(for: self)
186+
}
187+
188+
@_inlineable // FIXME(sil-serialize-all)
189+
public func _hash(into hasher: inout _Hasher) {
190+
hasher.append(Int(Builtin.ptrtoint_Word(_rawValue)))
186191
}
187192
}
188193

stdlib/public/core/DoubleWidth.swift.gyb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,13 @@ extension DoubleWidth : Comparable {
151151
extension DoubleWidth : Hashable {
152152
@_inlineable // FIXME(sil-serialize-all)
153153
public var hashValue: Int {
154-
var result = 0
155-
result = _combineHashValues(result, _storage.high.hashValue)
156-
result = _combineHashValues(result, _storage.low.hashValue)
157-
return result
154+
return _hashValue(for: self)
155+
}
156+
157+
@_inlineable // FIXME(sil-serialize-all)
158+
public func _hash(into hasher: inout _Hasher) {
159+
hasher.append(low)
160+
hasher.append(high)
158161
}
159162
}
160163

stdlib/public/core/DropWhile.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,10 @@ extension LazyDropWhileCollection.Index: Hashable where Base.Index: Hashable {
190190
public var hashValue: Int {
191191
return base.hashValue
192192
}
193+
194+
public func _hash(into hasher: inout _Hasher) {
195+
hasher.append(base)
196+
}
193197
}
194198

195199
extension LazyDropWhileCollection: Collection {

stdlib/public/core/Flatten.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,14 @@ extension FlattenCollection.Index : Comparable {
233233
extension FlattenCollection.Index : Hashable
234234
where Base.Index : Hashable, Base.Element.Index : Hashable {
235235
public var hashValue: Int {
236-
return _combineHashValues(_inner?.hashValue ?? 0, _outer.hashValue)
236+
return _hashValue(for: self)
237+
}
238+
239+
public func _hash(into hasher: inout _Hasher) {
240+
hasher.append(_outer)
241+
if let inner = _inner {
242+
hasher.append(inner)
243+
}
237244
}
238245
}
239246

stdlib/public/core/FloatingPointTypes.swift.gyb

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,26 +1524,25 @@ extension ${Self} : Hashable {
15241524
/// your program. Do not save hash values to use during a future execution.
15251525
@_inlineable // FIXME(sil-serialize-all)
15261526
public var hashValue: Int {
1527+
return _hashValue(for: self)
1528+
}
1529+
1530+
@_inlineable // FIXME(sil-serialize-all)
1531+
public func _hash(into hasher: inout _Hasher) {
1532+
var v = self
15271533
if isZero {
15281534
// To satisfy the axiom that equality implies hash equality, we need to
15291535
// finesse the hash value of -0.0 to match +0.0.
1530-
return 0
1531-
} else {
1532-
%if bits <= word_bits:
1533-
return Int(bitPattern: UInt(bitPattern))
1534-
%elif bits == 64: # Double -> 32-bit Int
1535-
return Int(truncatingIfNeeded: bitPattern &>> 32) ^
1536-
Int(truncatingIfNeeded: bitPattern)
1537-
%elif word_bits == 32: # Float80 -> 32-bit Int
1538-
return Int(truncatingIfNeeded: significandBitPattern &>> 32) ^
1539-
Int(truncatingIfNeeded: significandBitPattern) ^
1540-
Int(_representation.signAndExponent)
1541-
%else: # Float80 -> 64-bit Int
1542-
return Int(bitPattern: UInt(significandBitPattern)) ^
1543-
Int(_representation.signAndExponent)
1544-
%end
1536+
v = 0
15451537
}
1538+
%if bits == 80:
1539+
hasher.append(v._representation.signAndExponent)
1540+
hasher.append(v.significandBitPattern)
1541+
%else:
1542+
hasher.append(v.bitPattern)
1543+
%end
15461544
}
1545+
15471546
}
15481547

15491548
extension ${Self} {

stdlib/public/core/Hashable.swift

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,24 @@ public protocol Hashable : Equatable {
108108
/// Hash values are not guaranteed to be equal across different executions of
109109
/// your program. Do not save hash values to use during a future execution.
110110
var hashValue: Int { get }
111+
112+
/// Feed bits to be hashed into the hash function represented by `hasher`.
113+
func _hash(into hasher: inout _Hasher)
114+
}
115+
116+
extension Hashable {
117+
@inline(__always)
118+
public func _hash(into hasher: inout _Hasher) {
119+
hasher.append(self.hashValue)
120+
}
121+
}
122+
123+
// Called by synthesized `hashValue` implementations.
124+
@inline(__always)
125+
public func _hashValue<H: Hashable>(for value: H) -> Int {
126+
var hasher = _Hasher()
127+
hasher.append(value)
128+
return hasher.finalize()
111129
}
112130

113131
// Called by the SwiftValue implementation.
@@ -126,4 +144,3 @@ internal func Hashable_hashValue_indirect<T : Hashable>(
126144
) -> Int {
127145
return value.pointee.hashValue
128146
}
129-

stdlib/public/core/HashedCollections.swift.gyb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -792,11 +792,16 @@ extension Set : Hashable {
792792
@_inlineable // FIXME(sil-serialize-all)
793793
public var hashValue: Int {
794794
// FIXME(ABI)#177: <rdar://problem/18915294> Cache Set<T> hashValue
795-
var result: Int = _mixInt(0)
795+
return _hashValue(for: self)
796+
}
797+
798+
@_inlineable // FIXME(sil-serialize-all)
799+
public func _hash(into hasher: inout _Hasher) {
800+
var hash = 0
796801
for member in self {
797-
result ^= _mixInt(member.hashValue)
802+
hash ^= _hashValue(for: member)
798803
}
799-
return result
804+
hasher.append(hash)
800805
}
801806
}
802807

@@ -3985,7 +3990,7 @@ extension _Native${Self}Buffer
39853990
@_versioned
39863991
@inline(__always) // For performance reasons.
39873992
internal func _bucket(_ k: Key) -> Int {
3988-
return _squeezeHashValue(k.hashValue, bucketCount)
3993+
return _hashValue(for: k) & _bucketMask
39893994
}
39903995

39913996
@_inlineable // FIXME(sil-serialize-all)

0 commit comments

Comments
 (0)