Uh oh!
There was an error while loading. Please reload this page.
Prevent StackoverflowException when hashing lists above ~30k elements - #14516
Conversation
Open questions:
|
0101
commented
Jan 2, 2023
I'd probably stick with doing it the same as we do with array.
Can there be a SO with any other type? Maybe Set?
Probably just bump FSharp.Core version. |
T-Gro
commented
Jan 2, 2023
Just checked:
|
vzarytovskii
commented
Jan 3, 2023
The problem that can theoretically arise is that if someone persists their hashes somewhere, it would be a breaking change for them. |
Uh oh!
There was an error while loading. Please reload this page.
T-Gro
commented
Jan 3, 2023
The .NET guarantee is HashCode being stable only for the same process run. |
T-Gro
commented
Jan 5, 2023
Update after chatting with @dsyme I will change the code to hash based on the full contents, not just the first 18 elements. Reason: Devil's advocate: |
dsyme
commented
Jan 5, 2023
Plausible example - "I'm hashing the files in source repositories, which are represented as lists of lines. Now they all have the same hash code, because each has the same license at the top!" :) |
Uh oh!
There was an error while loading. Please reload this page.
dsyme
left a comment
There was a problem hiding this comment.
Need to look at the IStructuralEquatable GetHashCode imlpementation too?
OK, just needed to check things a little mote
T-Gro
commented
Jan 5, 2023
It calls to the same method in the end. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
dsyme
left a comment
There was a problem hiding this comment.
Change requested see comment
T-Gro
commented
Jan 5, 2023
dsyme
commented
Jan 5, 2023
@T-Gro Separately - regarding our earlier discussion on Teams - if people want to limit their hashing they should use this little known piece of magic in FSharp.Core https://github.com/dotnet/fsharp/blob/main/src/FSharp.Core/collections.fsi#L104C8-L124. If we were to make things more consistent, then really we should hash all of arrays all of the time for unlimited hashing (though that's also a risky change to make and I don't recommend it). Likewise my understanding is .NET hashes all of strings, for example (as does both F# limited and unlimited hashing at the moment) |
dsyme
commented
Jan 5, 2023
Yes, agreed, I looked through and checked that, thanks This still needs changing I think: #14516 (review) |
…et/fsharp into list-hashcode-stackoverflow
T-Gro
commented
Jan 5, 2023
Done, ready for re-review by everyone. |
abelbraaksma
commented
Jan 7, 2023
Wow, I’m so glad this bug got resolved!! Amazing work! Id love to see this backported into 6.x, but as you’ve said before, that’s not standard policy. It was such a big bug, and SOE cannot be captured. Funny, in all these years I never knew about the |

This addresses #1838 and fixes .GetHashCode() for the built-in
List<T>type.List is internally implemented as a recursive DU, and existing codegen emits code which is not tail recursive. If the list gets bigger (~30K elements), StackoverflowException appears when hashing it.
While I understand the desire to find a general solution for all recursive DUs incl. custom ones, I consider a StackoverflowException on the main language's collection type more urgent.
This solution is therefore special-cased only for the list type.
@cartermp tried to address this via standard F# attributes in https://github.com/dotnet/fsharp/pull/9070/files , but it affected the API shape of List which is too dangerous of a change.
This PR solves this by:
List<T>and.CustomHashCode(comparer)member..GetHashCode(comparer)simply calls into.CustomHashCode(comparer)As of now, I would tend against exploring on how to offer this mechanism for other types (e.g. a new CustomHashCodeOnlyAttribute), and for sure not part of this PR because the SO for list is much more visible.
Reason is, the existing mechanisms for custom types offer a solution and enforce treating GetHashCode and Equals together => for custom types which looser backwards compatibility limits, I believe it is sufficient..
The implementation follows the one for Array, which hashes based on the first 18 elements.
Note that this is not necessary, I only did it for consistency with array.