-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
[WIP] Conditionally conform stdlib types to Hashable #14527
Conversation
…d Array types. (#14247) * Add conditional Hashable conformance to Optional, Dictionary, Array, ArraySlice and ContiguousArray * Modified hashValue implementations The hashValues are now calculated similar to the automatically synthesized values when conforming to Hashable. This entails using _combineHashValues as values of the collections are iterated - as well as calling _mixInt before returning the hash. * Added FIXMEs as suggested by Max Moiseev * Use checkHashable to check Hashable conformance * Use 2 space indentation * Hashing of Dictionary is now independent of traversal order * Added a test to proof failure of (previous) wrong implementation of Dictionary hashValue. Unfortunately it does not work. * Removed '_mixInt' from 'hashValue' implementation of Optional and Array types based on recommendations from lorentey * Another attempt at detecting bad hashing due to traversal order * Dictionary Hashable validation tests now detect bad hashing due to dependence on traversal order * Removed superfluous initial _mixInt call for Dictionary hashValue implementation. * Add more elements to dictionary in test to increase the number of possible permutations - making it more likely to detect order-dependent hashes * Added Hashable conformance to CollectionOfOne, EmptyCollection and Range types * Fix indirect referral to the only member of CollectionOfOne * Re-added Hashable conformance to Range after merge from master * Change hashValue based on comment from @lorentey * Remove tests for conditional Hashable conformance for Range types. This is left for a followup PR * Added tests for CollectionOfOne and EmptyCollection * Added conditional conformance fo Equatable and Hashable for DictionaryLiteral. Added tests too. * Added conditional Equatable and Hashable conformance to Slice * Use 'elementsEqual' for Slice equality operator * Fixed documentation comment and indentation * Fix DictionaryLiteral equality implementation * Revert "Fix DictionaryLiteral equality implementation" This reverts commit 7fc1510. * Fix DictionaryLiteral equality implementation * Use equalElements(:by:) to compare DictionaryLiteral elements * Added conditional conformance for Equatable and Hashable to AnyCollection * Revert "Use 'elementsEqual' for Slice equality operator" This reverts commit 0ba2278. * Revert "Added conditional Equatable and Hashable conformance to Slice" This reverts commit 84f9934. * Added conditional conformance for Equatable and Hashable for ClosedRange
This is a continuation of @mortenbekditlevsen's PR #14247; having a dedicated branch makes it easier to collaborate on landing this change. |
@lorentey Perhaps this is obvious to all involved already, but I am discovering that in fact Swift ignores |
@Xvu That's unfortunate; I think we may still get it to work by also adding the attribute to the individual members that implement the conformances. (At the cost of adding more warnings to stdlib's build logs.) Or we may just leave these types out for now; however, shipping conditional conformances without full stdlib coverage would probably lead to people adding these conformances in their own code, causing even more issues. cc @airspeedswift |
We didn't have this problem with I really think |
I agree that The (*) However, later cannot mean a subsequent Swift release, because that would leave the door wide open for even more serious problems. If the stdlib didn't provide conditional Ideally, the stdlib should add conformances for its own protocols to its own types whenever doing that makes even the slightest sense, to prevent packages from trying to do it themselves. |
Adding |
Well, actually... A key difference between As to your general point, I would disagree that the bar for adding conditional conformances for standard library protocols is that it "makes even the slightest sense." When there are multiple reasonable semantics for doing so, adopting one such conditional conformance bars end users from conforming in any other way. This is particularly problematic when the standard library type in question is generic: It may make extraordinary sense to conform In general, we should reiterate that conforming a type you don't own to a protocol you don't own is not advised. We don't need to undertake heroics to design around such shenanigans, just as we don't in general make design decisions explicitly to prevent abuses. |
The "slightest sense" argument clearly does not apply to In any case, my opinion is that users should never, ever conform stdlib types to stdlib protocols, conditionally or otherwise -- that is the sole prerogative of the standard library. The way to customize/override the behavior of stdlib types is to wrap them in little structs, making the deviation explicit. Do you have an example where such a conformance may in fact be necessary? (We should probably take this to the forums, though.) |
I think we largely agree on the general principle. As to the appending of attributes, have you explored avoiding build warnings by instead using conditional compilation directives? |
If |
…tions Now that Array and Dictionary conform to Hashable, we need to make sure that their bridged counterparts provide the same hash values when converted to AnyHashable.
[conditional-hashable] Silence warning (unused binding) and break to 80 characters
This assumes these will land in Swift 4.1; the attributes need to be adjusted if that turns out not to be the case. It seems @available for protocol conformances is not yet functional. I added attributes for those anyway, marked with FIXME(conformance-availability). # Conflicts: # stdlib/public/core/ExistentialCollection.swift.gyb # stdlib/public/core/Mirror.swift
…texts yet Array and Dictionary are now conditionally Hashable, so the importer wants to use them when importing NSArray and NSDictionary types in hashable contexts. Unfortunately, this currently means that a type like NSSet<NSDictionary<NSString *, id> *> * gets imported as Set<Dictionary<String, Any>>, which is invalid — Dictionary.Value needs to be Hashable, too: Set<Dictionary<String, AnyHashable>> For now, work around this by explicitly turning NSArray and NSDictionary into AnyHashable when they are used as the first type parameter of NSSet or NSDictionary, ignoring Hashable conformance in this case. This reverts to the previous behavior.
@swift-ci please test |
@swift-ci please test source compatibility |
@swift-ci please smoke test compiler performance |
I pushed some changes to fix the two failures in the Swift tests. I also added I expect the Linux build will still fail for now. (In fact, the package manager failure will prevent the compiler performance and source compatibility tests from succeeding, either. D'oh.) |
Since you've annotated |
Build comment file:Compilation-performance test failed |
Oops, that was a serious oversight on my part; I intended to annotate But if we mark the ones we add now, we should also mark the conformances added for SE-0188, so let's consider it a lucky mistake. (We may end up choosing to remove all of these attributes but the one for |
@swift-ci Please test OS X platform |
Fun: |
When implementing a protocol requirement, additional doc comments are unnecessary unless there are additional semantics peculiar to the specific implementation. The manually propagated documentation had several problems, including copypasta and inconsistent terminology. Therefore, if `T.==` doesn't have any doc comments and the implementation of `T.hashValue` is unremarkable, remove the unnecessary doc comment.
@swift-ci Please smoke test |
@lorentey We don't need [Edit: As long as the user isn't in turn vending a public implementation of The one scenario where this fails is in the case of operators. Down the road, we'll want to fix that issue definitively; operator overload resolution has all sorts of messy issues at the moment. |
With respect to The underscored attribute Until then, if the worry is that leaving out a conditional conformance will cause end users to use their own, we can prohibit such use with a placeholder such as |
First, make sure combining hash values isn't done with xor. Second, Optional is now conditionally Hashable, so let's use it!
...I have an alternative solution, based on that realization about non-operator methods not causing source breakage. |
@swift-ci Please smoke test |
Please test with the following PR: swiftlang/swift-package-manager#1495 |
Huzzah! |
May I ask: What is the next step for this PR? |
It was a little late in the 4.1 convergence cycle to get this in for that release, especially as it involves workarounds, adding an |
Super - thanks for the update! I'll move parts of the implementation to 'user space' until it lands in Swift 4.2 or Swift 5.0. :-) |
@mortenbekditlevsen That's...not ideal if you can avoid it. Doing so guarantees that we'll be source-breaking when this does land. |
@xwu Thanks - it's only for some non-public libraries that we use internally at work, so we'll be fine with the source breaking update. |
Will this realistically be possible to land in Swift 4.2? I have seen there are a few open PRs concerning hashing and quite the discussion about the |
This change is orthogonal to To get things moving again, I'm going to rebase and update this PR to incorporate recent changes in hashing, which made hash compositions much easier to implement. While the source compatibility issue is important, I believe adding the
|
I opened #15382 as the rebased & updated version of this PR, with a view towards landing it soon. |
As a followup to SE-0143, this adds conditional conformance to
Hashable
to the following types in the standard library:Optional
Array
,ContiguousArray
,ArraySlice
Dictionary
Range
,ClosedRange
AnyCollection
DictionaryLiteral
CollectionOfOne
This PR also adds unconditional conformance to
Hashable
forEmptyCollection
.This makes synthesized
Hashable
implementations (SE-0185) available for structs and enums that include fields of these types, freeing programmers from having to manually deal with hash combinators.As part of this work, this PR also adds conditional conformances to
Equatable
to the following types:AnyCollection
DictionaryLiteral
CollectionOfOne
Resolves SR-6910.