|
29 | 29 | #include "swift/Runtime/Metadata.h"
|
30 | 30 | #include "swift/Runtime/ObjCBridge.h"
|
31 | 31 | #include "swift/Runtime/Debug.h"
|
| 32 | +#include "swift/Threading/Mutex.h" |
32 | 33 | #include "Private.h"
|
33 | 34 | #include "SwiftEquatableSupport.h"
|
34 | 35 | #include "SwiftHashableSupport.h"
|
35 | 36 | #include <objc/runtime.h>
|
36 | 37 | #include <Foundation/Foundation.h>
|
37 | 38 |
|
38 | 39 | #include <new>
|
| 40 | +#include <unordered_set> |
39 | 41 |
|
40 | 42 | using namespace swift;
|
41 | 43 | using namespace swift::hashable_support;
|
@@ -446,15 +448,43 @@ - (BOOL)isEqual:(id)other {
|
446 | 448 | }
|
447 | 449 |
|
448 | 450 | - (NSUInteger)hash {
|
| 451 | + // If Swift type is Hashable, get the hash value from there |
449 | 452 | auto selfHeader = getSwiftValueHeader(self);
|
450 | 453 | auto hashableConformance = selfHeader->getHashableConformance();
|
451 |
| - if (!hashableConformance) { |
452 |
| - return (NSUInteger)self; |
| 454 | + if (hashableConformance) { |
| 455 | + return _swift_stdlib_Hashable_hashValue_indirect( |
| 456 | + getSwiftValuePayload(self, |
| 457 | + getSwiftValuePayloadAlignMask(selfHeader->type)), |
| 458 | + selfHeader->type, hashableConformance); |
453 | 459 | }
|
454 |
| - return _swift_stdlib_Hashable_hashValue_indirect( |
455 |
| - getSwiftValuePayload(self, |
456 |
| - getSwiftValuePayloadAlignMask(selfHeader->type)), |
457 |
| - selfHeader->type, hashableConformance); |
| 460 | + |
| 461 | + // If Swift type is Equatable but not Hashable, |
| 462 | + // we have to return something here that is compatible |
| 463 | + // with the `isEqual:` above. |
| 464 | + auto equatableConformance = selfHeader->getEquatableConformance(); |
| 465 | + if (equatableConformance) { |
| 466 | + // Warn once per type about this |
| 467 | + auto metadata = getSwiftValueTypeMetadata(self); |
| 468 | + static Lazy<std::unordered_set<const Metadata *>> warned; |
| 469 | + static LazyMutex warnedLock; |
| 470 | + LazyMutex::ScopedLock guard(warnedLock); |
| 471 | + auto result = warned.get().insert(metadata); |
| 472 | + auto inserted = std::get<1>(result); |
| 473 | + if (inserted) { |
| 474 | + TypeNamePair typeName = swift_getTypeName(metadata, true); |
| 475 | + warning(0, |
| 476 | + "Obj-C `-hash` invoked on a Swift value of type `%s` that is Equatable but not Hashable; " |
| 477 | + "this can lead to severe performance problems.\n", |
| 478 | + typeName.data); |
| 479 | + } |
| 480 | + // Constant value (yuck!) is the only choice here |
| 481 | + return (NSUInteger)1; |
| 482 | + } |
| 483 | + |
| 484 | + // If the Swift type is neither Equatable nor Hashable, |
| 485 | + // then we can hash the identity, which should be pretty |
| 486 | + // good in practice. |
| 487 | + return (NSUInteger)self; |
458 | 488 | }
|
459 | 489 |
|
460 | 490 | static id getValueDescription(__SwiftValue *self) {
|
|
0 commit comments