Skip to content

Commit 55d4a56

Browse files
authored
Merge pull request #74821 from tbkka/tbkka-bincompat
Upstream some binary-compatibility logic
2 parents 31fc0a6 + 4470ed7 commit 55d4a56

File tree

4 files changed

+116
-34
lines changed

4 files changed

+116
-34
lines changed

stdlib/public/runtime/Bincompat.cpp

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,20 @@ static enum sdk_test isAppAtLeastFall2023() {
6767
const dyld_build_version_t fall_2023_os_versions = {0xffffffff, 0x007e70901};
6868
return isAppAtLeast(fall_2023_os_versions);
6969
}
70+
71+
static enum sdk_test isAppAtLeastFall2024() {
72+
const dyld_build_version_t fall_2024_os_versions = {0xffffffff, 0x007e80000};
73+
return isAppAtLeast(fall_2024_os_versions);
74+
}
7075
#endif
7176

7277
static _SwiftStdlibVersion binCompatVersionOverride = { 0 };
7378

7479
static _SwiftStdlibVersion const knownVersions[] = {
7580
{ /* 5.6.0 */0x050600 },
7681
{ /* 5.7.0 */0x050700 },
82+
// Note: If you add a new entry here, also add it to versionMap in
83+
// _swift_stdlib_isExecutableLinkedOnOrAfter below.
7784
{ 0 },
7885
};
7986

@@ -111,9 +118,27 @@ extern "C" __swift_bool _swift_stdlib_isExecutableLinkedOnOrAfter(
111118
}
112119

113120
#if BINARY_COMPATIBILITY_APPLE
114-
// Return true for all known versions for now -- we can't map them to OS
115-
// versions at this time.
116-
return isKnownBinCompatVersion(version);
121+
typedef struct {
122+
_SwiftStdlibVersion stdlib;
123+
dyld_build_version_t dyld;
124+
} stdlib_version_map;
125+
126+
const dyld_build_version_t spring_2022_os_versions = {0xffffffff, 0x007e60301};
127+
const dyld_build_version_t fall_2022_os_versions = {0xffffffff, 0x007e60901};
128+
129+
static stdlib_version_map const versionMap[] = {
130+
{ { /* 5.6.0 */0x050600 }, spring_2022_os_versions },
131+
{ { /* 5.7.0 */0x050700 }, fall_2022_os_versions },
132+
// Note: if you add a new entry here, also add it to knownVersions above.
133+
{ { 0 }, { 0, 0 } },
134+
};
135+
136+
for (uint32_t i = 0; versionMap[i].stdlib._value != 0; ++i) {
137+
if (versionMap[i].stdlib._value == version._value) {
138+
return isAppAtLeast(versionMap[i].dyld) == newApp;
139+
}
140+
}
141+
return false;
117142

118143
#else // !BINARY_COMPATIBILITY_APPLE
119144
return isKnownBinCompatVersion(version);
@@ -247,9 +272,11 @@ bool useLegacySwiftValueUnboxingInCasting() {
247272
//
248273
bool useLegacySwiftObjCHashing() {
249274
#if BINARY_COMPATIBILITY_APPLE
250-
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
275+
switch (isAppAtLeastFall2024()) {
276+
case oldOS: return true; // Legacy behavior on old OS
277+
case oldApp: return true; // Legacy behavior for old apps
278+
case newApp: return false; // New behavior for new apps
279+
}
253280
#else
254281
return false; // Always use the new behavior on non-Apple OSes
255282
#endif
@@ -268,12 +295,13 @@ bool useLegacySwiftObjCHashing() {
268295
// * This allows the method to invoke 'SerialExecutor/checkIsolated'
269296
// * Which is allowed to call 'dispatch_precondition' and handle "on dispatch queue but not on Swift executor" cases
270297
//
271-
// FIXME(concurrency): Once the release is announced, adjust the logic detecting the SDKs
272298
bool swift_bincompat_useLegacyNonCrashingExecutorChecks() {
273299
#if BINARY_COMPATIBILITY_APPLE
274-
return true; // For now, legacy behavior on Apple OSes
275-
#elif SWIFT_TARGET_OS_DARWIN
276-
return true; // For now, use legacy behavior on open-source builds for Apple platforms
300+
switch (isAppAtLeastFall2024()) {
301+
case oldOS: return true; // Legacy behavior on old OS
302+
case oldApp: return true; // Legacy behavior for old apps
303+
case newApp: return false; // New behavior for new apps
304+
}
277305
#else
278306
return false; // Always use the new behavior on non-Apple OSes
279307
#endif

test/Distributed/Runtime/distributed_actor_custom_executor_availability.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import Distributed
2929
let system = LocalTestingDistributedActorSystem()
3030

3131
tests.test("5.7 actor, no availability executor property => no custom executor") {
32-
expectCrashLater(withMessage: "Incorrect actor executor assumption; Expected MainActor executor")
32+
expectCrashLater()
3333
try! await FiveSevenActor_NothingExecutor(actorSystem: system).test(x: 42)
3434
}
3535

@@ -38,7 +38,7 @@ import Distributed
3838
}
3939

4040
tests.test("5.7 actor, 5.9 executor property => no custom executor") {
41-
expectCrashLater(withMessage: "Incorrect actor executor assumption; Expected MainActor executor")
41+
expectCrashLater()
4242
try! await FiveSevenActor_FiveNineExecutor(actorSystem: system).test(x: 42)
4343
}
4444

test/stdlib/Inputs/SwiftObjectNSObject/SwiftObjectNSObject.m

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ void TestSwiftObjectNSObjectAssertNoErrors(void)
8989
}
9090
}
9191

92+
int CheckSwiftObjectNSObjectEquals(id e1, id e2)
93+
{
94+
return [e1 isEqual:e2];
95+
}
9296

9397
void TestSwiftObjectNSObjectEquals(id e1, id e2)
9498
{

test/stdlib/SwiftObjectNSObject.swift

Lines changed: 72 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,37 @@
3030

3131
import 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+
3364
class 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")
79110
func TestSwiftObjectNSObject(_ c: C, _ d: D)
111+
@_silgen_name("CheckSwiftObjectNSObjectEquals")
112+
func CheckSwiftObjectNSObjectEquals(_: AnyObject, _: AnyObject) -> Bool
80113
@_silgen_name("TestSwiftObjectNSObjectEquals")
81114
func TestSwiftObjectNSObjectEquals(_: AnyObject, _: AnyObject)
82115
@_silgen_name("TestSwiftObjectNSObjectNotEquals")
@@ -88,15 +121,20 @@ func TestSwiftObjectNSObjectDefaultHashValue(_: AnyObject)
88121
@_silgen_name("TestSwiftObjectNSObjectAssertNoErrors")
89122
func 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 ==
92130
func 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
110148
func 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
122160
func 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

134172
func 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.
153191
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"
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

Comments
 (0)