1
- // RUN: %target-run-simple-swift | %FileCheck %s
1
+ // RUN: %target-run-simple-swift
2
2
// REQUIRES: executable_test
3
3
4
- // RawRepresentable is not Equatable itself, but it does provide a generic
5
- // implementation of == based on rawValues. This gets picked up as the
6
- // implementation of Equatable.== when a concrete RawRepresentable type conforms
7
- // to Equatable without providing its own implementation.
8
- //
9
- // However, RawRepresentable used to not provide equivalent implementations for
10
- // hashing, allowing the compiler to synthesized hashing as usual, based on the
11
- // actual contents of the type rather than its rawValue. Thus, the definitions
12
- // of equality and hashing may not actually match, leading to broken hashes.
13
- //
14
- // The difference between rawValue and the actual contents is subtle, and it
15
- // only causes problems in custom RawRepresentable implementations where the
16
- // rawValue isn't actually the storage representation, like the weird struct
17
- // below.
18
- //
19
- // rdar://problem/45308741
4
+ import StdlibUnittest
5
+
6
+ let suite = TestSuite ( " RawRepresentable " )
7
+
8
+ extension Hasher {
9
+ static func hash< H: Hashable > ( _ value: H ) -> Int {
10
+ var hasher = Hasher ( )
11
+ hasher. combine ( value)
12
+ return hasher. finalize ( )
13
+ }
14
+ }
15
+
16
+
20
17
21
18
struct TrickyRawRepresentable : RawRepresentable , Hashable {
22
19
var value : [ Unicode . Scalar ]
@@ -30,27 +27,58 @@ struct TrickyRawRepresentable: RawRepresentable, Hashable {
30
27
}
31
28
}
32
29
33
- let s1 = TrickyRawRepresentable ( rawValue: " café " ) !
34
- let s2 = TrickyRawRepresentable ( rawValue: " cafe \u{301} " ) !
30
+ suite. test ( " Tricky hashing " ) {
31
+ // RawRepresentable is not Equatable itself, but it does provide a generic
32
+ // implementation of == based on rawValue. This gets picked up as the
33
+ // implementation of Equatable.== when a concrete RawRepresentable type
34
+ // conforms to Equatable without providing its own implementation.
35
+ //
36
+ // However, RawRepresentable used to not provide equivalent implementations
37
+ // for hashing, allowing the compiler to synthesize hashing as usual, based on
38
+ // the actual contents of the type rather than its rawValue. Thus, the
39
+ // definitions of equality and hashing did not actually match in some cases,
40
+ // leading to broken behavior.
41
+ //
42
+ // The difference between rawValue and the actual contents is subtle, and it
43
+ // only causes problems in custom RawRepresentable implementations where the
44
+ // rawValue isn't actually the storage representation, like the weird struct
45
+ // above.
46
+ //
47
+ // rdar://problem/45308741
35
48
36
- // CHECK: s1 == s2: true
37
- print ( " s1 == s2: \( s1 == s2 ) " )
49
+ let s1 = TrickyRawRepresentable ( rawValue : " café " ) !
50
+ let s2 = TrickyRawRepresentable ( rawValue : " cafe \u{301} " ) !
38
51
39
- // CHECK: hashValue matches: true
40
- print ( " hashValue matches: \( s1. hashValue == s2. hashValue) " )
52
+ expectEqual ( s1, s2)
53
+ expectEqual ( s1. hashValue, s2. hashValue)
54
+ expectEqual ( Hasher . hash ( s1) , Hasher . hash ( s2) )
55
+ expectEqual ( s1. _rawHashValue ( seed: 42 ) , s2. _rawHashValue ( seed: 42 ) )
56
+ }
41
57
42
- extension Hasher {
43
- static func hash< H: Hashable > ( _ value: H ) -> Int {
44
- var hasher = Hasher ( )
45
- hasher. combine ( value)
46
- return hasher. finalize ( )
58
+ struct CustomRawRepresentable : RawRepresentable , Hashable {
59
+ var rawValue : Int
60
+
61
+ init ? ( rawValue: Int ) {
62
+ self . rawValue = rawValue
63
+ }
64
+
65
+ func hash( into hasher: inout Hasher ) {
66
+ hasher. combine ( 23 )
47
67
}
48
68
}
49
69
50
- // CHECK: hash(into:) matches: true
51
- print ( " hash(into:) matches: \( Hasher . hash ( s1) == Hasher . hash ( s2) ) " )
70
+ suite. test ( " Custom hashing " ) {
71
+ // In 5.0, RawRepresentable had a bogus default implementation for
72
+ // _rawHashValue(seed:) that interfered with custom hashing for
73
+ // RawRepresentable types. Adding a custom hash(into:) implementation should
74
+ // always be enough to customize hashing.
75
+ //
76
+ // See https://bugs.swift.org/browse/SR-10734
77
+
78
+ if #available( macOS 10 . 15 , iOS 13 . 0 , tvOS 13 . 0 , watchOS 6 . 0 , * ) {
79
+ let r = CustomRawRepresentable ( rawValue: 42 ) !
80
+ expectEqual ( Hasher . hash ( r) , Hasher . hash ( 23 ) )
81
+ }
82
+ }
52
83
53
- // CHECK: _rawHashValue(seed:) matches: true
54
- let r1 = s1. _rawHashValue ( seed: 42 )
55
- let r2 = s2. _rawHashValue ( seed: 42 )
56
- print ( " _rawHashValue(seed:) matches: \( r1 == r2) " )
84
+ runAllTests ( )
0 commit comments