Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-7496] AnyHashable conforms to neither Equatable nor Hashable #3717

Closed
lorentey opened this issue Apr 22, 2018 · 3 comments
Closed

[SR-7496] AnyHashable conforms to neither Equatable nor Hashable #3717

lorentey opened this issue Apr 22, 2018 · 3 comments

Comments

@lorentey
Copy link
Member

Previous ID SR-7496
Radar rdar://problem/39648819
Original Reporter @lorentey
Type Bug
Status Resolved
Resolution Done
Environment

Tested in Swift 4.1 and above, including current master (as of 2018-04-22)

Additional Detail from JIRA
Votes 0
Component/s Foundation, Standard Library
Labels Bug
Assignee @lorentey
Priority Medium

md5: dd7f6a2e9eb8a9ba1143f7ad79b73be4

Issue Description:

AnyHashable violates the transitivity requirement of Equatable:

import Foundation
let a: AnyHashable = 42 as UInt8
let b: AnyHashable = NSNumber(value: 42)
let c: AnyHashable = 42
print(a == b, b == c, a == c) // => true, true, false

Hashable requires that if two values are equal, then they must hash the same way. AnyHashable violates this, too:

let f: AnyHashable = 42.0
let g: AnyHashable = NSNumber(value: 42.0)
print(f == g, f.hashValue == g.hashValue) // => true, false

This makes AnyHashable unreliable for use in Sets or as Dictionary keys. Values inserted into a dictionary may or may not be retrievable, depending on the provenance of the lookup key. Set and Dictionary are sometimes able to detect these violations during rehashing operations; when that happens, they will (correctly!) trap with "Duplicate element found in Set"/"Duplicate key found in Dictionary".

With SE-0206 (Hashable Enhancements), we'd like to have integer types use distinct hash encodings; however, that would mean that even simple integer bridging cases would trigger the Hashable violation. (_HasCustomAnyHashableRepresentation's magical Equatable support implicitly assumes that every Swift type that bridges to the same Objective-C representation shares the same hash encoding.)

@lorentey
Copy link
Member Author

I think the issue only affects bridging scenarios that involve NSNumber, but that includes Hashable collections of numbers like Array<Int>, Set<UInt32>, or Dictionary<String, Float>.

With SE-0170, integer/float bridging is not strictly type preserving any more; we need to update integers' and floating point numbers' AnyHashable representation to correctly take this into account.

let i = 42 as UInt8
let j = 42 as Int64
print(i == j) // => true (heterogeneous comparison, unrelated to Equatable)
print(i.hashValue == j.hashValue) // true in 4.1, SE-0206 changes it to false
let ai = i as AnyHashable
let aj = j as AnyHashable
print(ai == aj) // false in 4.1, should be true
print(ai.hashValue == aj.hashValue) // true in 4.1, should remain true with SE-0206

@lorentey
Copy link
Member Author

@swift-ci create

@lorentey
Copy link
Member Author

Fixed in apple/swift#17396 & apple/swift#17518

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@shahmishal shahmishal transferred this issue from apple/swift May 5, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant