Skip to content

Commit 69ef3ad

Browse files
authored
Merge pull request swiftlang#71852 from tbkka/tbkka-rdar123422591
Fix hash/isEqual interop conditionals, update tests
2 parents b2b22e7 + 2be0f04 commit 69ef3ad

File tree

5 files changed

+58
-9
lines changed

5 files changed

+58
-9
lines changed

stdlib/public/runtime/Bincompat.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/Runtime/EnvironmentVariables.h"
2121
#include "swift/Threading/Once.h"
2222
#include "swift/shims/RuntimeShims.h"
23+
#include "swift/shims/Target.h"
2324
#include <stdint.h>
2425

2526
// If this is an Apple OS, use the Apple binary compatibility rules
@@ -247,6 +248,8 @@ bool useLegacySwiftValueUnboxingInCasting() {
247248
bool useLegacySwiftObjCHashing() {
248249
#if BINARY_COMPATIBILITY_APPLE
249250
return true; // For now, legacy behavior on Apple OSes
251+
#elif SWIFT_TARGET_OS_DARWIN
252+
return true; // For now, use legacy behavior on open-source builds for Apple platforms
250253
#else
251254
return false; // Always use the new behavior on non-Apple OSes
252255
#endif

stdlib/public/runtime/SwiftValue.mm

+1-1
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ - (NSUInteger)hash {
464464
selfHeader->type, hashableConformance);
465465
}
466466

467-
if (!runtime::bincompat::useLegacySwiftObjCHashing()) {
467+
if (runtime::bincompat::useLegacySwiftObjCHashing()) {
468468
// Legacy behavior doesn't honor Equatable conformance, only Hashable
469469
return (NSUInteger)self;
470470
}

test/stdlib/BridgeEquatableToObjC.swift

+5
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@ BridgeEquatableToObjC.test("Bridge equatable struct") {
3232
let objcResult = objcA.isEqual(objcB)
3333

3434
expectEqual(swiftResult, true)
35+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
36+
// Apple platforms use old semantics for now...
37+
expectEqual(objcResult, false)
38+
#else
3539
expectEqual(objcResult, true)
40+
#endif
3641
}
3742

3843
BridgeEquatableToObjC.test("Bridge non-equatable struct") {

test/stdlib/SwiftObjectNSObject.swift

+30-5
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,12 @@ func TestSwiftObjectNSObjectAssertNoErrors()
9191
// Verify that Obj-C isEqual: provides same answer as Swift ==
9292
func TestEquatableEquals<T: Equatable & AnyObject>(_ e1: T, _ e2: T) {
9393
if e1 == e2 {
94+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
95+
// Legacy behavior: Equatable Swift does not imply == in ObjC
96+
TestSwiftObjectNSObjectNotEquals(e1, e2)
97+
#else
9498
TestSwiftObjectNSObjectEquals(e1, e2)
99+
#endif
95100
} else {
96101
TestSwiftObjectNSObjectNotEquals(e1, e2)
97102
}
@@ -104,14 +109,26 @@ func TestNonEquatableEquals(_ e1: AnyObject, _ e2: AnyObject) {
104109
// Verify that Obj-C hashValue matches Swift hashValue for Hashable types
105110
func TestHashable(_ h: H)
106111
{
112+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
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
107117
TestSwiftObjectNSObjectHashValue(h, h.hashValue)
118+
#endif
108119
}
109120
110121
// Test Obj-C hashValue for Swift types that are Equatable but not Hashable
111122
func TestEquatableHash(_ e: AnyObject)
112123
{
113-
// These should have a constant hash value
124+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
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
114130
TestSwiftObjectNSObjectHashValue(e, 1)
131+
#endif
115132
}
116133
117134
func TestNonEquatableHash(_ e: AnyObject)
@@ -125,11 +142,19 @@ func TestNonEquatableHash(_ e: AnyObject)
125142
// CHECK-NEXT: d ##SwiftObjectNSObject.D##
126143
// CHECK-NEXT: S ##{{.*}}SwiftObject##
127144
128-
// Full message is longer, but this is the essential part...
145+
// Verify that the runtime emits the warning that we expected...
129146
// CHECK-NEXT: Obj-C `-hash` {{.*}} type `SwiftObjectNSObject.E` {{.*}} Equatable but not Hashable
130147
// CHECK-NEXT: Obj-C `-hash` {{.*}} type `SwiftObjectNSObject.E1` {{.*}} Equatable but not Hashable
131148
// CHECK-NEXT: Obj-C `-hash` {{.*}} type `SwiftObjectNSObject.E2` {{.*}} Equatable but not Hashable
132149
150+
// If we're checking legacy behavior or unsupported platform, then
151+
// the warning above won't be emitted. This function emits a fake
152+
// message that will satisfy the checks above in such cases.
153+
func fakeEquatableWarning(_ e: AnyObject) {
154+
let msg = "Obj-C `-hash` ... type `SwiftObjectNSObject.\(type(of: e))` ... Equatable but not Hashable\n"
155+
fputs(msg, stderr)
156+
}
157+
133158
// Temporarily disable this test on older OSes until we have time to
134159
// look into why it's failing there. rdar://problem/47870743
135160
if #available(OSX 10.12, iOS 10.0, *) {
@@ -195,7 +220,7 @@ if #available(OSX 10.12, iOS 10.0, *) {
195220
fputs("c ##SwiftObjectNSObject.C##\n", stderr)
196221
fputs("d ##SwiftObjectNSObject.D##\n", stderr)
197222
fputs("S ##Swift._SwiftObject##\n", stderr)
198-
fputs("Obj-C `-hash` ... type `SwiftObjectNSObject.E` ... Equatable but not Hashable", stderr)
199-
fputs("Obj-C `-hash` ... type `SwiftObjectNSObject.E1` ... Equatable but not Hashable", stderr)
200-
fputs("Obj-C `-hash` ... type `SwiftObjectNSObject.E2` ... Equatable but not Hashable", stderr)
223+
fakeEquatableWarning(E(i:1))
224+
fakeEquatableWarning(E1(i:1))
225+
fakeEquatableWarning(E2(i:1))
201226
}

test/stdlib/SwiftValueNSObject.swift

+19-3
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,25 @@ func TestSwiftValueNSObjectDefaultHashValue(_: AnyObject)
7777
func TestSwiftValueNSObjectAssertNoErrors()
7878

7979
// Verify that Obj-C isEqual: provides same answer as Swift ==
80+
// This has been true for a long time for Hashable value types
81+
func TestHashableEquals<T: Equatable>(_ e1: T, _ e2: T) {
82+
if e1 == e2 {
83+
TestSwiftValueNSObjectEquals(e1 as AnyObject, e2 as AnyObject)
84+
} else {
85+
TestSwiftValueNSObjectNotEquals(e1 as AnyObject, e2 as AnyObject)
86+
}
87+
}
88+
89+
// Verify that Obj-C isEqual: provides same answer as Swift ==
90+
// This has not always been true for Equatable value types
8091
func TestEquatableEquals<T: Equatable>(_ e1: T, _ e2: T) {
8192
if e1 == e2 {
93+
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
94+
// Legacy: Swift Equatable is not used in ObjC
95+
TestSwiftValueNSObjectNotEquals(e1 as AnyObject, e2 as AnyObject)
96+
#else
8297
TestSwiftValueNSObjectEquals(e1 as AnyObject, e2 as AnyObject)
98+
#endif
8399
} else {
84100
TestSwiftValueNSObjectNotEquals(e1 as AnyObject, e2 as AnyObject)
85101
}
@@ -143,9 +159,9 @@ if #available(OSX 10.12, iOS 10.0, *) {
143159
TestNonEquatableHash(D())
144160

145161
// Hashable types are also Equatable
146-
TestEquatableEquals(H(i:1), H(i:1))
147-
TestEquatableEquals(H(i:1), H(i:2))
148-
TestEquatableEquals(H(i:2), H(i:1))
162+
TestHashableEquals(H(i:1), H(i:1))
163+
TestHashableEquals(H(i:1), H(i:2))
164+
TestHashableEquals(H(i:2), H(i:1))
149165

150166
// Verify Obj-C hash value agrees with Swift
151167
TestHashable(H(i:1))

0 commit comments

Comments
 (0)