Skip to content

Commit eb5e5e5

Browse files
lorenteyairspeedswift
authored andcommitted
[5.0][stdlib] Add default Hashable implementations for RawRepresentable types (#20913)
* [stdlib] Add hashing methods to RawRepresentable to match == (cherry picked from commit 8bd5650) * [stdlib] Re-add previously synthesized hashing definitions (cherry picked from commit 545fa5e) * [test] Update for RawRepresentable hashing changes (cherry picked from commit 811d3d7)
1 parent d52c692 commit eb5e5e5

File tree

12 files changed

+1660
-1296
lines changed

12 files changed

+1660
-1296
lines changed

stdlib/public/core/CompilerProtocols.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,27 @@ public func != <T : Equatable>(lhs: T, rhs: T) -> Bool
178178
return lhs.rawValue != rhs.rawValue
179179
}
180180

181+
// Ensure that any RawRepresentable types that conform to Hashable without
182+
// providing explicit implementations get hashing that's consistent with the ==
183+
// definition above. (Compiler-synthesized hashing is based on stored properties
184+
// rather than rawValue; the difference is subtle, but it can be fatal.)
185+
extension RawRepresentable where RawValue: Hashable, Self: Hashable {
186+
@inlinable // trivial
187+
public var hashValue: Int {
188+
return rawValue.hashValue
189+
}
190+
191+
@inlinable // trivial
192+
public func hash(into hasher: inout Hasher) {
193+
hasher.combine(rawValue)
194+
}
195+
196+
@inlinable // trivial
197+
public func _rawHashValue(seed: Int) -> Int {
198+
return rawValue._rawHashValue(seed: seed)
199+
}
200+
}
201+
181202
/// A type that provides a collection of all of its values.
182203
///
183204
/// Types that conform to the `CaseIterable` protocol are typically

stdlib/public/core/FloatingPoint.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1237,6 +1237,19 @@ public enum FloatingPointSign: Int {
12371237
public static func ==(a: FloatingPointSign, b: FloatingPointSign) -> Bool {
12381238
return a.rawValue == b.rawValue
12391239
}
1240+
1241+
@inlinable
1242+
public var hashValue: Int { return rawValue.hashValue }
1243+
1244+
@inlinable
1245+
public func hash(into hasher: inout Hasher) {
1246+
hasher.combine(rawValue)
1247+
}
1248+
1249+
@inlinable
1250+
public func _rawHashValue(seed: Int) -> Int {
1251+
return rawValue._rawHashValue(seed: seed)
1252+
}
12401253
}
12411254

12421255
/// The IEEE 754 floating-point classes.

stdlib/public/core/UnicodeScalarProperties.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,12 +1258,27 @@ extension Unicode {
12581258
self.rawValue = rawValue
12591259
}
12601260

1261+
public static func == (
1262+
lhs: CanonicalCombiningClass,
1263+
rhs: CanonicalCombiningClass
1264+
) -> Bool {
1265+
return lhs.rawValue == rhs.rawValue
1266+
}
1267+
12611268
public static func < (
12621269
lhs: CanonicalCombiningClass,
12631270
rhs: CanonicalCombiningClass
12641271
) -> Bool {
12651272
return lhs.rawValue < rhs.rawValue
12661273
}
1274+
1275+
public var hashValue: Int {
1276+
return rawValue.hashValue
1277+
}
1278+
1279+
public func hash(into hasher: inout Hasher) {
1280+
hasher.combine(rawValue)
1281+
}
12671282
}
12681283
}
12691284

test/IDE/complete_enum_elements.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,8 @@ enum QuxEnum : Int {
267267
// QUX_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Qux1[#QuxEnum#]{{; name=.+$}}
268268
// QUX_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Qux2[#QuxEnum#]{{; name=.+$}}
269269
// QUX_ENUM_NO_DOT-NEXT: Decl[TypeAlias]/CurrNominal: .RawValue[#Int#]{{; name=.+$}}
270-
// QUX_ENUM_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .hash({#self: QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(QuxEnum)
271270
// QUX_ENUM_NO_DOT-NEXT: Decl[Constructor]/CurrNominal: ({#rawValue: Int#})[#QuxEnum?#]{{; name=.+$}}
271+
// QUX_ENUM_NO_DOT-NEXT: Decl[InstanceMethod]/Super: .hash({#self: QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(QuxEnum)
272272
// QUX_ENUM_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#QuxEnum.Type#]; name=self
273273
// QUX_ENUM_NO_DOT-NEXT: End completions
274274

@@ -277,8 +277,8 @@ enum QuxEnum : Int {
277277
// QUX_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Qux1[#QuxEnum#]{{; name=.+$}}
278278
// QUX_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Qux2[#QuxEnum#]{{; name=.+$}}
279279
// QUX_ENUM_DOT-NEXT: Decl[TypeAlias]/CurrNominal: RawValue[#Int#]{{; name=.+$}}
280-
// QUX_ENUM_DOT-NEXT: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: hash({#self: QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(QuxEnum)
281280
// QUX_ENUM_DOT-NEXT: Decl[Constructor]/CurrNominal: init({#rawValue: Int#})[#QuxEnum?#]{{; name=.+$}}
281+
// QUX_ENUM_DOT-NEXT: Decl[InstanceMethod]/Super/NotRecommended/TypeRelation[Invalid]: hash({#self: QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(QuxEnum)
282282
// QUX_ENUM_DOT-NEXT: End completions
283283

284284
func freeFunc() {}

test/IDE/print_ast_tc_decls.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,8 +1073,6 @@ enum d2300_EnumDeclWithValues1 : Int {
10731073
// PASS_COMMON-NEXT: {{^}} case EDV2_First{{$}}
10741074
// PASS_COMMON-NEXT: {{^}} case EDV2_Second{{$}}
10751075
// PASS_COMMON-NEXT: {{^}} typealias RawValue = Int
1076-
// PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}}
1077-
// PASS_COMMON-NEXT: {{^}} func hash(into hasher: inout Hasher)
10781076
// PASS_COMMON-NEXT: {{^}} init?(rawValue: Int){{$}}
10791077
// PASS_COMMON-NEXT: {{^}} var rawValue: Int { get }{{$}}
10801078
// PASS_COMMON-NEXT: {{^}}}{{$}}
@@ -1087,8 +1085,6 @@ enum d2400_EnumDeclWithValues2 : Double {
10871085
// PASS_COMMON-NEXT: {{^}} case EDV3_First{{$}}
10881086
// PASS_COMMON-NEXT: {{^}} case EDV3_Second{{$}}
10891087
// PASS_COMMON-NEXT: {{^}} typealias RawValue = Double
1090-
// PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}}
1091-
// PASS_COMMON-NEXT: {{^}} func hash(into hasher: inout Hasher)
10921088
// PASS_COMMON-NEXT: {{^}} init?(rawValue: Double){{$}}
10931089
// PASS_COMMON-NEXT: {{^}} var rawValue: Double { get }{{$}}
10941090
// PASS_COMMON-NEXT: {{^}}}{{$}}

test/ParseableInterface/synthesized.swift

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,6 @@ public enum HasRawValue: Int {
55
// CHECK-NEXT: case a, b, c
66
case a, b = 5, c
77
// CHECK-NEXT: public typealias RawValue = Swift.Int
8-
// CHECK-NEXT: public var hashValue: Swift.Int {
9-
// CHECK-NEXT: get{{$}}
10-
// CHECK-NEXT: }
11-
// CHECK-NEXT: public func hash(into hasher: inout Swift.Hasher)
128
// CHECK-NEXT: @inlinable public init?(rawValue: Swift.Int)
139
// CHECK-NEXT: public var rawValue: Swift.Int {
1410
// CHECK-NEXT: @inlinable get{{$}}
@@ -20,10 +16,6 @@ public enum HasRawValue: Int {
2016
// CHECK-NEXT: case a, b, c
2117
case a, b = 5, c
2218
// CHECK-NEXT: public typealias RawValue = Swift.Int
23-
// CHECK-NEXT: public var hashValue: Swift.Int {
24-
// CHECK-NEXT: get{{$}}
25-
// CHECK-NEXT: }
26-
// CHECK-NEXT: public func hash(into hasher: inout Swift.Hasher)
2719
// CHECK-NEXT: @inlinable public init?(rawValue: Swift.Int)
2820
// CHECK-NEXT: public var rawValue: Swift.Int {
2921
// CHECK-NEXT: @inlinable get{{$}}

test/SILGen/objc_enum.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import gizmo
99

1010
// CHECK-DAG: sil shared [serializable] @$sSo16NSRuncingOptionsV{{[_0-9a-zA-Z]*}}fC
1111
// CHECK-DAG: sil shared [serializable] @$sSo16NSRuncingOptionsV8rawValueSivg
12-
// CHECK-DAG: sil shared [serializable] @$sSo16NSRuncingOptionsV9hashValueSivg
1312

1413
// Non-payload enum ctors don't need to be instantiated at all.
1514
// NEGATIVE-NOT: sil shared [transparent] @$sSo16NSRuncingOptionsV5MinceAbBmF

0 commit comments

Comments
 (0)