Skip to content

Commit 3d03925

Browse files
authored
Merge pull request swiftlang#39378 from Catfish-Man/any-hashable-in-this-economy
Avoid protocol conformance lookups in more NSString bridging scenarios
2 parents 3f815ac + 12166a9 commit 3d03925

File tree

4 files changed

+46
-6
lines changed

4 files changed

+46
-6
lines changed

stdlib/public/core/AnyHashable.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,13 @@ public struct AnyHashable {
145145
/// Creates a type-erased hashable value that wraps the given instance.
146146
///
147147
/// - Parameter base: A hashable value to wrap.
148+
@_specialize(where H == String)
148149
public init<H: Hashable>(_ base: H) {
150+
if H.self == String.self {
151+
self.init(_box: _ConcreteHashableBox(base))
152+
return
153+
}
154+
149155
if let custom =
150156
(base as? _HasCustomAnyHashableRepresentation)?._toCustomAnyHashable() {
151157
self = custom

stdlib/public/runtime/DynamicCast.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -764,14 +764,21 @@ tryCastToAnyHashable(
764764
assert(cast<StructMetadata>(destType)->Description
765765
== &STRUCT_TYPE_DESCR_SYM(s11AnyHashable));
766766

767+
const HashableWitnessTable *hashableConformance = nullptr;
768+
767769
switch (srcType->getKind()) {
768770
case MetadataKind::ForeignClass: // CF -> String
769771
case MetadataKind::ObjCClassWrapper: { // Obj-C -> String
770772
#if SWIFT_OBJC_INTEROP
771-
// TODO: Implement a fast path for NSString->AnyHashable casts.
772-
// These are incredibly common because an NSDictionary with
773-
// NSString keys is bridged by default to [AnyHashable:Any].
774-
// Until this is implemented, fall through to the general case
773+
auto cls = srcType;
774+
auto nsString = getNSStringMetadata();
775+
do {
776+
if (cls == nsString) {
777+
hashableConformance = getNSStringHashableConformance();
778+
break;
779+
}
780+
cls = _swift_class_getSuperclass(cls);
781+
} while (cls != nullptr);
775782
break;
776783
#else
777784
// If no Obj-C interop, just fall through to the general case.
@@ -805,8 +812,11 @@ tryCastToAnyHashable(
805812

806813

807814
// General case: If it conforms to Hashable, we cast it
808-
auto hashableConformance = reinterpret_cast<const HashableWitnessTable *>(
809-
swift_conformsToProtocol(srcType, &HashableProtocolDescriptor));
815+
if (hashableConformance == nullptr) {
816+
hashableConformance = reinterpret_cast<const HashableWitnessTable *>(
817+
swift_conformsToProtocol(srcType, &HashableProtocolDescriptor)
818+
);
819+
}
810820
if (hashableConformance) {
811821
_swift_convertToAnyHashableIndirect(srcValue, destLocation,
812822
srcType, hashableConformance);

stdlib/public/runtime/SwiftObject.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <cstdint>
2323
#include <utility>
2424
#include "swift/Runtime/HeapObject.h"
25+
#include "../runtime/SwiftHashableSupport.h"
2526
#if SWIFT_OBJC_INTEROP
2627
#include <objc/NSObject.h>
2728
#endif
@@ -89,7 +90,11 @@ namespace swift {
8990

9091
/// Get the NSObject metadata.
9192
const Metadata *getNSObjectMetadata();
93+
const Metadata *getNSStringMetadata();
9294

95+
namespace hashable_support {
96+
const HashableWitnessTable *getNSStringHashableConformance();
97+
}
9398
}
9499

95100
#endif

stdlib/public/runtime/SwiftObject.mm

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#endif
5757

5858
using namespace swift;
59+
using namespace hashable_support;
5960

6061
#if SWIFT_HAS_ISA_MASKING
6162
OBJC_EXPORT __attribute__((__weak_import__))
@@ -1600,6 +1601,24 @@ void swift_objc_swift3ImplicitObjCEntrypoint(id self, SEL selector,
16001601
swift_getObjCClassMetadata((const ClassMetadata *)[NSObject class]));
16011602
}
16021603

1604+
const Metadata *swift::getNSStringMetadata() {
1605+
return SWIFT_LAZY_CONSTANT(swift_getObjCClassMetadata(
1606+
(const ClassMetadata *)objc_lookUpClass("NSString")
1607+
));
1608+
}
1609+
1610+
const HashableWitnessTable *
1611+
swift::hashable_support::getNSStringHashableConformance() {
1612+
return SWIFT_LAZY_CONSTANT(
1613+
reinterpret_cast<const HashableWitnessTable *>(
1614+
swift_conformsToProtocol(
1615+
getNSStringMetadata(),
1616+
&HashableProtocolDescriptor
1617+
)
1618+
)
1619+
);
1620+
}
1621+
16031622
#endif
16041623

16051624
const ClassMetadata *swift::getRootSuperclass() {

0 commit comments

Comments
 (0)