@@ -41,7 +41,11 @@ internal protocol _AnyHashableBox {
4141 var _typeID : ObjectIdentifier { get }
4242 func _unbox< T : Hashable > ( ) -> T ?
4343
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 ?
4549 var _hashValue : Int { get }
4650
4751 var _base : Any { get }
@@ -62,11 +66,11 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
6266 return ( self as _AnyHashableBox as? _ConcreteHashableBox < T > ) ? . _baseHashable
6367 }
6468
65- internal func _isEqual( to rhs: _AnyHashableBox ) -> Bool {
69+ internal func _isEqual( to rhs: _AnyHashableBox ) -> Bool ? {
6670 if let rhs: Base = rhs. _unbox ( ) {
6771 return _baseHashable == rhs
6872 }
69- return false
73+ return nil
7074 }
7175
7276 internal var _hashValue : Int {
@@ -85,6 +89,18 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
8589 }
8690}
8791
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+
88104/// A type-erased hashable value.
89105///
90106/// The `AnyHashable` type forwards equality comparisons and hashing operations
@@ -106,6 +122,7 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
106122/// print(descriptions[AnyHashable(Set(["a", "b"]))]!) // prints "a set of strings"
107123public struct AnyHashable {
108124 internal var _box : _AnyHashableBox
125+ internal var _usedCustomRepresentation : Bool
109126
110127 /// Creates a type-erased hashable value that wraps the given instance.
111128 ///
@@ -129,17 +146,20 @@ public struct AnyHashable {
129146 if let customRepresentation =
130147 ( base as? _HasCustomAnyHashableRepresentation ) ? . _toCustomAnyHashable ( ) {
131148 self = customRepresentation
149+ self . _usedCustomRepresentation = true
132150 return
133151 }
134152
135153 self . _box = _ConcreteHashableBox ( 0 as Int )
154+ self . _usedCustomRepresentation = false
136155 _stdlib_makeAnyHashableUpcastingToHashableBaseType (
137156 base,
138157 storingResultInto: & self )
139158 }
140159
141160 internal init < H : Hashable > ( _usingDefaultRepresentationOf base: H ) {
142161 self . _box = _ConcreteHashableBox ( base)
162+ self . _usedCustomRepresentation = false
143163 }
144164
145165 /// The value wrapped by this instance.
@@ -162,7 +182,21 @@ public struct AnyHashable {
162182 /// a downcast on `base`.
163183 internal
164184 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
166200 }
167201}
168202
@@ -193,7 +227,34 @@ extension AnyHashable : Equatable {
193227 /// - lhs: A type-erased hashable value.
194228 /// - rhs: Another type-erased hashable value.
195229 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
197258 }
198259}
199260
0 commit comments