mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #17396 from lorentey/anyhashable-is-not-hashable
[stdlib] Fix AnyHashable's Equatable/Hashable conformance
This commit is contained in:
@@ -2410,6 +2410,33 @@ internal func hash<H: Hashable>(_ value: H, seed: Int? = nil) -> Int {
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
/// Test that the elements of `groups` consist of instances that satisfy the
|
||||
/// semantic requirements of `Hashable`, with each group defining a distinct
|
||||
/// equivalence class under `==`.
|
||||
public func checkHashableGroups<Groups: Collection>(
|
||||
_ groups: Groups,
|
||||
_ message: @autoclosure () -> String = "",
|
||||
stackTrace: SourceLocStack = SourceLocStack(),
|
||||
showFrame: Bool = true,
|
||||
file: String = #file, line: UInt = #line
|
||||
) where Groups.Element: Collection, Groups.Element.Element: Hashable {
|
||||
let instances = groups.flatMap { $0 }
|
||||
// groupIndices[i] is the index of the element in groups that contains
|
||||
// instances[i].
|
||||
let groupIndices =
|
||||
zip(0..., groups).flatMap { i, group in group.map { _ in i } }
|
||||
func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
|
||||
return groupIndices[lhs] == groupIndices[rhs]
|
||||
}
|
||||
checkHashable(
|
||||
instances,
|
||||
equalityOracle: equalityOracle,
|
||||
hashEqualityOracle: equalityOracle,
|
||||
allowBrokenTransitivity: false,
|
||||
stackTrace: stackTrace.pushIf(showFrame, file: file, line: line),
|
||||
showFrame: false)
|
||||
}
|
||||
|
||||
/// Test that the elements of `instances` satisfy the semantic requirements of
|
||||
/// `Hashable`, using `equalityOracle` to generate equality and hashing
|
||||
/// expectations from pairs of positions in `instances`.
|
||||
|
||||
@@ -39,20 +39,29 @@ public protocol _HasCustomAnyHashableRepresentation {
|
||||
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
internal protocol _AnyHashableBox {
|
||||
func _unbox<T : Hashable>() -> T?
|
||||
var _canonicalBox: _AnyHashableBox { get }
|
||||
|
||||
/// Determine whether values in the boxes are equivalent.
|
||||
///
|
||||
/// - Precondition: `self` and `box` are in canonical form.
|
||||
/// - Returns: `nil` to indicate that the boxes store different types, so
|
||||
/// no comparison is possible. Otherwise, contains the result of `==`.
|
||||
func _isEqual(to: _AnyHashableBox) -> Bool?
|
||||
func _isEqual(to box: _AnyHashableBox) -> Bool?
|
||||
var _hashValue: Int { get }
|
||||
func _hash(into hasher: inout Hasher)
|
||||
func _rawHashValue(_seed: (UInt64, UInt64)) -> Int
|
||||
|
||||
var _base: Any { get }
|
||||
func _unbox<T: Hashable>() -> T?
|
||||
func _downCastConditional<T>(into result: UnsafeMutablePointer<T>) -> Bool
|
||||
}
|
||||
|
||||
extension _AnyHashableBox {
|
||||
var _canonicalBox: _AnyHashableBox {
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
||||
@_fixed_layout // FIXME(sil-serialize-all)
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
|
||||
@@ -87,6 +96,11 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
|
||||
_baseHashable.hash(into: &hasher)
|
||||
}
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
|
||||
return _baseHashable._rawHashValue(seed: _seed)
|
||||
}
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal var _base: Any {
|
||||
return _baseHashable
|
||||
@@ -101,19 +115,6 @@ internal struct _ConcreteHashableBox<Base : Hashable> : _AnyHashableBox {
|
||||
}
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
// Retrieve the custom AnyHashable representation of the value after it
|
||||
// has been bridged to Objective-C. This mapping to Objective-C and back
|
||||
// turns a non-custom representation into a custom one, which is used as
|
||||
// the lowest-common-denominator for comparisons.
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal func _getBridgedCustomAnyHashable<T>(_ value: T) -> AnyHashable? {
|
||||
let bridgedValue = _bridgeAnythingToObjectiveC(value)
|
||||
return (bridgedValue as?
|
||||
_HasCustomAnyHashableRepresentation)?._toCustomAnyHashable()
|
||||
}
|
||||
#endif
|
||||
|
||||
/// A type-erased hashable value.
|
||||
///
|
||||
/// The `AnyHashable` type forwards equality comparisons and hashing operations
|
||||
@@ -137,8 +138,11 @@ internal func _getBridgedCustomAnyHashable<T>(_ value: T) -> AnyHashable? {
|
||||
public struct AnyHashable {
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
internal var _box: _AnyHashableBox
|
||||
@usableFromInline // FIXME(sil-serialize-all)
|
||||
internal var _usedCustomRepresentation: Bool
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal init(_box box: _AnyHashableBox) {
|
||||
self._box = box
|
||||
}
|
||||
|
||||
/// Creates a type-erased hashable value that wraps the given instance.
|
||||
///
|
||||
@@ -160,15 +164,13 @@ public struct AnyHashable {
|
||||
/// - Parameter base: A hashable value to wrap.
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
public init<H : Hashable>(_ base: H) {
|
||||
if let customRepresentation =
|
||||
if let custom =
|
||||
(base as? _HasCustomAnyHashableRepresentation)?._toCustomAnyHashable() {
|
||||
self = customRepresentation
|
||||
self._usedCustomRepresentation = true
|
||||
self = custom
|
||||
return
|
||||
}
|
||||
|
||||
self._box = _ConcreteHashableBox(0 as Int)
|
||||
self._usedCustomRepresentation = false
|
||||
self.init(_box: _ConcreteHashableBox(false)) // Dummy value
|
||||
_makeAnyHashableUpcastingToHashableBaseType(
|
||||
base,
|
||||
storingResultInto: &self)
|
||||
@@ -177,7 +179,6 @@ public struct AnyHashable {
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal init<H : Hashable>(_usingDefaultRepresentationOf base: H) {
|
||||
self._box = _ConcreteHashableBox(base)
|
||||
self._usedCustomRepresentation = false
|
||||
}
|
||||
|
||||
/// The value wrapped by this instance.
|
||||
@@ -206,14 +207,12 @@ public struct AnyHashable {
|
||||
if _box._downCastConditional(into: result) { return true }
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
// If we used a custom representation, bridge to Objective-C and then
|
||||
// attempt the cast from there.
|
||||
if _usedCustomRepresentation {
|
||||
// Bridge to Objective-C and then attempt the cast from there.
|
||||
// FIXME: This should also work without the Objective-C runtime.
|
||||
if let value = _bridgeAnythingToObjectiveC(_box._base) as? T {
|
||||
result.initialize(to: value)
|
||||
return true
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return false
|
||||
@@ -248,34 +247,7 @@ extension AnyHashable : Equatable {
|
||||
/// - rhs: Another type-erased hashable value.
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
public static func == (lhs: AnyHashable, rhs: AnyHashable) -> Bool {
|
||||
// If they're equal, we're done.
|
||||
if let result = lhs._box._isEqual(to: rhs._box) { return result }
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
// If one used a custom representation but the other did not, bridge
|
||||
// the one that did *not* use the custom representation to Objective-C:
|
||||
// if the bridged result has a custom representation, compare those custom
|
||||
// custom representations.
|
||||
if lhs._usedCustomRepresentation != rhs._usedCustomRepresentation {
|
||||
// If the lhs used a custom representation, try comparing against the
|
||||
// custom representation of the bridged rhs (if there is one).
|
||||
if lhs._usedCustomRepresentation {
|
||||
if let customRHS = _getBridgedCustomAnyHashable(rhs._box._base) {
|
||||
return lhs._box._isEqual(to: customRHS._box) ?? false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Otherwise, try comparing the rhs against the custom representation of
|
||||
// the bridged lhs (if there is one).
|
||||
if let customLHS = _getBridgedCustomAnyHashable(lhs._box._base) {
|
||||
return customLHS._box._isEqual(to: rhs._box) ?? false
|
||||
}
|
||||
return false
|
||||
}
|
||||
#endif
|
||||
|
||||
return false
|
||||
return lhs._box._canonicalBox._isEqual(to: rhs._box._canonicalBox) ?? false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +255,7 @@ extension AnyHashable : Hashable {
|
||||
/// The hash value.
|
||||
@inlinable
|
||||
public var hashValue: Int {
|
||||
return _box._hashValue
|
||||
return _box._canonicalBox._hashValue
|
||||
}
|
||||
|
||||
/// Hashes the essential components of this value by feeding them into the
|
||||
@@ -293,7 +265,12 @@ extension AnyHashable : Hashable {
|
||||
/// of this instance.
|
||||
@inlinable
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
_box._hash(into: &hasher)
|
||||
_box._canonicalBox._hash(into: &hasher)
|
||||
}
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
public func _rawHashValue(seed: (UInt64, UInt64)) -> Int {
|
||||
return _box._canonicalBox._rawHashValue(_seed: seed)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1758,3 +1758,76 @@ extension Array {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
extension Array: _HasCustomAnyHashableRepresentation
|
||||
where Element: Hashable {
|
||||
public func _toCustomAnyHashable() -> AnyHashable? {
|
||||
return AnyHashable(_box: _ArrayAnyHashableBox(self))
|
||||
}
|
||||
}
|
||||
|
||||
internal protocol _ArrayAnyHashableProtocol: _AnyHashableBox {
|
||||
var count: Int { get }
|
||||
subscript(index: Int) -> AnyHashable { get }
|
||||
}
|
||||
|
||||
internal struct _ArrayAnyHashableBox<Element: Hashable>
|
||||
: _ArrayAnyHashableProtocol {
|
||||
internal let _value: [Element]
|
||||
|
||||
internal init(_ value: [Element]) {
|
||||
self._value = value
|
||||
}
|
||||
|
||||
internal var _base: Any {
|
||||
return _value
|
||||
}
|
||||
|
||||
internal var count: Int {
|
||||
return _value.count
|
||||
}
|
||||
|
||||
internal subscript(index: Int) -> AnyHashable {
|
||||
return _value[index] as AnyHashable
|
||||
}
|
||||
|
||||
func _isEqual(to other: _AnyHashableBox) -> Bool? {
|
||||
guard let other = other as? _ArrayAnyHashableProtocol else { return nil }
|
||||
guard _value.count == other.count else { return false }
|
||||
for i in 0 ..< _value.count {
|
||||
if self[i] != other[i] { return false }
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
var _hashValue: Int {
|
||||
var hasher = Hasher()
|
||||
_hash(into: &hasher)
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
func _hash(into hasher: inout Hasher) {
|
||||
hasher.combine(_value.count) // discriminator
|
||||
for i in 0 ..< _value.count {
|
||||
hasher.combine(self[i])
|
||||
}
|
||||
}
|
||||
|
||||
func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
|
||||
var hasher = Hasher(_seed: _seed)
|
||||
self._hash(into: &hasher)
|
||||
return hasher._finalize()
|
||||
}
|
||||
|
||||
internal func _unbox<T : Hashable>() -> T? {
|
||||
return _value as? T
|
||||
}
|
||||
|
||||
internal func _downCastConditional<T>(
|
||||
into result: UnsafeMutablePointer<T>
|
||||
) -> Bool {
|
||||
guard let value = _value as? T else { return false }
|
||||
result.initialize(to: value)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1471,6 +1471,65 @@ extension Dictionary: Hashable where Value: Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary: _HasCustomAnyHashableRepresentation
|
||||
where Value: Hashable {
|
||||
public func _toCustomAnyHashable() -> AnyHashable? {
|
||||
return AnyHashable(_box: _DictionaryAnyHashableBox(self))
|
||||
}
|
||||
}
|
||||
|
||||
internal struct _DictionaryAnyHashableBox<Key: Hashable, Value: Hashable>
|
||||
: _AnyHashableBox {
|
||||
internal let _value: Dictionary<Key, Value>
|
||||
internal let _canonical: Dictionary<AnyHashable, AnyHashable>
|
||||
|
||||
internal init(_ value: Dictionary<Key, Value>) {
|
||||
self._value = value
|
||||
self._canonical = value as Dictionary<AnyHashable, AnyHashable>
|
||||
}
|
||||
|
||||
internal var _base: Any {
|
||||
return _value
|
||||
}
|
||||
|
||||
internal var _canonicalBox: _AnyHashableBox {
|
||||
return _DictionaryAnyHashableBox<AnyHashable, AnyHashable>(_canonical)
|
||||
}
|
||||
|
||||
internal func _isEqual(to other: _AnyHashableBox) -> Bool? {
|
||||
guard
|
||||
let other = other as? _DictionaryAnyHashableBox<AnyHashable, AnyHashable>
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
return _canonical == other._value
|
||||
}
|
||||
|
||||
internal var _hashValue: Int {
|
||||
return _canonical.hashValue
|
||||
}
|
||||
|
||||
internal func _hash(into hasher: inout Hasher) {
|
||||
_canonical.hash(into: &hasher)
|
||||
}
|
||||
|
||||
internal func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
|
||||
return _canonical._rawHashValue(seed: _seed)
|
||||
}
|
||||
|
||||
internal func _unbox<T: Hashable>() -> T? {
|
||||
return _value as? T
|
||||
}
|
||||
|
||||
internal func _downCastConditional<T>(
|
||||
into result: UnsafeMutablePointer<T>
|
||||
) -> Bool {
|
||||
guard let value = _value as? T else { return false }
|
||||
result.initialize(to: value)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary: CustomStringConvertible, CustomDebugStringConvertible {
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
internal func _makeDescription() -> String {
|
||||
|
||||
@@ -1559,6 +1559,13 @@ extension ${Self} : Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
extension ${Self}: _HasCustomAnyHashableRepresentation {
|
||||
// Not @inlinable
|
||||
public func _toCustomAnyHashable() -> AnyHashable? {
|
||||
return AnyHashable(_box: _${Self}AnyHashableBox(self))
|
||||
}
|
||||
}
|
||||
|
||||
extension ${Self} {
|
||||
/// The magnitude of this value.
|
||||
///
|
||||
@@ -1809,6 +1816,87 @@ extension ${Self} : Strideable {
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// AnyHashable
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
internal struct _${Self}AnyHashableBox: _AnyHashableBox {
|
||||
internal typealias Base = ${Self}
|
||||
internal let _value: Base
|
||||
|
||||
internal init(_ value: Base) {
|
||||
self._value = value
|
||||
}
|
||||
|
||||
internal var _canonicalBox: _AnyHashableBox {
|
||||
// Float and Double are bridged with NSNumber, so we have to follow
|
||||
// NSNumber's rules for equality. I.e., we need to make sure equal
|
||||
// numerical values end up in identical boxes after canonicalization, so
|
||||
// that _isEqual will consider them equal and they're hashed the same way.
|
||||
//
|
||||
// Note that these AnyHashable boxes don't currently feed discriminator bits
|
||||
// to the hasher, so we allow repeatable collisions. E.g., -1 will always
|
||||
// collide with UInt64.max.
|
||||
if _value < 0 {
|
||||
if let i = Int64(exactly: _value) {
|
||||
return _IntegerAnyHashableBox(i)
|
||||
}
|
||||
} else {
|
||||
if let i = UInt64(exactly: _value) {
|
||||
return _IntegerAnyHashableBox(i)
|
||||
}
|
||||
}
|
||||
if let d = Double(exactly: _value) {
|
||||
return _DoubleAnyHashableBox(d)
|
||||
}
|
||||
// If a value can't be represented by a Double, keep it in its original
|
||||
// representation so that it won't compare equal to approximations. (So that
|
||||
// we don't round off Float80 values.)
|
||||
return self
|
||||
}
|
||||
|
||||
internal func _isEqual(to box: _AnyHashableBox) -> Bool? {
|
||||
_sanityCheck(Int64(exactly: _value) == nil, "self isn't canonical")
|
||||
_sanityCheck(UInt64(exactly: _value) == nil, "self isn't canonical")
|
||||
if let box = box as? _${Self}AnyHashableBox {
|
||||
return _value == box._value
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
internal var _hashValue: Int {
|
||||
return _rawHashValue(_seed: Hasher._seed)
|
||||
}
|
||||
|
||||
internal func _hash(into hasher: inout Hasher) {
|
||||
_sanityCheck(Int64(exactly: _value) == nil, "self isn't canonical")
|
||||
_sanityCheck(UInt64(exactly: _value) == nil, "self isn't canonical")
|
||||
hasher.combine(_value)
|
||||
}
|
||||
|
||||
internal func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
|
||||
var hasher = Hasher(_seed: _seed)
|
||||
_hash(into: &hasher)
|
||||
return hasher.finalize()
|
||||
}
|
||||
|
||||
internal var _base: Any {
|
||||
return _value
|
||||
}
|
||||
|
||||
internal func _unbox<T: Hashable>() -> T? {
|
||||
return _value as? T
|
||||
}
|
||||
|
||||
internal func _downCastConditional<T>(
|
||||
into result: UnsafeMutablePointer<T>
|
||||
) -> Bool {
|
||||
guard let value = _value as? T else { return false }
|
||||
result.initialize(to: value)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Deprecated operators
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -437,6 +437,7 @@ public struct Hasher {
|
||||
seed: (UInt64, UInt64),
|
||||
bytes value: UInt64,
|
||||
count: Int) -> Int {
|
||||
_sanityCheck(count >= 0 && count < 8)
|
||||
var core = RawCore(seed: seed)
|
||||
let tbc = _HasherTailBuffer(tail: value, byteCount: count)
|
||||
return Int(truncatingIfNeeded: core.finalize(tailAndByteCount: tbc.value))
|
||||
|
||||
@@ -3880,41 +3880,31 @@ extension ${Self} : Hashable {
|
||||
/// of this instance.
|
||||
@inlinable
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
// FIXME(hasher): To correctly bridge `Set`s/`Dictionary`s containing
|
||||
// `AnyHashable`-boxed integers, all integer values are currently required
|
||||
// to hash exactly the same way as the corresponding (U)Int64 value. To fix
|
||||
// this, we should introduce a custom AnyHashable box for integer values
|
||||
// that sign-extends values to 64 bits.
|
||||
% if bits <= word_bits:
|
||||
hasher._combine(_lowWord)
|
||||
% elif bits == 2 * word_bits:
|
||||
if let word = ${"" if signed else "U"}Int(exactly: self) {
|
||||
hasher._combine(word._lowWord)
|
||||
} else {
|
||||
hasher._combine(UInt64(_value))
|
||||
}
|
||||
% else:
|
||||
fatalError("Unsupported integer width")
|
||||
% end
|
||||
hasher._combine(${U}${Self}(_value))
|
||||
}
|
||||
|
||||
@inlinable
|
||||
public func _rawHashValue(seed: (UInt64, UInt64)) -> Int {
|
||||
// FIXME(hasher): Note that the AnyHashable concern applies here too,
|
||||
// because hashValue uses top-level hashing.
|
||||
% if bits <= word_bits:
|
||||
return Hasher._hash(seed: seed, _lowWord)
|
||||
% elif bits == 2 * word_bits:
|
||||
if let word = ${"" if signed else "U"}Int(exactly: self) {
|
||||
return Hasher._hash(seed: seed, word._lowWord)
|
||||
}
|
||||
% if bits == 64:
|
||||
return Hasher._hash(seed: seed, UInt64(_value))
|
||||
% elif bits == word_bits:
|
||||
return Hasher._hash(seed: seed, UInt(_value))
|
||||
% else:
|
||||
fatalError("Unsupported integer width")
|
||||
return Hasher._hash(
|
||||
seed: seed,
|
||||
bytes: UInt64(truncatingIfNeeded: ${U}${Self}(_value)),
|
||||
count: ${bits / 8})
|
||||
% end
|
||||
}
|
||||
}
|
||||
|
||||
extension ${Self} : _HasCustomAnyHashableRepresentation {
|
||||
// Not @inlinable
|
||||
public func _toCustomAnyHashable() -> AnyHashable? {
|
||||
return AnyHashable(_box: _IntegerAnyHashableBox(self))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create an ambiguity when indexing or slicing
|
||||
// Range[OfStrideable]<${Self}> outside a generic context. See
|
||||
@@ -4223,6 +4213,73 @@ extension SignedInteger where Self : FixedWidthInteger {
|
||||
% end
|
||||
}
|
||||
|
||||
internal struct _IntegerAnyHashableBox<
|
||||
Base: FixedWidthInteger
|
||||
>: _AnyHashableBox {
|
||||
internal let _value: Base
|
||||
|
||||
internal init(_ value: Base) {
|
||||
self._value = value
|
||||
}
|
||||
|
||||
internal var _canonicalBox: _AnyHashableBox {
|
||||
// We need to follow NSNumber semantics here; the AnyHashable forms of
|
||||
// integer types holding the same mathematical value should compare equal.
|
||||
// Sign-extend value to a 64-bit integer. This will generate hash conflicts
|
||||
// between, say -1 and UInt.max, but that's fine.
|
||||
if _value < 0 {
|
||||
return _IntegerAnyHashableBox<Int64>(Int64(truncatingIfNeeded: _value))
|
||||
}
|
||||
return _IntegerAnyHashableBox<UInt64>(UInt64(truncatingIfNeeded: _value))
|
||||
}
|
||||
|
||||
internal func _isEqual(to box: _AnyHashableBox) -> Bool? {
|
||||
if Base.self == UInt64.self {
|
||||
guard let box = box as? _IntegerAnyHashableBox<UInt64> else { return nil }
|
||||
return _value == box._value
|
||||
}
|
||||
if Base.self == Int64.self {
|
||||
guard let box = box as? _IntegerAnyHashableBox<Int64> else { return nil }
|
||||
return _value == box._value
|
||||
}
|
||||
_preconditionFailure("self isn't canonical")
|
||||
}
|
||||
|
||||
internal var _hashValue: Int {
|
||||
_sanityCheck(Base.self == UInt64.self || Base.self == Int64.self,
|
||||
"self isn't canonical")
|
||||
return _value.hashValue
|
||||
}
|
||||
|
||||
internal func _hash(into hasher: inout Hasher) {
|
||||
_sanityCheck(Base.self == UInt64.self || Base.self == Int64.self,
|
||||
"self isn't canonical")
|
||||
_value.hash(into: &hasher)
|
||||
}
|
||||
|
||||
internal func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
|
||||
_sanityCheck(Base.self == UInt64.self || Base.self == Int64.self,
|
||||
"self isn't canonical")
|
||||
return _value._rawHashValue(seed: _seed)
|
||||
}
|
||||
|
||||
internal var _base: Any {
|
||||
return _value
|
||||
}
|
||||
|
||||
internal func _unbox<T: Hashable>() -> T? {
|
||||
return _value as? T
|
||||
}
|
||||
|
||||
internal func _downCastConditional<T>(
|
||||
into result: UnsafeMutablePointer<T>
|
||||
) -> Bool {
|
||||
guard let value = _value as? T else { return false }
|
||||
result.initialize(to: value)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// ${'Local Variables'}:
|
||||
// eval: (read-only-mode 1)
|
||||
// End:
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
/// An implementation detail used to implement support importing
|
||||
/// (Objective-)C entities marked with the swift_newtype Clang
|
||||
/// attribute.
|
||||
public protocol _SwiftNewtypeWrapper : RawRepresentable { }
|
||||
public protocol _SwiftNewtypeWrapper
|
||||
: RawRepresentable, _HasCustomAnyHashableRepresentation { }
|
||||
|
||||
extension _SwiftNewtypeWrapper where Self: Hashable, Self.RawValue : Hashable {
|
||||
extension _SwiftNewtypeWrapper where Self: Hashable, Self.RawValue: Hashable {
|
||||
/// The hash value.
|
||||
@inlinable
|
||||
public var hashValue: Int {
|
||||
@@ -31,6 +32,70 @@ extension _SwiftNewtypeWrapper where Self: Hashable, Self.RawValue : Hashable {
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(rawValue)
|
||||
}
|
||||
|
||||
@inlinable // FIXME(sil-serialize-all)
|
||||
public func _rawHashValue(seed: (UInt64, UInt64)) -> Int {
|
||||
return rawValue._rawHashValue(seed: seed)
|
||||
}
|
||||
}
|
||||
|
||||
extension _SwiftNewtypeWrapper {
|
||||
public func _toCustomAnyHashable() -> AnyHashable? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension _SwiftNewtypeWrapper where Self: Hashable, Self.RawValue: Hashable {
|
||||
public func _toCustomAnyHashable() -> AnyHashable? {
|
||||
return AnyHashable(_box: _NewtypeWrapperAnyHashableBox(self))
|
||||
}
|
||||
}
|
||||
|
||||
internal struct _NewtypeWrapperAnyHashableBox<Base>: _AnyHashableBox
|
||||
where Base: _SwiftNewtypeWrapper & Hashable, Base.RawValue: Hashable {
|
||||
var _value: Base
|
||||
|
||||
init(_ value: Base) {
|
||||
self._value = value
|
||||
}
|
||||
|
||||
var _canonicalBox: _AnyHashableBox {
|
||||
return (_value.rawValue as AnyHashable)._box._canonicalBox
|
||||
}
|
||||
|
||||
func _isEqual(to other: _AnyHashableBox) -> Bool? {
|
||||
_preconditionFailure("_isEqual called on non-canonical AnyHashable box")
|
||||
}
|
||||
|
||||
var _hashValue: Int {
|
||||
_preconditionFailure("_hashValue called on non-canonical AnyHashable box")
|
||||
}
|
||||
|
||||
func _hash(into hasher: inout Hasher) {
|
||||
_preconditionFailure("_hash(into:) called on non-canonical AnyHashable box")
|
||||
}
|
||||
|
||||
func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
|
||||
_preconditionFailure("_rawHashValue(_seed:) called on non-canonical AnyHashable box")
|
||||
}
|
||||
|
||||
var _base: Any { return _value }
|
||||
|
||||
func _unbox<T: Hashable>() -> T? {
|
||||
return _value as? T ?? _value.rawValue as? T
|
||||
}
|
||||
|
||||
func _downCastConditional<T>(into result: UnsafeMutablePointer<T>) -> Bool {
|
||||
if let value = _value as? T {
|
||||
result.initialize(to: value)
|
||||
return true
|
||||
}
|
||||
if let value = _value.rawValue as? T {
|
||||
result.initialize(to: value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
|
||||
@@ -503,6 +503,61 @@ extension Set: Hashable {
|
||||
}
|
||||
}
|
||||
|
||||
extension Set: _HasCustomAnyHashableRepresentation {
|
||||
public func _toCustomAnyHashable() -> AnyHashable? {
|
||||
return AnyHashable(_box: _SetAnyHashableBox(self))
|
||||
}
|
||||
}
|
||||
|
||||
internal struct _SetAnyHashableBox<Element: Hashable>: _AnyHashableBox {
|
||||
internal let _value: Set<Element>
|
||||
internal let _canonical: Set<AnyHashable>
|
||||
|
||||
internal init(_ value: Set<Element>) {
|
||||
self._value = value
|
||||
self._canonical = value as Set<AnyHashable>
|
||||
}
|
||||
|
||||
internal var _base: Any {
|
||||
return _value
|
||||
}
|
||||
|
||||
internal var _canonicalBox: _AnyHashableBox {
|
||||
return _SetAnyHashableBox<AnyHashable>(_canonical)
|
||||
}
|
||||
|
||||
internal func _isEqual(to other: _AnyHashableBox) -> Bool? {
|
||||
guard let other = other as? _SetAnyHashableBox<AnyHashable> else {
|
||||
return nil
|
||||
}
|
||||
return _canonical == other._value
|
||||
}
|
||||
|
||||
internal var _hashValue: Int {
|
||||
return _canonical.hashValue
|
||||
}
|
||||
|
||||
internal func _hash(into hasher: inout Hasher) {
|
||||
_canonical.hash(into: &hasher)
|
||||
}
|
||||
|
||||
func _rawHashValue(_seed: (UInt64, UInt64)) -> Int {
|
||||
return _canonical._rawHashValue(seed: _seed)
|
||||
}
|
||||
|
||||
internal func _unbox<T: Hashable>() -> T? {
|
||||
return _value as? T
|
||||
}
|
||||
|
||||
internal func _downCastConditional<T>(
|
||||
into result: UnsafeMutablePointer<T>
|
||||
) -> Bool {
|
||||
guard let value = _value as? T else { return false }
|
||||
result.initialize(to: value)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension Set: SetAlgebra {
|
||||
|
||||
/// Inserts the given element in the set if it is not already present.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// RUN: %empty-directory(%t)
|
||||
//
|
||||
// RUN: %gyb %s -o %t/AnyHashableCasts.swift
|
||||
// RUN: %target-build-swift -g -module-name a %t/AnyHashableCasts.swift -o %t.out
|
||||
// RUN: %target-run %t.out
|
||||
// RUN: %target-build-swift -g -O -module-name a %t/AnyHashableCasts.swift -o %t.out.optimized
|
||||
// RUN: %target-run %t.out.optimized
|
||||
// RUN: %line-directive %t/AnyHashableCasts.swift -- %target-build-swift -g -module-name a %t/AnyHashableCasts.swift -o %t.out
|
||||
// RUN: %line-directive %t/AnyHashableCasts.swift -- %target-run %t.out
|
||||
// RUN: %line-directive %t/AnyHashableCasts.swift -- %target-build-swift -g -O -module-name a %t/AnyHashableCasts.swift -o %t.out.optimized
|
||||
// RUN: %line-directive %t/AnyHashableCasts.swift -- %target-run %t.out.optimized
|
||||
// REQUIRES: executable_test
|
||||
|
||||
import StdlibUnittest
|
||||
@@ -117,34 +117,129 @@ AnyHashableCasts.test("${valueExpr} as ${coercedType} as? ${castType}") {
|
||||
% end
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
// A wrapper type around Int that bridges to NSNumber.
|
||||
struct IntWrapper1: _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
let rawValue: Int
|
||||
}
|
||||
|
||||
// A wrapper type around Int that bridges to NSNumber.
|
||||
struct IntWrapper2: _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
let rawValue: Int
|
||||
}
|
||||
|
||||
AnyHashableCasts.test("Wrappers around bridged integers") {
|
||||
let wrapper1: AnyHashable = IntWrapper1(rawValue: 42)
|
||||
let wrapper2: AnyHashable = IntWrapper2(rawValue: 42)
|
||||
let integer: AnyHashable = 42 as Int
|
||||
let byte: AnyHashable = 42 as UInt8
|
||||
let double: AnyHashable = 42.0 as Double
|
||||
let number: AnyHashable = 42 as NSNumber
|
||||
|
||||
// Wrappers compare equal to their wrapped value as AnyHashable.
|
||||
expectEqual(wrapper1, wrapper2)
|
||||
expectEqual(wrapper1, integer)
|
||||
expectEqual(wrapper1, byte)
|
||||
expectEqual(wrapper1, double)
|
||||
expectEqual(wrapper1, number)
|
||||
|
||||
// Original types are preserved in the base property.
|
||||
expectTrue(wrapper1.base is IntWrapper1)
|
||||
expectTrue(wrapper2.base is IntWrapper2)
|
||||
expectTrue(integer.base is Int)
|
||||
expectTrue(byte.base is UInt8)
|
||||
expectTrue(double.base is Double)
|
||||
expectTrue(number.base is NSNumber) // Through bridging
|
||||
|
||||
// AnyHashable forms can be casted to any standard numeric type that can hold
|
||||
// their value.
|
||||
expectNotNil(wrapper1 as? IntWrapper1)
|
||||
expectNotNil(wrapper1 as? IntWrapper2)
|
||||
expectNotNil(wrapper1 as? Int)
|
||||
expectNotNil(wrapper1 as? UInt8)
|
||||
expectNotNil(wrapper1 as? Double)
|
||||
expectNotNil(wrapper1 as? NSNumber)
|
||||
|
||||
expectNotNil(byte as? IntWrapper1)
|
||||
expectNotNil(byte as? IntWrapper2)
|
||||
expectNotNil(byte as? Int)
|
||||
expectNotNil(byte as? UInt8)
|
||||
expectNotNil(byte as? Double)
|
||||
expectNotNil(byte as? NSNumber)
|
||||
|
||||
expectNotNil(integer as? IntWrapper1)
|
||||
expectNotNil(integer as? IntWrapper2)
|
||||
expectNotNil(integer as? Int)
|
||||
expectNotNil(integer as? UInt8)
|
||||
expectNotNil(integer as? Double)
|
||||
expectNotNil(integer as? NSNumber)
|
||||
|
||||
expectNotNil(double as? IntWrapper1)
|
||||
expectNotNil(double as? IntWrapper2)
|
||||
expectNotNil(double as? Int)
|
||||
expectNotNil(double as? UInt8)
|
||||
expectNotNil(double as? Double)
|
||||
expectNotNil(double as? NSNumber)
|
||||
|
||||
expectNotNil(number as? IntWrapper1)
|
||||
expectNotNil(number as? IntWrapper2)
|
||||
expectNotNil(number as? Int)
|
||||
expectNotNil(number as? UInt8)
|
||||
expectNotNil(number as? Double)
|
||||
expectNotNil(number as? NSNumber)
|
||||
|
||||
// We can't cast to a numeric type that can't hold the value.
|
||||
let big: AnyHashable = Int32.max
|
||||
expectNotNil(big as? IntWrapper1)
|
||||
expectNotNil(big as? IntWrapper2)
|
||||
expectNotNil(big as? Int)
|
||||
expectNil(big as? UInt8) // <--
|
||||
expectNotNil(big as? Double)
|
||||
expectNotNil(big as? NSNumber)
|
||||
}
|
||||
|
||||
// A wrapper type around a String that bridges to NSString.
|
||||
struct StringWrapper1 : _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
struct StringWrapper1: _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
let rawValue: String
|
||||
}
|
||||
|
||||
// A wrapper type around a String that bridges to NSString.
|
||||
struct StringWrapper2 : _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
struct StringWrapper2: _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
let rawValue: String
|
||||
}
|
||||
|
||||
AnyHashableCasts.test("Wrappers around bridged types") {
|
||||
AnyHashableCasts.test("Wrappers around bridged strings") {
|
||||
let wrapper1Hello: AnyHashable = StringWrapper1(rawValue: "hello")
|
||||
let wrapper2Hello: AnyHashable = StringWrapper2(rawValue: "hello")
|
||||
let stringHello: AnyHashable = "hello" as String
|
||||
let nsStringHello: AnyHashable = "hello" as NSString
|
||||
|
||||
// Casting from Swift wrapper maintains type identity
|
||||
// Wrappers compare equal to their wrapped value as AnyHashable.
|
||||
expectEqual(wrapper1Hello, wrapper2Hello)
|
||||
expectEqual(wrapper1Hello, stringHello)
|
||||
expectEqual(wrapper1Hello, nsStringHello)
|
||||
expectEqual(wrapper2Hello, stringHello)
|
||||
expectEqual(wrapper2Hello, nsStringHello)
|
||||
expectEqual(stringHello, nsStringHello)
|
||||
|
||||
// Type identity is maintained through the base property.
|
||||
expectTrue(wrapper1Hello.base is StringWrapper1)
|
||||
expectTrue(wrapper2Hello.base is StringWrapper2)
|
||||
expectTrue(stringHello.base is String)
|
||||
expectTrue(nsStringHello.base is NSString) // Through bridging
|
||||
|
||||
// Swift wrapper's AnyHashable form doesn't enfore type identity.
|
||||
expectNotNil(wrapper1Hello as? StringWrapper1)
|
||||
expectNil(wrapper1Hello as? StringWrapper2)
|
||||
expectNil(wrapper1Hello as? String)
|
||||
expectNotNil(wrapper1Hello as? StringWrapper2)
|
||||
expectNotNil(wrapper1Hello as? String)
|
||||
expectNotNil(wrapper1Hello as? NSString)
|
||||
|
||||
// Casting from String maintains type identity
|
||||
expectNil(stringHello as? StringWrapper1)
|
||||
expectNil(stringHello as? StringWrapper2)
|
||||
// String's AnyHashable form doesn't enfore type identity.
|
||||
expectNotNil(stringHello as? StringWrapper1)
|
||||
expectNotNil(stringHello as? StringWrapper2)
|
||||
expectNotNil(stringHello as? String)
|
||||
expectNotNil(stringHello as? NSString)
|
||||
|
||||
// Casting form NSString works with anything.
|
||||
// NSString's AnyHashable form doesn't enfore type identity.
|
||||
expectNotNil(nsStringHello as? StringWrapper1)
|
||||
expectNotNil(nsStringHello as? StringWrapper2)
|
||||
expectNotNil(nsStringHello as? String)
|
||||
|
||||
@@ -767,6 +767,132 @@ AnyHashableTests.test("AnyHashable(MinimalHashableRCSwiftError).base") {
|
||||
expectEqual(MinimalHashableRCSwiftError.self, type(of: ah.base))
|
||||
}
|
||||
|
||||
AnyHashableTests.test("AnyHashable(NumericTypes)/Hashable") {
|
||||
// Numeric types holding mathematically equal values must compare equal and
|
||||
// hash the same way when converted to AnyHashable.
|
||||
let groups: [[AnyHashable]] = [
|
||||
[
|
||||
1 as Int,
|
||||
1 as UInt,
|
||||
1 as Int8,
|
||||
1 as UInt8,
|
||||
1 as Int16,
|
||||
1 as UInt16,
|
||||
1 as Int32,
|
||||
1 as UInt32,
|
||||
1 as Int64,
|
||||
1 as UInt64,
|
||||
1 as Float,
|
||||
1 as Double,
|
||||
],
|
||||
[
|
||||
42 as Int,
|
||||
42 as UInt,
|
||||
42 as Int8,
|
||||
42 as UInt8,
|
||||
42 as Int16,
|
||||
42 as UInt16,
|
||||
42 as Int32,
|
||||
42 as UInt32,
|
||||
42 as Int64,
|
||||
42 as UInt64,
|
||||
42 as Float,
|
||||
42 as Double,
|
||||
],
|
||||
[
|
||||
Int(Int32.max),
|
||||
UInt(Int32.max),
|
||||
Int32.max,
|
||||
UInt32(Int32.max),
|
||||
Int64(Int32.max),
|
||||
UInt64(Int32.max),
|
||||
Double(Int32.max),
|
||||
],
|
||||
[
|
||||
Float.infinity,
|
||||
Double.infinity,
|
||||
],
|
||||
[
|
||||
0x1.aP1 as Float, // 3.25
|
||||
0x1.aP1 as Double,
|
||||
],
|
||||
[
|
||||
0x1.a000000000001P1, // 3.25.nextUp, not representable by a Float
|
||||
]
|
||||
]
|
||||
checkHashableGroups(groups)
|
||||
}
|
||||
|
||||
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
||||
AnyHashableTests.test("AnyHashable(Float80)/Hashable") {
|
||||
let groups: [[AnyHashable]] = [
|
||||
[
|
||||
42 as Int,
|
||||
42 as Float,
|
||||
42 as Double,
|
||||
42 as Float80,
|
||||
],
|
||||
[
|
||||
Float.infinity,
|
||||
Double.infinity,
|
||||
Float80.infinity,
|
||||
],
|
||||
[
|
||||
3.25 as Float,
|
||||
3.25 as Double,
|
||||
3.25 as Float80,
|
||||
],
|
||||
[
|
||||
0x1.a000000000001P1 as Double, // 3.25.nextUp
|
||||
0x1.a000000000001P1 as Float80,
|
||||
],
|
||||
[
|
||||
0x1.a000000000000002p1 as Float80, // (3.25 as Float80).nextUp
|
||||
],
|
||||
]
|
||||
checkHashableGroups(groups)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
// A wrapper type around an Int that bridges to NSNumber.
|
||||
struct IntWrapper1 : _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
let rawValue: Int
|
||||
}
|
||||
|
||||
// A wrapper type around an Int that bridges to NSNumber.
|
||||
struct IntWrapper2 : _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
let rawValue: Int
|
||||
}
|
||||
|
||||
// A wrapper type around an Int that bridges to NSNumber.
|
||||
struct Int8Wrapper : _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
let rawValue: Int8
|
||||
}
|
||||
|
||||
AnyHashableTests.test("AnyHashable(IntWrappers)/Hashable") {
|
||||
let groups: [[AnyHashable]] = [
|
||||
[
|
||||
IntWrapper1(rawValue: 42),
|
||||
IntWrapper2(rawValue: 42),
|
||||
Int8Wrapper(rawValue: 42),
|
||||
42,
|
||||
42 as Double,
|
||||
42 as NSNumber,
|
||||
],
|
||||
[
|
||||
IntWrapper1(rawValue: -23),
|
||||
IntWrapper2(rawValue: -23),
|
||||
Int8Wrapper(rawValue: -23),
|
||||
-23,
|
||||
-23 as Double,
|
||||
-23 as NSNumber,
|
||||
],
|
||||
]
|
||||
checkHashableGroups(groups)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
// A wrapper type around a String that bridges to NSString.
|
||||
struct StringWrapper1 : _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
@@ -778,126 +904,101 @@ struct StringWrapper2 : _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
||||
let rawValue: String
|
||||
}
|
||||
|
||||
AnyHashableTests.test("AnyHashable(Wrappers)/Hashable") {
|
||||
let values: [AnyHashable] = [
|
||||
AnyHashableTests.test("AnyHashable(StringWrappers)/Hashable") {
|
||||
let groups: [[AnyHashable]] = [
|
||||
[
|
||||
StringWrapper1(rawValue: "hello"),
|
||||
StringWrapper2(rawValue: "hello"),
|
||||
"hello" as String,
|
||||
"hello" as NSString,
|
||||
],
|
||||
[
|
||||
StringWrapper1(rawValue: "world"),
|
||||
StringWrapper2(rawValue: "world"),
|
||||
"world" as String,
|
||||
"world" as NSString,
|
||||
]
|
||||
|
||||
func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
|
||||
// Elements in [0, 3] match 3.
|
||||
if lhs == 3 { return rhs >= 0 && rhs <= 3 }
|
||||
if rhs == 3 { return lhs >= 0 && lhs <= 3 }
|
||||
|
||||
// Elements in [4, 7] match 7.
|
||||
if lhs == 7 { return rhs >= 4 && rhs <= 7 }
|
||||
if rhs == 7 { return lhs >= 4 && lhs <= 7 }
|
||||
|
||||
return lhs == rhs
|
||||
}
|
||||
|
||||
func hashEqualityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
|
||||
// Elements in [0, 3] hash the same, as do elements in [4, 7].
|
||||
return lhs / 4 == rhs / 4
|
||||
}
|
||||
|
||||
checkHashable(
|
||||
values,
|
||||
equalityOracle: equalityOracle,
|
||||
hashEqualityOracle: hashEqualityOracle,
|
||||
allowBrokenTransitivity: true)
|
||||
]
|
||||
checkHashableGroups(groups)
|
||||
}
|
||||
|
||||
AnyHashableTests.test("AnyHashable(Set)/Hashable") {
|
||||
let values: [AnyHashable] = [
|
||||
let groups: [[AnyHashable]] = [
|
||||
[
|
||||
Set([1, 2, 3]),
|
||||
Set([1, 2, 3] as [Int8]),
|
||||
Set([1, 2, 3] as [Float]),
|
||||
NSSet(set: [1, 2, 3]),
|
||||
],
|
||||
[
|
||||
Set([2, 3, 4]),
|
||||
NSSet(set: [2, 3, 4]),
|
||||
],
|
||||
[
|
||||
Set([Set([1, 2]), Set([3, 4])]),
|
||||
NSSet(set: [NSSet(set: [1, 2]), NSSet(set: [3, 4])]),
|
||||
],
|
||||
[
|
||||
Set([Set([1, 3]), Set([2, 4])]),
|
||||
NSSet(set: [NSSet(set: [1, 3]), NSSet(set: [2, 4])]),
|
||||
],
|
||||
]
|
||||
|
||||
func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (0...1, 0...1): return true
|
||||
case (2...3, 2...3): return true
|
||||
case (4...5, 4...5): return true
|
||||
case (6...7, 6...7): return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
checkHashable(
|
||||
values,
|
||||
equalityOracle: equalityOracle,
|
||||
allowBrokenTransitivity: true)
|
||||
checkHashableGroups(groups)
|
||||
}
|
||||
|
||||
AnyHashableTests.test("AnyHashable(Array)/Hashable") {
|
||||
let values: [AnyHashable] = [
|
||||
let groups: [[AnyHashable]] = [
|
||||
[
|
||||
[1, 2, 3],
|
||||
[1, 2, 3] as [Int8],
|
||||
[1, 2, 3] as [Double],
|
||||
NSArray(array: [1, 2, 3]),
|
||||
],
|
||||
[
|
||||
[3, 2, 1],
|
||||
[3, 2, 1] as [AnyHashable],
|
||||
NSArray(array: [3, 2, 1]),
|
||||
],
|
||||
[
|
||||
[[1, 2], [3, 4]],
|
||||
NSArray(array: [NSArray(array: [1, 2]), NSArray(array: [3, 4])]),
|
||||
],
|
||||
[
|
||||
[[3, 4], [1, 2]],
|
||||
NSArray(array: [NSArray(array: [3, 4]), NSArray(array: [1, 2])]),
|
||||
]
|
||||
|
||||
func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (0...1, 0...1): return true
|
||||
case (2...3, 2...3): return true
|
||||
case (4...5, 4...5): return true
|
||||
case (6...7, 6...7): return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
checkHashable(values, equalityOracle: equalityOracle,
|
||||
allowBrokenTransitivity: true)
|
||||
]
|
||||
checkHashableGroups(groups)
|
||||
}
|
||||
|
||||
AnyHashableTests.test("AnyHashable(Dictionary)/Hashable") {
|
||||
let values: [AnyHashable] = [
|
||||
["hello": 1, "world": 2],
|
||||
let groups: [[AnyHashable]] = [
|
||||
[
|
||||
["hello": 1, "world": 2] as [String: Int],
|
||||
["hello": 1, "world": 2] as [String: Int16],
|
||||
["hello": 1, "world": 2] as [String: Float],
|
||||
NSDictionary(dictionary: ["hello": 1, "world": 2]),
|
||||
],
|
||||
[
|
||||
["hello": 2, "world": 1],
|
||||
NSDictionary(dictionary: ["hello": 2, "world": 1]),
|
||||
],
|
||||
[
|
||||
["hello": ["foo": 1, "bar": 2],
|
||||
"world": ["foo": 2, "bar": 1]],
|
||||
NSDictionary(dictionary: [
|
||||
"hello": ["foo": 1, "bar": 2],
|
||||
"world": ["foo": 2, "bar": 1]]),
|
||||
],
|
||||
[
|
||||
["hello": ["foo": 2, "bar": 1],
|
||||
"world": ["foo": 1, "bar": 2]],
|
||||
NSDictionary(dictionary: [
|
||||
"hello": ["foo": 2, "bar": 1],
|
||||
"world": ["foo": 1, "bar": 2]]),
|
||||
],
|
||||
]
|
||||
|
||||
func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (0...1, 0...1): return true
|
||||
case (2...3, 2...3): return true
|
||||
case (4...5, 4...5): return true
|
||||
case (6...7, 6...7): return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
checkHashable(values, equalityOracle: equalityOracle,
|
||||
allowBrokenTransitivity: true)
|
||||
checkHashableGroups(groups)
|
||||
}
|
||||
|
||||
AnyHashableTests.test("AnyHashable(_SwiftNativeNSError(MinimalHashablePODSwiftError))/Hashable") {
|
||||
|
||||
@@ -6,26 +6,73 @@ import StdlibUnittest
|
||||
var DictionaryTests = TestSuite("Dictionary")
|
||||
|
||||
DictionaryTests.test("index<Hashable>(forKey:)") {
|
||||
let d: [AnyHashable : Int] = [
|
||||
AnyHashable(10) : 1010,
|
||||
AnyHashable(20) : 2020,
|
||||
AnyHashable(30.0) : 3030,
|
||||
let a = AnyHashable(10 as UInt16)
|
||||
let b = AnyHashable(20)
|
||||
let c = AnyHashable(30.0)
|
||||
let d: [AnyHashable: Int] = [
|
||||
a: 1010,
|
||||
b: 2020,
|
||||
c: 3030,
|
||||
]
|
||||
|
||||
expectEqual(1010, d[d.index(forKey: 10)!].value)
|
||||
expectEqual(2020, d[d.index(forKey: 20)!].value)
|
||||
expectEqual(3030, d[d.index(forKey: 30.0)!].value)
|
||||
for (key, k, value) in [(a, 10, 1010), (b, 20, 2020), (c, 30, 3030)] {
|
||||
let index = d.index(forKey: key)!
|
||||
expectEqual(value, d[index].value)
|
||||
// We must be able to look up the same number in any representation.
|
||||
expectEqual(index, d.index(forKey: UInt8(k)))
|
||||
expectEqual(index, d.index(forKey: UInt16(k)))
|
||||
expectEqual(index, d.index(forKey: UInt32(k)))
|
||||
expectEqual(index, d.index(forKey: UInt64(k)))
|
||||
expectEqual(index, d.index(forKey: UInt(k)))
|
||||
expectEqual(index, d.index(forKey: Int8(k)))
|
||||
expectEqual(index, d.index(forKey: Int16(k)))
|
||||
expectEqual(index, d.index(forKey: Int32(k)))
|
||||
expectEqual(index, d.index(forKey: Int64(k)))
|
||||
expectEqual(index, d.index(forKey: Int(k)))
|
||||
expectEqual(index, d.index(forKey: Float(k)))
|
||||
expectEqual(index, d.index(forKey: Double(k)))
|
||||
|
||||
expectNil(d.index(forKey: 10.0))
|
||||
expectNil(d.index(forKey: 20.0))
|
||||
expectNil(d.index(forKey: 30))
|
||||
expectNil(d.index(forKey: String(k)))
|
||||
}
|
||||
}
|
||||
|
||||
DictionaryTests.test("subscript<Hashable>(_:)") {
|
||||
var d: [AnyHashable : Int] = [
|
||||
AnyHashable(10) : 1010,
|
||||
AnyHashable(20) : 2020,
|
||||
AnyHashable(30.0) : 3030,
|
||||
let a = AnyHashable(10 as UInt16)
|
||||
let b = AnyHashable(20)
|
||||
let c = AnyHashable(30.0)
|
||||
let d: [AnyHashable: Int] = [
|
||||
a: 1010,
|
||||
b: 2020,
|
||||
c: 3030,
|
||||
]
|
||||
|
||||
for (key, k, value) in [(a, 10, 1010), (b, 20, 2020), (c, 30, 3030)] {
|
||||
let index = d.index(forKey: key)!
|
||||
expectEqual(value, d[key])
|
||||
// We must be able to look up the same number in any representation.
|
||||
expectEqual(value, d[UInt8(k)])
|
||||
expectEqual(value, d[UInt16(k)])
|
||||
expectEqual(value, d[UInt32(k)])
|
||||
expectEqual(value, d[UInt64(k)])
|
||||
expectEqual(value, d[UInt(k)])
|
||||
expectEqual(value, d[Int8(k)])
|
||||
expectEqual(value, d[Int16(k)])
|
||||
expectEqual(value, d[Int32(k)])
|
||||
expectEqual(value, d[Int64(k)])
|
||||
expectEqual(value, d[Int(k)])
|
||||
expectEqual(value, d[Float(k)])
|
||||
expectEqual(value, d[Double(k)])
|
||||
|
||||
expectNil(d[String(k)])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DictionaryTests.test("subscript<Hashable>(_:)/2") {
|
||||
var d: [AnyHashable: Int] = [
|
||||
AnyHashable(10): 1010,
|
||||
AnyHashable(20): 2020,
|
||||
AnyHashable(30.0): 3030,
|
||||
]
|
||||
|
||||
expectEqual(1010, d[10])
|
||||
@@ -100,57 +147,61 @@ DictionaryTests.test("updateValue<Hashable>(_:forKey:)") {
|
||||
expectEqual(expected, d)
|
||||
}
|
||||
|
||||
expectNil(d.updateValue(4040, forKey: 10.0))
|
||||
expectEqual(101010, d.updateValue(4040, forKey: 10.0))
|
||||
do {
|
||||
let expected: [AnyHashable : Int] = [
|
||||
AnyHashable(10) : 101010,
|
||||
AnyHashable(10) : 4040,
|
||||
AnyHashable(20) : 202020,
|
||||
AnyHashable(30.0) : 303030,
|
||||
AnyHashable(10.0) : 4040,
|
||||
]
|
||||
expectEqual(expected, d)
|
||||
}
|
||||
|
||||
expectNil(d.updateValue(5050, forKey: 20.0))
|
||||
expectEqual(202020, d.updateValue(5050, forKey: 20.0))
|
||||
do {
|
||||
let expected: [AnyHashable : Int] = [
|
||||
AnyHashable(10) : 101010,
|
||||
AnyHashable(20) : 202020,
|
||||
AnyHashable(10) : 4040,
|
||||
AnyHashable(20) : 5050,
|
||||
AnyHashable(30.0) : 303030,
|
||||
AnyHashable(10.0) : 4040,
|
||||
AnyHashable(20.0) : 5050,
|
||||
]
|
||||
expectEqual(expected, d)
|
||||
}
|
||||
|
||||
expectNil(d.updateValue(6060, forKey: 30))
|
||||
expectEqual(303030, d.updateValue(6060, forKey: 30))
|
||||
do {
|
||||
let expected: [AnyHashable : Int] = [
|
||||
AnyHashable(10) : 101010,
|
||||
AnyHashable(20) : 202020,
|
||||
AnyHashable(30.0) : 303030,
|
||||
AnyHashable(10.0) : 4040,
|
||||
AnyHashable(20.0) : 5050,
|
||||
AnyHashable(30) : 6060,
|
||||
AnyHashable(10) : 4040,
|
||||
AnyHashable(20) : 5050,
|
||||
AnyHashable(30.0) : 6060,
|
||||
]
|
||||
expectEqual(expected, d)
|
||||
}
|
||||
}
|
||||
|
||||
DictionaryTests.test("removeValue<Hashable>(forKey:)") {
|
||||
var d: [AnyHashable : Int] = [
|
||||
AnyHashable(10) : 1010,
|
||||
let d: [AnyHashable : Int] = [
|
||||
AnyHashable(10 as UInt8) : 1010,
|
||||
AnyHashable(20) : 2020,
|
||||
AnyHashable(30.0) : 3030,
|
||||
]
|
||||
|
||||
expectNil(d.removeValue(forKey: 10.0))
|
||||
expectNil(d.removeValue(forKey: 20.0))
|
||||
expectNil(d.removeValue(forKey: 30))
|
||||
for (key, value) in [(10, 1010), (20, 2020), (30, 3030)] {
|
||||
var dd = d
|
||||
expectEqual(value, dd.removeValue(forKey: UInt8(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: UInt16(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: UInt32(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: UInt64(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: UInt(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: Int8(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: Int16(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: Int32(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: Int64(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: Int(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: Float(key)))
|
||||
dd = d; expectEqual(value, dd.removeValue(forKey: Double(key)))
|
||||
|
||||
expectEqual(1010, d.removeValue(forKey: 10)!)
|
||||
expectEqual(2020, d.removeValue(forKey: 20)!)
|
||||
expectEqual(3030, d.removeValue(forKey: 30.0)!)
|
||||
dd = d; expectNil(dd.removeValue(forKey: String(key)))
|
||||
}
|
||||
}
|
||||
|
||||
runAllTests()
|
||||
|
||||
@@ -233,22 +233,26 @@ hash_value_test_template = gyb.parse_template("hash_value",
|
||||
% for self_ty in all_integer_types(word_bits):
|
||||
% Self = self_ty.stdlib_name
|
||||
|
||||
FixedPoint.test("${Self}.hashValue") {
|
||||
FixedPoint.test("${Self}.hash(into:)") {
|
||||
|
||||
% for bit_pattern in test_bit_patterns:
|
||||
|
||||
do {
|
||||
% input = prepare_bit_pattern(bit_pattern, self_ty.bits, self_ty.is_signed)
|
||||
let input = get${Self}(${input})
|
||||
let output = getInt(input.hashValue)
|
||||
var hasher = Hasher()
|
||||
input.hash(into: &hasher)
|
||||
let output = getInt(hasher.finalize())
|
||||
|
||||
var hasher = _SipHash13(_seed: Hasher._seed)
|
||||
% if prepare_bit_pattern(input, word_bits, self_ty.is_signed) == input:
|
||||
hasher._combine(UInt(truncatingIfNeeded: ${input} as ${"" if self_ty.is_signed else "U"}Int))
|
||||
% reference = prepare_bit_pattern(bit_pattern, self_ty.bits, False)
|
||||
% if self_ty.bits == 64:
|
||||
let expected = Hasher._hash(seed: Hasher._seed, ${reference} as UInt64)
|
||||
% else:
|
||||
hasher._combine(UInt64(truncatingIfNeeded: input))
|
||||
let expected = Hasher._hash(
|
||||
seed: Hasher._seed,
|
||||
bytes: ${reference},
|
||||
count: ${self_ty.bits / 8})
|
||||
% end
|
||||
let expected = getInt(Int(truncatingIfNeeded: hasher.finalize()))
|
||||
expectEqual(expected, output, "input: \(input)")
|
||||
}
|
||||
|
||||
|
||||
@@ -28,28 +28,45 @@ var SetTests = TestSuite("Set")
|
||||
|
||||
SetTests.test("contains<Hashable>(_:)") {
|
||||
let s: Set<AnyHashable> = [
|
||||
AnyHashable(1010), AnyHashable(2020), AnyHashable(3030.0)
|
||||
AnyHashable(1010 as UInt16), AnyHashable(2020), AnyHashable(3030.0)
|
||||
]
|
||||
expectTrue(s.contains(1010))
|
||||
expectTrue(s.contains(2020))
|
||||
expectTrue(s.contains(3030.0))
|
||||
for i in [1010, 2020, 3030] {
|
||||
// We must be able to look up the same number in any representation.
|
||||
expectTrue(s.contains(UInt16(i)))
|
||||
expectTrue(s.contains(UInt32(i)))
|
||||
expectTrue(s.contains(UInt64(i)))
|
||||
expectTrue(s.contains(UInt(i)))
|
||||
expectTrue(s.contains(Int16(i)))
|
||||
expectTrue(s.contains(Int32(i)))
|
||||
expectTrue(s.contains(Int64(i)))
|
||||
expectTrue(s.contains(Int(i)))
|
||||
expectTrue(s.contains(Float(i)))
|
||||
expectTrue(s.contains(Double(i)))
|
||||
|
||||
expectFalse(s.contains(1010.0))
|
||||
expectFalse(s.contains(2020.0))
|
||||
expectFalse(s.contains(3030))
|
||||
expectFalse(s.contains(String(i)))
|
||||
}
|
||||
}
|
||||
|
||||
SetTests.test("index<Hashable>(of:)") {
|
||||
let s: Set<AnyHashable> = [
|
||||
AnyHashable(1010), AnyHashable(2020), AnyHashable(3030.0)
|
||||
]
|
||||
expectEqual(AnyHashable(1010), s[s.firstIndex(of: 1010)!])
|
||||
expectEqual(AnyHashable(2020), s[s.firstIndex(of: 2020)!])
|
||||
expectEqual(AnyHashable(3030.0), s[s.firstIndex(of: 3030.0)!])
|
||||
let a = AnyHashable(1010 as UInt16)
|
||||
let b = AnyHashable(2020)
|
||||
let c = AnyHashable(3030.0)
|
||||
let s: Set<AnyHashable> = [a, b, c]
|
||||
for (element, i) in [(a, 1010), (b, 2020), (c, 3030)] {
|
||||
let index = s.firstIndex(of: element)!
|
||||
|
||||
expectNil(s.firstIndex(of: 1010.0))
|
||||
expectNil(s.firstIndex(of: 2020.0))
|
||||
expectNil(s.firstIndex(of: 3030))
|
||||
// We must be able to look up the same number in any representation.
|
||||
expectEqual(index, s.firstIndex(of: UInt16(i)))
|
||||
expectEqual(index, s.firstIndex(of: UInt32(i)))
|
||||
expectEqual(index, s.firstIndex(of: UInt64(i)))
|
||||
expectEqual(index, s.firstIndex(of: UInt(i)))
|
||||
expectEqual(index, s.firstIndex(of: Int16(i)))
|
||||
expectEqual(index, s.firstIndex(of: Int32(i)))
|
||||
expectEqual(index, s.firstIndex(of: Int64(i)))
|
||||
expectEqual(index, s.firstIndex(of: Int(i)))
|
||||
expectEqual(index, s.firstIndex(of: Float(i)))
|
||||
expectEqual(index, s.firstIndex(of: Double(i)))
|
||||
}
|
||||
}
|
||||
|
||||
SetTests.test("insert<Hashable>(_:)") {
|
||||
@@ -230,5 +247,28 @@ SetTests.test("remove<Hashable>(_:)/CastTrap")
|
||||
s.remove(TestHashableDerivedB(2020, identity: 2))
|
||||
}
|
||||
|
||||
SetTests.test("Hashable/Conversions") {
|
||||
let input: [Set<AnyHashable>] = [
|
||||
[10 as UInt8, 20 as UInt8, 30 as UInt8],
|
||||
[10 as UInt16, 20 as UInt16, 30 as UInt16],
|
||||
[10 as UInt32, 20 as UInt32, 30 as UInt32],
|
||||
[10 as UInt64, 20 as UInt64, 30 as UInt64],
|
||||
[10 as UInt, 20 as UInt, 30 as UInt],
|
||||
[10 as Int8, 20 as Int8, 30 as Int8],
|
||||
[10 as Int16, 20 as Int16, 30 as Int16],
|
||||
[10 as Int32, 20 as Int32, 30 as Int32],
|
||||
[10 as Int64, 20 as Int64, 30 as Int64],
|
||||
[10 as Int, 20 as Int, 30 as Int],
|
||||
[10 as Float, 20 as Float, 30 as Float],
|
||||
[10 as Double, 20 as Double, 30 as Double],
|
||||
[[1, 2, 3] as Set<Int>, [2, 3, 4] as Set<UInt8>, [3, 4, 5] as Set<Float>],
|
||||
[[1, 2, 3] as Set<Int8>, [2, 3, 4] as Set<Double>, [3, 4, 5] as Set<Int32>],
|
||||
[[1, 2, 3] as Set<UInt32>, [2, 3, 4] as Set<Int16>, [3, 4, 5] as Set<UInt>],
|
||||
]
|
||||
|
||||
checkHashable(input, equalityOracle: { ($0 < 12) == ($1 < 12) })
|
||||
}
|
||||
|
||||
|
||||
runAllTests()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user