Like NSObject, CFType has primitive operations CFEqual and CFHash,
so Swift should allow those types to show up in Hashable positions
(like dictionaries). The most general way to do this was to
introduce a new protocol, _CFObject, and then have the importer
automatically make all CF types conform to it.
This did require one additional change: the == implementation that
calls through to CFEqual is in a new CoreFoundation overlay, but the
conformance is in the underlying Clang module. Therefore, operator
lookup for conformances has been changed to look in the overlay for
an imported declaration (if there is one).
This re-applies 361ab62454, reverted in
f50b1e73dc, after a /very/ long interval
where we decided if it was worth breaking people who've added these
conformances on their own. Since the workaround isn't too difficult---
use `#if swift(>=3.2)` to guard the extension introducing the
conformance---it was deemed acceptable.
https://bugs.swift.org/browse/SR-2388
Swift value types are their bridged Objective-C classes can have
different hash values. To address this, AnyHashable's responds to the
_HasCustomAnyHashableRepresentation protocol, which bridge objects of
those class types---NSString, NSNumber, etc---into their Swift
counterparts. That way, we get consistent (Swift) hashing behavior
across platforms.
However, there are cases where multiple Swift value types map to the
same Objective-C class type. In such cases, AnyHashable ends up
converting the object of class type back to some canonical type. For
example, an NS_STRING_ENUM (such as (NS)RunLoopMode) is a Swift
wrapper around a String. If an (NS)RunLoopMode is placed into an
AnyHashable, it maintains it's Swift type identity (which is correct
behavior). If it is bridged to Objective-C, it becomes an NSString; if
that NSString is placed into an AnyHashable, it produces a String. The
hash values still line up, but equality of the AnyHashable values
fails, which breaks when (for example) a dictionary with AnyHashable
keys is used from Objective-C. See SR-2648 / rdar://problem/27992351
for a case where this breaks interoperability.
To address this problem, make AnyHashable's casting and equality
sensitive to the origin of the hashed value: if the AnyHashable was
created through a _HasCustomAnyHashableRepresentation conformance,
treat comparisons/casting from it as "fuzzy":
* For equality, if one of the AnyHashable's comes from a custom
representation (e.g., it originated with an Objective-C type like
NSString) but the other did not, bridge the value of the *other*
AnyHashable to Objective-C, re-wrap it in an AnyHashable, and
compare that. This allows, e.g., an (NS)RunLoopMode created in Swift
to compare to an NSString constant with the same string value.
* For casting, if the AnyHashable we're casting from came from a
custom representation and the cast would fail, bridge to Objective-C
and then initiate the cast again. This allows an NSString to be
casted to (NS)RunLoopMode.
Fixes SR-2648 / rdar://problem/27992351.
Like NSObject, CFType has primitive operations CFEqual and CFHash,
so Swift should allow those types to show up in Hashable positions
(like dictionaries). The most general way to do this was to
introduce a new protocol, _CFObject, and then have the importer
automatically make all CF types conform to it.
This did require one additional change: the == implementation that
calls through to CFEqual is in a new CoreFoundation overlay, but the
conformance is in the underlying Clang module. Therefore, operator
lookup for conformances has been changed to look in the overlay for
an imported declaration (if there is one).
https://bugs.swift.org/browse/SR-2388
Previously AnyHashable would consider SwiftValue to be a subclass of
NSObject (which it is in practice) and return false when trying to
compare an AnyHashable of a SwiftValue box to an AnyHashable of the
unboxed value.
If the Swift error wrapped in a _SwiftNativeNSError box conforms to
Hashable, the box now uses the Swift's conformance to Hashable.
Part of rdar://problem/27574348.