3030
3131import Foundation
3232
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+
3364class C {
3465 @objc func cInstanceMethod( ) -> Int { return 1 }
3566 @objc class func cClassMethod( ) -> Int { return 2 }
@@ -77,6 +108,8 @@ class H : E, Hashable {
77108
78109@_silgen_name ( " TestSwiftObjectNSObject " )
79110func TestSwiftObjectNSObject( _ c: C , _ d: D )
111+ @_silgen_name( " CheckSwiftObjectNSObjectEquals" )
112+ func CheckSwiftObjectNSObjectEquals( _: AnyObject , _: AnyObject ) -> Bool
80113@_silgen_name ( " TestSwiftObjectNSObjectEquals " )
81114func TestSwiftObjectNSObjectEquals( _: AnyObject , _: AnyObject )
82115@_silgen_name ( " TestSwiftObjectNSObjectNotEquals " )
@@ -88,15 +121,20 @@ func TestSwiftObjectNSObjectDefaultHashValue(_: AnyObject)
88121@_silgen_name ( " TestSwiftObjectNSObjectAssertNoErrors " )
89122func TestSwiftObjectNSObjectAssertNoErrors( )
90123
124+
125+ func CheckEquatableEquals< T: Equatable & AnyObject > ( _ e1: T , _ e2: T ) -> Bool {
126+ return CheckSwiftObjectNSObjectEquals ( e1, e2)
127+ }
128+
91129// Verify that Obj-C isEqual: provides same answer as Swift ==
92130func TestEquatableEquals< T: Equatable & AnyObject > ( _ e1: T , _ e2: T ) {
93131 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+ }
100138 } else {
101139 TestSwiftObjectNSObjectNotEquals ( e1, e2)
102140 }
@@ -109,26 +147,26 @@ func TestNonEquatableEquals(_ e1: AnyObject, _ e2: AnyObject) {
109147// Verify that Obj-C hashValue matches Swift hashValue for Hashable types
110148func TestHashable( _ h: H )
111149{
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+ }
119157}
120158
121159// Test Obj-C hashValue for Swift types that are Equatable but not Hashable
122160func TestEquatableHash( _ e: AnyObject )
123161{
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+ }
132170}
133171
134172func TestNonEquatableHash( _ e: AnyObject )
@@ -151,7 +189,7 @@ func TestNonEquatableHash(_ e: AnyObject)
151189// the warning above won't be emitted. This function emits a fake
152190// message that will satisfy the checks above in such cases.
153191func 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 "
155193 fputs ( msg, stderr)
156194}
157195
@@ -161,6 +199,18 @@ if #available(OSX 10.12, iOS 10.0, *) {
161199 // Test a large number of Obj-C APIs
162200 TestSwiftObjectNSObject ( C ( ) , D ( ) )
163201
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+
164214 // ** Equatable types with an Equatable parent class
165215 // Same type and class
166216 TestEquatableEquals ( E ( i: 1 ) , E ( i: 1 ) )
0 commit comments