@@ -41,7 +41,11 @@ internal protocol _AnyHashableBox {
41
41
var _typeID : ObjectIdentifier { get }
42
42
func _unbox< T : Hashable > ( ) -> T ?
43
43
44
- func _isEqual( to: _AnyHashableBox ) -> Bool
44
+ /// Determine whether values in the boxes are equivalent.
45
+ ///
46
+ /// - Returns: `nil` to indicate that the boxes store different types, so
47
+ /// no comparison is possible. Otherwise, contains the result of `==`.
48
+ func _isEqual( to: _AnyHashableBox ) -> Bool ?
45
49
var _hashValue : Int { get }
46
50
47
51
var _base : Any { get }
@@ -62,11 +66,11 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
62
66
return ( self as _AnyHashableBox as? _ConcreteHashableBox < T > ) ? . _baseHashable
63
67
}
64
68
65
- internal func _isEqual( to rhs: _AnyHashableBox ) -> Bool {
69
+ internal func _isEqual( to rhs: _AnyHashableBox ) -> Bool ? {
66
70
if let rhs: Base = rhs. _unbox ( ) {
67
71
return _baseHashable == rhs
68
72
}
69
- return false
73
+ return nil
70
74
}
71
75
72
76
internal var _hashValue : Int {
@@ -85,6 +89,18 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
85
89
}
86
90
}
87
91
92
+ #if _runtime(_ObjC)
93
+ // Retrieve the custom AnyHashable representation of the value after it
94
+ // has been bridged to Objective-C. This mapping to Objective-C and back
95
+ // turns a non-custom representation into a custom one, which is used as
96
+ // the lowest-common-denominator for comparisons.
97
+ func _getBridgedCustomAnyHashable< T> ( _ value: T ) -> AnyHashable ? {
98
+ let bridgedValue = _bridgeAnythingToObjectiveC ( value)
99
+ return ( bridgedValue as?
100
+ _HasCustomAnyHashableRepresentation ) ? . _toCustomAnyHashable ( )
101
+ }
102
+ #endif
103
+
88
104
/// A type-erased hashable value.
89
105
///
90
106
/// The `AnyHashable` type forwards equality comparisons and hashing operations
@@ -106,6 +122,7 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
106
122
/// print(descriptions[AnyHashable(Set(["a", "b"]))]!) // prints "a set of strings"
107
123
public struct AnyHashable {
108
124
internal var _box : _AnyHashableBox
125
+ internal var _usedCustomRepresentation : Bool
109
126
110
127
/// Creates a type-erased hashable value that wraps the given instance.
111
128
///
@@ -129,17 +146,20 @@ public struct AnyHashable {
129
146
if let customRepresentation =
130
147
( base as? _HasCustomAnyHashableRepresentation ) ? . _toCustomAnyHashable ( ) {
131
148
self = customRepresentation
149
+ self . _usedCustomRepresentation = true
132
150
return
133
151
}
134
152
135
153
self . _box = _ConcreteHashableBox ( 0 as Int )
154
+ self . _usedCustomRepresentation = false
136
155
_stdlib_makeAnyHashableUpcastingToHashableBaseType (
137
156
base,
138
157
storingResultInto: & self )
139
158
}
140
159
141
160
internal init < H : Hashable > ( _usingDefaultRepresentationOf base: H ) {
142
161
self . _box = _ConcreteHashableBox ( base)
162
+ self . _usedCustomRepresentation = false
143
163
}
144
164
145
165
/// The value wrapped by this instance.
@@ -162,7 +182,21 @@ public struct AnyHashable {
162
182
/// a downcast on `base`.
163
183
internal
164
184
func _downCastConditional< T> ( into result: UnsafeMutablePointer < T > ) -> Bool {
165
- return _box. _downCastConditional ( into: result)
185
+ // Attempt the downcast.
186
+ if _box. _downCastConditional ( into: result) { return true }
187
+
188
+ #if _runtime(_ObjC)
189
+ // If we used a custom representation, bridge to Objective-C and then
190
+ // attempt the cast from there.
191
+ if _usedCustomRepresentation {
192
+ if let value = _bridgeAnythingToObjectiveC ( _box. _base) as? T {
193
+ result. initialize ( to: value)
194
+ return true
195
+ }
196
+ }
197
+ #endif
198
+
199
+ return false
166
200
}
167
201
}
168
202
@@ -193,7 +227,34 @@ extension AnyHashable : Equatable {
193
227
/// - lhs: A type-erased hashable value.
194
228
/// - rhs: Another type-erased hashable value.
195
229
public static func == ( lhs: AnyHashable , rhs: AnyHashable ) -> Bool {
196
- return lhs. _box. _isEqual ( to: rhs. _box)
230
+ // If they're equal, we're done.
231
+ if let result = lhs. _box. _isEqual ( to: rhs. _box) { return result }
232
+
233
+ #if _runtime(_ObjC)
234
+ // If one used a custom representation but the other did not, bridge
235
+ // the one that did *not* use the custom representation to Objective-C:
236
+ // if the bridged result has a custom representation, compare those custom
237
+ // custom representations.
238
+ if lhs. _usedCustomRepresentation != rhs. _usedCustomRepresentation {
239
+ // If the lhs used a custom representation, try comparing against the
240
+ // custom representation of the bridged rhs (if there is one).
241
+ if lhs. _usedCustomRepresentation {
242
+ if let customRHS = _getBridgedCustomAnyHashable ( rhs. _box. _base) {
243
+ return lhs. _box. _isEqual ( to: customRHS. _box) ?? false
244
+ }
245
+ return false
246
+ }
247
+
248
+ // Otherwise, try comparing the rhs against the custom representation of
249
+ // the bridged lhs (if there is one).
250
+ if let customLHS = _getBridgedCustomAnyHashable ( lhs. _box. _base) {
251
+ return customLHS. _box. _isEqual ( to: rhs. _box) ?? false
252
+ }
253
+ return false
254
+ }
255
+ #endif
256
+
257
+ return false
197
258
}
198
259
}
199
260
0 commit comments