-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[4.2][stdlib] Fix AnyHashable's Equatable/Hashable conformance #17518
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
[4.2][stdlib] Fix AnyHashable's Equatable/Hashable conformance #17518
Conversation
AnyHashable has numerous edge cases where two AnyHashable values compare equal but produce different hashes. This breaks Set and Dictionary invariants and can cause unexpected behavior and/or traps. This change overhauls AnyHashable's implementation to fix these edge cases, hopefully without introducing new issues. - Fix transitivity of ==. Previously, comparisons involving AnyHashable values with Objective-C provenance were handled specially, breaking Equatable: let a = (42 as Int as AnyHashable) let b = (42 as NSNumber as AnyHashable) let c = (42 as Double as AnyHashable) a == b // true b == c // true a == c // was false(!), now true let d = ("foo" as AnyHashable) let e = ("foo" as NSString as AnyHashable) let f = ("foo" as NSString as NSAttributedStringKey as AnyHashable) d == e // true e == f // true d == f // was false(!), now true - Fix Hashable conformance for numeric types boxed into AnyHashable: b == c // true b.hashValue == c.hashValue // was false(!), now true Fixing this required adding a custom AnyHashable box for all standard integer and floating point types. The custom box was needed to ensure that two AnyHashables containing the same number compare equal and hash the same way, no matter what their original type was. (This behavior is required to ensure consistency with NSNumber, which has not been preserving types since SE-0170. - Add custom AnyHashable representations for Arrays, Sets and Dictionaries, so that when they contain numeric types, they hash correctly under the new rules above. - Remove AnyHashable._usedCustomRepresentation. The provenance of a value should not affect its behavior. - Allow AnyHashable values to be downcasted into compatible types more often. - Forward _rawHashValue(seed:) to AnyHashable box. This fixes AnyHashable hashing for types that customize single-shot hashing. https://bugs.swift.org/browse/SR-7496 rdar://problem/39648819 (cherry picked from commit ff91f36) # Conflicts: # stdlib/public/core/AnyHashable.swift # stdlib/public/core/Arrays.swift.gyb # stdlib/public/core/Hasher.swift # stdlib/public/core/Integers.swift.gyb
@swift-ci test |
@swift-ci test source compatibility |
Build failed |
Build failed |
Fix Hashable conformance of standard integer types so that the number of bits they feed into hasher is exactly Self.bitWidth. This was intended to be part of SE-0206. However, it would have introduced additional issues with AnyHashable. The custom AnyHashable representations introduced in the previous commit unify hashing for numeric types, eliminating the problem. (cherry picked from commit bf872ec) # Conflicts: # stdlib/public/core/Integers.swift.gyb
3802b8c
to
8bd1930
Compare
@swift-ci test |
1 similar comment
@swift-ci test |
_canonicalBox can perform essentially the same job, so there is no reason to have these requirements. (cherry picked from commit 2f4ad79)
@swift-ci please test |
1 similar comment
@swift-ci please test |
Build failed |
@swift-ci please test linux platform |
Build failed |
Hm, the Foundation failure seems unrelated. @swift-ci clean test linux platform |
@swift-ci please nominate |
(Cherry-picked from #17396, removing support for single-shot hashing. See that PR for more details.)
Explanation: AnyHashable violated Equatable/Hashable semantic requirements for numeric values, newtypes with bridged raw values, and bridgeable collections of these, which could result in unexpected behavior and/or traps when items of these types were inserted into heterogeneous sets/dictionaries. Completes the implementation of SE-0206 by implementing type-dependent hashing for standard integer types. (The requirement violations aren't new in 4.2, but completing SE-0206 would've added a bunch more cases, increasing urgency.)
Scope: Affects semantics of
Set<AnyHashable>
/Dictionary<AnyHashable, Any>
when elements are numeric types or newtypes. (Objective-C interfaces using NSSet/NSDictionary without type annotations get imported as these.) Changes integer hashing.Issue: https://bugs.swift.org/browse/SR-7496, rdar://problem/39648819
Risk: Medium. This has received extensive testing, but AnyHashable is subtle.
Testing: Swift stdlib tests via Swift CI, including new tests constructed specifically for these issues.
Reviewer: @DougGregor