30
30
#include " swift/Runtime/ObjCBridge.h"
31
31
#include " swift/Runtime/Debug.h"
32
32
#include " Private.h"
33
+ #include " SwiftEquatableSupport.h"
33
34
#include " SwiftHashableSupport.h"
34
35
#include < objc/runtime.h>
35
36
#include < Foundation/Foundation.h>
@@ -57,32 +58,22 @@ - (id)copyWithZone:(NSZone *)zone;
57
58
58
59
@end
59
60
60
- struct EquatableWitnessTable ;
61
-
62
- // / Calls `Equatable.==` through an `Equatable` witness table
63
- SWIFT_CC (swift) SWIFT_RUNTIME_STDLIB_INTERNAL
64
- bool _swift_stdlib_Equatable_isEqual_indirect(
65
- const void *lhsValue, const void *rhsValue, const Metadata *type,
66
- const EquatableWitnessTable *wt);
67
-
68
61
// / The fixed-size ivars of `__SwiftValue`. The actual boxed value is
69
62
// / tail-allocated.
70
63
struct SwiftValueHeader {
71
64
// / The type of the value contained in the `__SwiftValue` box.
72
65
const Metadata *type;
73
66
74
- // / The base type that introduces the `Hashable` conformance.
75
- // / This member is only available for native Swift errors.
67
+ // / The base type that introduces the `Hashable` or `Equatable` conformance.
76
68
// / This member is lazily-initialized.
77
- // / Instead of using it directly, call `getHashableBaseType()`.
78
- mutable std::atomic<const Metadata *> hashableBaseType ;
69
+ // / Instead of using it directly, call `getHashableBaseType()` or `getEquatableBaseType()`
70
+ mutable std::atomic<const Metadata *> cachedBaseType ;
79
71
80
72
// / The witness table for `Hashable` conformance.
81
73
// / This member is only available for native Swift errors.
82
74
// / This member is lazily-initialized.
83
75
// / Instead of using it directly, call `getHashableConformance()`.
84
- mutable std::atomic<const hashable_support::HashableWitnessTable *>
85
- hashableConformance;
76
+ mutable std::atomic<const void *> cachedConformance;
86
77
87
78
// / Get the base type that conforms to `Hashable`.
88
79
// / Returns NULL if the type does not conform.
@@ -98,75 +89,126 @@ bool _swift_stdlib_Equatable_isEqual_indirect(
98
89
99
90
// / Get the `Equatable` protocol witness table for the contained type.
100
91
// / Returns NULL if the type does not conform.
101
- const EquatableWitnessTable *getEquatableConformance () const ;
92
+ const equatable_support::EquatableWitnessTable *getEquatableConformance () const ;
93
+
94
+ // / Populate the `cachedConformance` with the Hashable conformance
95
+ // / (if there is one), else the Equatable conformance.
96
+ const void * cacheHashableEquatableConformance () const ;
97
+
102
98
103
99
SwiftValueHeader ()
104
- : hashableBaseType (nullptr ), hashableConformance( nullptr ) {}
100
+ : cachedBaseType (nullptr ), cachedConformance(( void *) nullptr ) {}
105
101
};
106
102
107
103
const Metadata *SwiftValueHeader::getHashableBaseType () const {
108
- if (auto type = hashableBaseType.load (std::memory_order_acquire)) {
109
- if (reinterpret_cast <uintptr_t >(type) == 1 ) {
110
- return nullptr ;
111
- }
104
+ auto type = cachedBaseType.load (std::memory_order_acquire);
105
+ if (type == nullptr ) {
106
+ cacheHashableEquatableConformance ();
107
+ type = cachedBaseType.load (std::memory_order_acquire);
108
+ }
109
+ if ((reinterpret_cast <uintptr_t >(type) & 1 ) == 0 ) {
112
110
return type;
111
+ } else {
112
+ return nullptr ;
113
113
}
114
-
115
- const Metadata *expectedType = nullptr ;
116
- const Metadata *hashableBaseType = findHashableBaseType (type);
117
- this ->hashableBaseType .compare_exchange_strong (
118
- expectedType, hashableBaseType ? hashableBaseType
119
- : reinterpret_cast <const Metadata *>(1 ),
120
- std::memory_order_acq_rel);
121
- return type;
122
114
}
123
115
124
- extern " C" const ProtocolDescriptor PROTOCOL_DESCR_SYM (SQ);
125
- static constexpr auto &EquatableProtocolDescriptor = PROTOCOL_DESCR_SYM(SQ);
126
-
127
116
const Metadata *SwiftValueHeader::getEquatableBaseType () const {
128
- auto witnessTable =
129
- swift_conformsToProtocolCommon (type, &EquatableProtocolDescriptor);
130
- if (!witnessTable) {
117
+ auto type = cachedBaseType.load (std::memory_order_acquire);
118
+ if (type == nullptr ) {
119
+ cacheHashableEquatableConformance ();
120
+ type = cachedBaseType.load (std::memory_order_acquire);
121
+ }
122
+ if ((reinterpret_cast <uintptr_t >(type) & 1 ) == 0 ) {
123
+ // A Hashable conformance was found
131
124
return nullptr ;
125
+ } else {
126
+ // An Equatable conformance (or neither) was found
127
+ return reinterpret_cast <const Metadata *>(reinterpret_cast <uintptr_t >(type) & ~1ULL );
132
128
}
129
+ }
130
+
131
+ // Set cachedConformance to the Hashable conformance if
132
+ // there is one, else the Equatable conformance.
133
+ // Also set cachedBaseType to the parent type that
134
+ // introduced the Hashable/Equatable conformance.
135
+ // The cached conformance and type set the LSbit to indicate
136
+ // which conformance is present:
137
+ // * If the LSbit is not set, it's the Hashable conformance
138
+ // * If the value is exactly 1, neither conformance is present
139
+ // * If the LSbit is 1, strip it and you'll have the Equatable conformance
140
+ // (Null indicates the cache has not been initialized yet)
141
+ const void *
142
+ SwiftValueHeader::cacheHashableEquatableConformance () const {
143
+ // Relevant conformance and baseType
144
+ const void * conformance;
145
+ const Metadata * baseType;
146
+
147
+ // First, see if it's Hashable
148
+ const HashableWitnessTable *hashable =
149
+ reinterpret_cast <const HashableWitnessTable *>(
150
+ swift_conformsToProtocolCommon (type, &HashableProtocolDescriptor));
151
+ if (hashable != nullptr ) {
152
+ conformance = hashable;
153
+ baseType = findHashableBaseType (type);
154
+ } else {
155
+ // If not Hashable, maybe Equatable?
156
+ auto equatable =
157
+ swift_conformsToProtocolCommon (type, &equatable_support::EquatableProtocolDescriptor);
158
+ conformance = reinterpret_cast <const void *>(reinterpret_cast <uintptr_t >(equatable) | 1 );
159
+
160
+ // Find equatable base type
133
161
#if SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES
134
- const auto *conformance = lookThroughOptionalConditionalWitnessTable (
135
- reinterpret_cast <const RelativeWitnessTable*>(witnessTable ))
136
- ->getDescription ();
162
+ const auto *conformance = lookThroughOptionalConditionalWitnessTable (
163
+ reinterpret_cast <const RelativeWitnessTable*>(equatable ))
164
+ ->getDescription ();
137
165
#else
138
- const auto *conformance = witnessTable ->getDescription ();
166
+ const auto *conformance = equatable ->getDescription ();
139
167
#endif
140
- const Metadata *baseTypeThatConformsToEquatable =
141
- findConformingSuperclass (type, conformance);
142
- return baseTypeThatConformsToEquatable;
168
+ const Metadata *baseTypeThatConformsToEquatable =
169
+ findConformingSuperclass (type, conformance);
170
+ baseType = reinterpret_cast <const Metadata *>(reinterpret_cast <uintptr_t >(baseTypeThatConformsToEquatable) | 1 );
171
+ }
172
+
173
+ // Set the conformance/baseType caches atomically
174
+ const void * expectedConformance = nullptr ;
175
+ cachedConformance.compare_exchange_strong (
176
+ expectedConformance, conformance, std::memory_order_acq_rel);
177
+ const Metadata * expectedType = (const Metadata *)nullptr ;
178
+ cachedBaseType.compare_exchange_strong (
179
+ expectedType, baseType, std::memory_order_acq_rel);
180
+
181
+ return conformance;
143
182
}
144
183
145
184
const hashable_support::HashableWitnessTable *
146
185
SwiftValueHeader::getHashableConformance () const {
147
- if (auto wt = hashableConformance.load (std::memory_order_acquire)) {
148
- if (reinterpret_cast <uintptr_t >(wt) == 1 ) {
149
- return nullptr ;
150
- }
151
- return wt;
186
+ const void * wt = cachedConformance.load (std::memory_order_acquire);
187
+ if (wt == nullptr ) {
188
+ wt = cacheHashableEquatableConformance ();
189
+ }
190
+ if ((reinterpret_cast <uintptr_t >(wt) & 1 ) == 0 ) {
191
+ // Hashable conformance found
192
+ return reinterpret_cast <const hashable_support::HashableWitnessTable *>(wt);
193
+ } else {
194
+ // Equatable conformance (or no conformance) found
195
+ return nullptr ;
152
196
}
153
-
154
- const HashableWitnessTable *expectedWT = nullptr ;
155
- const HashableWitnessTable *wt =
156
- reinterpret_cast <const HashableWitnessTable *>(
157
- swift_conformsToProtocolCommon (type, &HashableProtocolDescriptor));
158
- hashableConformance.compare_exchange_strong (
159
- expectedWT, wt ? wt : reinterpret_cast <const HashableWitnessTable *>(1 ),
160
- std::memory_order_acq_rel);
161
- return wt;
162
197
}
163
198
164
- const EquatableWitnessTable *
199
+ const equatable_support:: EquatableWitnessTable *
165
200
SwiftValueHeader::getEquatableConformance () const {
166
- const EquatableWitnessTable *wt =
167
- reinterpret_cast <const EquatableWitnessTable *>(
168
- swift_conformsToProtocolCommon (type, &EquatableProtocolDescriptor));
169
- return wt;
201
+ const void * wt = cachedConformance.load (std::memory_order_acquire);
202
+ if (wt == nullptr ) {
203
+ wt = cacheHashableEquatableConformance ();
204
+ }
205
+ if ((reinterpret_cast <uintptr_t >(wt) & 1 ) == 0 ) {
206
+ // Hashable conformance found
207
+ return nullptr ;
208
+ } else {
209
+ // Equatable conformance (or no conformance) found
210
+ return reinterpret_cast <const equatable_support::EquatableWitnessTable *>(reinterpret_cast <uintptr_t >(wt) & ~1ULL );
211
+ }
170
212
}
171
213
172
214
static constexpr const size_t SwiftValueHeaderOffset
@@ -353,16 +395,12 @@ - (BOOL)isEqual:(id)other {
353
395
auto selfHeader = getSwiftValueHeader (self);
354
396
auto otherHeader = getSwiftValueHeader (other);
355
397
356
- // TODO: getHashableBaseType seems to always succeed,
357
- // even if the type is in fact not Hashable. Why?
358
- // Maybe we should try getting the conformance first,
359
- // since that seems to actually fail?
360
- auto selfHashableBaseType = selfHeader->getHashableBaseType ();
361
- if (selfHashableBaseType) {
362
- auto otherHashableBaseType = otherHeader->getHashableBaseType ();
363
- if (selfHashableBaseType == otherHashableBaseType) {
364
- auto hashableConformance = selfHeader->getHashableConformance ();
365
- if (hashableConformance) {
398
+ auto hashableConformance = selfHeader->getHashableConformance ();
399
+ if (hashableConformance) {
400
+ auto selfHashableBaseType = selfHeader->getHashableBaseType ();
401
+ if (selfHashableBaseType) {
402
+ auto otherHashableBaseType = otherHeader->getHashableBaseType ();
403
+ if (selfHashableBaseType == otherHashableBaseType) {
366
404
return _swift_stdlib_Hashable_isEqual_indirect (
367
405
getSwiftValuePayload (self,
368
406
getSwiftValuePayloadAlignMask (selfHeader->type )),
@@ -373,14 +411,12 @@ - (BOOL)isEqual:(id)other {
373
411
}
374
412
}
375
413
376
- // TODO: As above, getEquatableBaseType seems to always succeed
377
- // even if the type is not in fact Equatable.
378
- auto selfEquatableBaseType = selfHeader->getEquatableBaseType ();
379
- if (selfEquatableBaseType) {
380
- auto otherEquatableBaseType = otherHeader->getEquatableBaseType ();
381
- if (selfEquatableBaseType == otherEquatableBaseType) {
382
- auto equatableConformance = selfHeader->getEquatableConformance ();
383
- if (equatableConformance) {
414
+ auto equatableConformance = selfHeader->getEquatableConformance ();
415
+ if (equatableConformance) {
416
+ auto selfEquatableBaseType = selfHeader->getEquatableBaseType ();
417
+ if (selfEquatableBaseType) {
418
+ auto otherEquatableBaseType = otherHeader->getEquatableBaseType ();
419
+ if (selfEquatableBaseType == otherEquatableBaseType) {
384
420
return _swift_stdlib_Equatable_isEqual_indirect (
385
421
getSwiftValuePayload (self,
386
422
getSwiftValuePayloadAlignMask (selfHeader->type )),
@@ -391,6 +427,7 @@ - (BOOL)isEqual:(id)other {
391
427
}
392
428
}
393
429
430
+ // Not Equatable, not Hashable, and not the same box
394
431
return NO ;
395
432
}
396
433
0 commit comments