Skip to content

Commit b3ed7ae

Browse files
committed
Bincompat hooks for revised hash/isEqual interop
This adds in hooks so that the new hash/isEqual interop (which bridges Obj-C hash/isEqual: calls to the corresponding Swift Hashable/Equatable conformances) can be selectively disabled based on the OS and/or client. For now, enable the new semantics everywhere except Apple platforms (which have legacy apps that may be relying on the old semantics).
1 parent 79fcb5e commit b3ed7ae

File tree

4 files changed

+51
-0
lines changed

4 files changed

+51
-0
lines changed

include/swift/Runtime/Bincompat.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ bool useLegacyObjCBoxingInCasting();
4141
/// Whether to use legacy semantics when unboxing __SwiftValue
4242
bool useLegacySwiftValueUnboxingInCasting();
4343

44+
/// Legacy semantics use trivial implementations for -hashValue/-isEqual:
45+
/// requests from ObjC to Swift values.
46+
/// New semantics attempt to dispatch to Swift Hashable/Equatable conformances
47+
/// if present.
48+
bool useLegacySwiftObjCHashing();
49+
4450
} // namespace bincompat
4551

4652
} // namespace runtime

stdlib/public/runtime/Bincompat.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,30 @@ bool useLegacySwiftValueUnboxingInCasting() {
228228
#endif
229229
}
230230

231+
// Controls how ObjC -hashValue and -isEqual are handled
232+
// by Swift objects.
233+
// There are two basic semantics:
234+
// * pointer: -hashValue returns pointer, -isEqual: tests pointer equality
235+
// * proxy: -hashValue calls on Hashable conformance, -isEqual: calls Equatable conformance
236+
//
237+
// Legacy handling:
238+
// * Swift struct/enum values that implement Hashable: proxy -hashValue and -isEqual:
239+
// * Swift struct/enum values that implement Equatable but not Hashable: pointer semantics
240+
// * Swift class values regardless of hashable/Equatable support: pointer semantics
241+
//
242+
// New behavior:
243+
// * Swift struct/enum/class values that implement Hashable: proxy -hashValue and -isEqual:
244+
// * Swift struct/enum/class values that implement Equatable but not Hashable: proxy -isEqual:, constant -hashValue
245+
// * All other cases: pointer semantics
246+
//
247+
bool useLegacySwiftObjCHashing() {
248+
#if BINARY_COMPATIBILITY_APPLE
249+
return true; // For now, legacy behavior on Apple OSes
250+
#else
251+
return false; // Always use the new behavior on non-Apple OSes
252+
#endif
253+
}
254+
231255
} // namespace bincompat
232256

233257
} // namespace runtime

stdlib/public/runtime/SwiftObject.mm

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,11 @@ + (BOOL)conformsToProtocol:(Protocol*)proto {
376376
}
377377

378378
- (NSUInteger)hash {
379+
if (runtime::bincompat::useLegacySwiftObjCHashing()) {
380+
// Legacy behavior: Don't proxy to Swift Hashable
381+
return (NSUInteger)self;
382+
}
383+
379384
auto selfMetadata = _swift_getClassOfAllocated(self);
380385

381386
// If it's Hashable, use that
@@ -423,6 +428,11 @@ - (BOOL)isEqual:(id)other {
423428
if (self == other) {
424429
return YES;
425430
}
431+
if (runtime::bincompat::useLegacySwiftObjCHashing()) {
432+
// Legacy behavior: Don't proxy to Swift Hashable or Equatable
433+
return NO; // We know the ids are different
434+
}
435+
426436

427437
// Get Swift type for self and other
428438
auto selfMetadata = _swift_getClassOfAllocated(self);

stdlib/public/runtime/SwiftValue.mm

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "SwiftObject.h"
2525
#include "SwiftValue.h"
2626
#include "swift/Basic/Lazy.h"
27+
#include "swift/Runtime/Bincompat.h"
2728
#include "swift/Runtime/Casting.h"
2829
#include "swift/Runtime/HeapObject.h"
2930
#include "swift/Runtime/Metadata.h"
@@ -429,6 +430,11 @@ - (BOOL)isEqual:(id)other {
429430
}
430431
}
431432

433+
if (runtime::bincompat::useLegacySwiftObjCHashing()) {
434+
// Legacy behavior only proxies isEqual: for Hashable, not Equatable
435+
return NO;
436+
}
437+
432438
if (auto equatableConformance = selfHeader->getEquatableConformance()) {
433439
if (auto selfEquatableBaseType = selfHeader->getEquatableBaseType()) {
434440
auto otherEquatableBaseType = otherHeader->getEquatableBaseType();
@@ -458,6 +464,11 @@ - (NSUInteger)hash {
458464
selfHeader->type, hashableConformance);
459465
}
460466

467+
if (!runtime::bincompat::useLegacySwiftObjCHashing()) {
468+
// Legacy behavior doesn't honor Equatable conformance, only Hashable
469+
return (NSUInteger)self;
470+
}
471+
461472
// If Swift type is Equatable but not Hashable,
462473
// we have to return something here that is compatible
463474
// with the `isEqual:` above.

0 commit comments

Comments
 (0)