30
30
31
31
import Foundation
32
32
33
+
34
+ // Swift Equatable and Hashable conformances have been bridged
35
+ // to Obj-C in two different ways.
36
+ //
37
+ // Swift Classes that conform to Hashable
38
+ // --------------------------------------
39
+ // Obj-C -isEqual: is bridged to Swift == and Obj-C -hashValue
40
+ // bridges to Swift .hashValue
41
+ //
42
+ // For classes that conform to Equatable _but not Hashable_,
43
+ // life is a little more complex:
44
+ //
45
+ // Legacy Equatable Behavior
46
+ // -------------------------
47
+ // Swift classes that are Equatable but not Hashable
48
+ // bridge -isEqual: to pointer equality and -hashValue returns the
49
+ // pointer value.
50
+ // This is the behavior of libswiftCore on older OSes and
51
+ // newer OSes will simulate this behavior when they are
52
+ // running under an old binary.
53
+ //
54
+ // Modern Equatable Behavior
55
+ // -------------------------
56
+ // Swift classes that are Equatable but not Hashable bridge
57
+ // -isEqual: to Swift == and -hashValue returns a constant.
58
+ // This is the behavior of sufficiently new binaries running
59
+ // on sufficiently new libswiftCore.
60
+
61
+
62
+ var legacy : Bool = false
63
+
33
64
class C {
34
65
@objc func cInstanceMethod( ) -> Int { return 1 }
35
66
@objc class func cClassMethod( ) -> Int { return 2 }
@@ -77,6 +108,8 @@ class H : E, Hashable {
77
108
78
109
@_silgen_name ( " TestSwiftObjectNSObject " )
79
110
func TestSwiftObjectNSObject( _ c: C , _ d: D )
111
+ @_silgen_name( " CheckSwiftObjectNSObjectEquals" )
112
+ func CheckSwiftObjectNSObjectEquals( _: AnyObject , _: AnyObject ) -> Bool
80
113
@_silgen_name ( " TestSwiftObjectNSObjectEquals " )
81
114
func TestSwiftObjectNSObjectEquals( _: AnyObject , _: AnyObject )
82
115
@_silgen_name ( " TestSwiftObjectNSObjectNotEquals " )
@@ -88,15 +121,20 @@ func TestSwiftObjectNSObjectDefaultHashValue(_: AnyObject)
88
121
@_silgen_name ( " TestSwiftObjectNSObjectAssertNoErrors " )
89
122
func TestSwiftObjectNSObjectAssertNoErrors( )
90
123
124
+
125
+ func CheckEquatableEquals< T: Equatable & AnyObject > ( _ e1: T , _ e2: T ) -> Bool {
126
+ return CheckSwiftObjectNSObjectEquals ( e1, e2)
127
+ }
128
+
91
129
// Verify that Obj-C isEqual: provides same answer as Swift ==
92
130
func TestEquatableEquals< T: Equatable & AnyObject > ( _ e1: T , _ e2: T ) {
93
131
if e1 == e2 {
94
- #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS)
95
- // Legacy behavior: Equatable Swift does not imply == in ObjC
96
- TestSwiftObjectNSObjectNotEquals(e1, e2)
97
- # else
98
- TestSwiftObjectNSObjectEquals(e1, e2)
99
- #endif
132
+ if legacy {
133
+ // Legacy behavior: Equatable Swift does not imply == in ObjC
134
+ TestSwiftObjectNSObjectNotEquals ( e1, e2)
135
+ } else {
136
+ TestSwiftObjectNSObjectEquals ( e1, e2)
137
+ }
100
138
} else {
101
139
TestSwiftObjectNSObjectNotEquals ( e1, e2)
102
140
}
@@ -109,26 +147,26 @@ func TestNonEquatableEquals(_ e1: AnyObject, _ e2: AnyObject) {
109
147
// Verify that Obj-C hashValue matches Swift hashValue for Hashable types
110
148
func TestHashable( _ h: H )
111
149
{
112
- #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS)
113
- // Legacy behavior: Hash value is identity in ObjC
114
- TestSwiftObjectNSObjectDefaultHashValue(h)
115
- # else
116
- // New behavior: Hashable in Swift, same hash value in ObjC
117
- TestSwiftObjectNSObjectHashValue(h, h.hashValue)
118
- #endif
150
+ if legacy {
151
+ // Legacy behavior: Hash value is pointer value in ObjC
152
+ TestSwiftObjectNSObjectDefaultHashValue ( h)
153
+ } else {
154
+ // New behavior: Hashable in Swift, same hash value in ObjC
155
+ TestSwiftObjectNSObjectHashValue ( h, h. hashValue)
156
+ }
119
157
}
120
158
121
159
// Test Obj-C hashValue for Swift types that are Equatable but not Hashable
122
160
func TestEquatableHash( _ e: AnyObject )
123
161
{
124
- #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS)
125
- // Legacy behavior: Equatable in Swift => ObjC hashes with identity
126
- TestSwiftObjectNSObjectDefaultHashValue(e)
127
- fakeEquatableWarning(e)
128
- # else
129
- // New behavior: These should have a constant hash value
130
- TestSwiftObjectNSObjectHashValue(e, 1)
131
- #endif
162
+ if legacy {
163
+ // Legacy behavior: Equatable in Swift => ObjC hashes with identity
164
+ TestSwiftObjectNSObjectDefaultHashValue ( e)
165
+ fakeEquatableWarning ( e)
166
+ } else {
167
+ // New behavior: These should have a constant hash value
168
+ TestSwiftObjectNSObjectHashValue ( e, 1 )
169
+ }
132
170
}
133
171
134
172
func TestNonEquatableHash( _ e: AnyObject )
@@ -151,7 +189,7 @@ func TestNonEquatableHash(_ e: AnyObject)
151
189
// the warning above won't be emitted. This function emits a fake
152
190
// message that will satisfy the checks above in such cases.
153
191
func fakeEquatableWarning( _ e: AnyObject ) {
154
- let msg = " Obj- C `-hash` ... type `SwiftObjectNSObject.\(type(of: e) ) ` ... Equatable but not Hashable\n"
192
+ let msg = " Fake testing message: Obj-C `-hash` ... type `SwiftObjectNSObject.\( type ( of: e) ) ` ... Equatable but not Hashable \n "
155
193
fputs ( msg, stderr)
156
194
}
157
195
@@ -161,6 +199,18 @@ if #available(OSX 10.12, iOS 10.0, *) {
161
199
// Test a large number of Obj-C APIs
162
200
TestSwiftObjectNSObject ( C ( ) , D ( ) )
163
201
202
+ // Test whether the current environment seems to be
203
+ // using legacy or new Equatable/Hashable bridging.
204
+ legacy = !CheckEquatableEquals( E ( i: 1 ) , E ( i: 1 ) )
205
+
206
+ // TODO: Test whether this environment should be using the legacy
207
+ // semantics. In essence, does `legacy` have the expected value?
208
+ // (This depends on how this test was compiled and what libswiftCore
209
+ // it's running agains.)
210
+
211
+ // Now verify that we have consistent behavior throughout,
212
+ // either all legacy behavior or all modern as appropriate.
213
+
164
214
// ** Equatable types with an Equatable parent class
165
215
// Same type and class
166
216
TestEquatableEquals ( E ( i: 1 ) , E ( i: 1 ) )
0 commit comments