Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Mix in the productPrefix hash statically in case class hashCode#11023

Merged
lrytz merged 1 commit into
scala:2.13.xfrom
lrytz:t13033b
Apr 3, 2025
Merged

Mix in the productPrefix hash statically in case class hashCode#11023
lrytz merged 1 commit into
scala:2.13.xfrom
lrytz:t13033b

Conversation

@lrytz
Copy link
Copy Markdown
Member

@lrytz lrytz commented Mar 20, 2025

Since 2.13, case class hashCode mixes in the hash code of the productPrefix string. This is inconsistent with the equals method, subclasses of case classes that override productPrefix compare equal but have a different hashCode.

This commit changes hashCode to mix in the productPrefix.hashCode statically instead of invoking productPrefix at runtime.

For case classes without primitive fields, the synthetic hashCode invokes ScalaRunTime._hashCode, which mixes in the result of productPrefix. To fix that, the synthetic hashCode is changed to invoke MurmurHash3.productHash directly and mix in the name to the seed statically. This works out with keeping productHash forwards and backwards compatible.

The MurmurHash3.productHash method is deprecated / renamed to caseClassHash. This method computes the same hash as the synthetic hashCode, except for the corner case where a case class (or a subclass) override the productPrefix. In this case, the case class name needs to be passed manually to caseClassHash.

Fixes scala/bug#13033

@scala-jenkins scala-jenkins added this to the 2.13.17 milestone Mar 20, 2025
@lrytz
Copy link
Copy Markdown
Member Author

lrytz commented Mar 20, 2025

The attemt to keep c.hashCode and MurmurHash3.productHash(c) in sync breaks because one depends on the compiler verison, the other on the runtime library version.

Not sure if that's a big issue...

Comment thread src/library/scala/util/hashing/MurmurHash3.scala Outdated
Comment thread src/compiler/scala/tools/nsc/typechecker/SyntheticMethods.scala Outdated
@SethTisue SethTisue added the release-notes worth highlighting in next release notes label Mar 20, 2025
@lrytz lrytz changed the title Mix classOf[C].getName instead of productPrefix in case hashCode Mix in the productPrefix hash statically in case class hashCode Mar 21, 2025
Comment thread src/library/scala/util/hashing/MurmurHash3.scala Outdated
Comment thread src/library/scala/util/hashing/MurmurHash3.scala Outdated
@lrytz lrytz force-pushed the t13033b branch 3 times, most recently from 11b1efa to 37a12ba Compare March 21, 2025 13:44
@lrytz lrytz force-pushed the t13033b branch 2 times, most recently from 2ea7949 to dedfce3 Compare March 21, 2025 20:11
Since 2.13, case class `hashCode` mixes in the hash code of the
`productPrefix` string. This is inconsistent with the `equals` method,
subclasses of case classes that override `productPrefix` compare
equal but have a different `hashCode`.

This commit changes `hashCode` to mix in the `productPrefix.hashCode`
statically instead of invoking `productPrefix` at runtime.

For case classes without primitive fields, the synthetic `hashCode`
invokes `ScalaRunTime._hashCode`, which mixes in the result of
`productPrefix`. To fix that, the synthetic hashCode is changed
to invoke `MurmurHash3.productHash` directly and mix in the name
to the seed statically. This works out with keeping `productHash`
forwards and backwards compatible.

The `MurmurHash3.productHash` method is deprecated / renamed to
`caseClassHash`. This method computes the same hash as the synthetic
`hashCode`, except for the corner case where a case class
(or a subclass) override the `productPrefix`. In this case, the
case class name needs to be passed manually to `caseClassHash`.
} else {
if (arr == 0)
if (!ignorePrefix) x.productPrefix.hashCode else seed
else {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rare case where I would retain extra braces (Scala 2)

def ensureAccessible(m: JMethod): JMethod = scala.reflect.ensureAccessible(m)

// This is called by the synthetic case class `toString` method.
// It originally had a `CaseClass` parameter type which was changed to `Product`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"originally" here means like first verse of Genesis

lrytz added a commit to scala/scala3 that referenced this pull request Apr 3, 2025
…22865)

Since 2.13, case class `hashCode` mixes in the hash code of the
`productPrefix` string. This is inconsistent with the `equals` method,
subclasses of case classes that override `productPrefix` compare equal
but have a different `hashCode`
(scala/bug#13033).

This commit changes `hashCode` to mix in the `productPrefix.hashCode`
statically instead of invoking `productPrefix` at runtime.

For case classes without primitive fields, the synthetic `hashCode`
invokes `ScalaRunTime._hashCode`, which mixes in the result of
`productPrefix`. To fix that, the synthetic hashCode is changed to
invoke `MurmurHash3.productHash` directly and mix in the name to the
seed statically.

Scala 3 forward port of scala/scala#11023
@lrytz lrytz merged commit 4345ced into scala:2.13.x Apr 3, 2025
hamzaremmal pushed a commit to hamzaremmal/scala3 that referenced this pull request May 2, 2025
Mix in the `productPrefix` hash statically in case class `hashCode`
hamzaremmal pushed a commit to scala/scala3 that referenced this pull request May 7, 2025
Mix in the `productPrefix` hash statically in case class `hashCode`
dongjoon-hyun pushed a commit to apache/spark that referenced this pull request Oct 13, 2025
### What changes were proposed in this pull request?
Upgrade to scala 2.13.17

### Why are the changes needed?
To bring the latest bug fixes and improvements like JDK 25 support. Note that Scala community announces two breaking changes due to the bug fixes.

> Breaking changes
> - Mix in the productPrefix hash statically in case class hashCode
> - Improve scala.util.Using suppression order

- https://github.com/scala/scala/releases/tag/v2.13.17
  - scala/scala#11046
  - scala/scala#10937
  - scala/scala#10927
    - scala/bug#13058
  - scala/scala#11023
    - scala/bug#13033
  - scala/scala#11000

### Does this PR introduce _any_ user-facing change?
No

### How was this patch tested?
local and github builds

### Was this patch authored or co-authored using generative AI tooling?
No

Closes #52509 from vrozov/SPARK-53585.

Authored-by: Vlad Rozov <[email protected]>
Signed-off-by: Dongjoon Hyun <[email protected]>
huangxiaopingRD pushed a commit to huangxiaopingRD/spark that referenced this pull request Nov 25, 2025
### What changes were proposed in this pull request?
Upgrade to scala 2.13.17

### Why are the changes needed?
To bring the latest bug fixes and improvements like JDK 25 support. Note that Scala community announces two breaking changes due to the bug fixes.

> Breaking changes
> - Mix in the productPrefix hash statically in case class hashCode
> - Improve scala.util.Using suppression order

- https://github.com/scala/scala/releases/tag/v2.13.17
  - scala/scala#11046
  - scala/scala#10937
  - scala/scala#10927
    - scala/bug#13058
  - scala/scala#11023
    - scala/bug#13033
  - scala/scala#11000

### Does this PR introduce _any_ user-facing change?
No

### How was this patch tested?
local and github builds

### Was this patch authored or co-authored using generative AI tooling?
No

Closes apache#52509 from vrozov/SPARK-53585.

Authored-by: Vlad Rozov <[email protected]>
Signed-off-by: Dongjoon Hyun <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-notes worth highlighting in next release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Overriding productPrefix breaks case class hash code

5 participants