Skip to content

Commit 7586fe7

Browse files
committed
case class hashCode implementation matches 2.13
1 parent 741c4d1 commit 7586fe7

File tree

1 file changed

+47
-17
lines changed

1 file changed

+47
-17
lines changed

compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
119119

120120
def syntheticRHS(vrefss: List[List[Tree]])(implicit ctx: Context): Tree = synthetic.name match {
121121
case nme.hashCode_ if isDerivedValueClass(clazz) => valueHashCodeBody
122-
case nme.hashCode_ => caseHashCodeBody
122+
case nme.hashCode_ => chooseHashcode
123123
case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime(vrefss.head)
124124
case nme.equals_ => equalsBody(vrefss.head.head)
125125
case nme.canEqual_ => canEqualBody(vrefss.head.head)
@@ -232,35 +232,65 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
232232
/** The class
233233
*
234234
* ```
235-
* package p
236-
* case class C(x: T, y: T)
235+
* case object C
236+
* ```
237+
*
238+
* gets the `hashCode` method:
239+
*
240+
* ```
241+
* "C".hashCode // constant folded
242+
* ```
243+
*
244+
* The class
245+
*
246+
* ```
247+
* case class C(x: T, y: U)
248+
* ```
249+
*
250+
* if non of `T` or `U` are primitive types, gets the `hashCode` method:
251+
*
252+
* ```
253+
* def hashCode: Int = ScalaRunTime._hashCode(this)
254+
* ```
255+
*
256+
* else if either `T` or `U` are primitive, gets the `hashCode` method implemented by [[caseHashCodeBody]]
257+
*/
258+
def chooseHashcode(implicit ctx: Context) = {
259+
if clazz.is(ModuleClass) then
260+
Literal(Constant(clazz.name.stripModuleClassSuffix.toString.hashCode))
261+
else if accessors `exists` (_.info.finalResultType.classSymbol.isPrimitiveValueClass) then
262+
caseHashCodeBody
263+
else
264+
ref(defn.ScalaRuntimeModule).select("_hashCode".toTermName).appliedTo(This(clazz))
265+
}
266+
267+
/** The class
268+
*
269+
* ```
270+
* case class C(x: Int, y: T)
237271
* ```
238272
*
239273
* gets the `hashCode` method:
240274
*
241275
* ```
242276
* def hashCode: Int = {
243-
* <synthetic> var acc: Int = "p.C".hashCode // constant folded
277+
* <synthetic> var acc: Int = 0xcafebabe
278+
* acc = Statics.mix(acc, this.productPrefix.hashCode);
244279
* acc = Statics.mix(acc, x);
245280
* acc = Statics.mix(acc, Statics.this.anyHash(y));
246281
* Statics.finalizeHash(acc, 2)
247282
* }
248283
* ```
249284
*/
250285
def caseHashCodeBody(implicit ctx: Context): Tree = {
251-
val seed = clazz.fullName.toString.hashCode
252-
if (accessors.nonEmpty) {
253-
val acc = ctx.newSymbol(ctx.owner, "acc".toTermName, Mutable | Synthetic, defn.IntType, coord = ctx.owner.span)
254-
val accDef = ValDef(acc, Literal(Constant(seed)))
255-
val mixes = for (accessor <- accessors) yield
256-
Assign(ref(acc), ref(defn.staticsMethod("mix")).appliedTo(ref(acc), hashImpl(accessor)))
257-
val finish = ref(defn.staticsMethod("finalizeHash")).appliedTo(ref(acc), Literal(Constant(accessors.size)))
258-
Block(accDef :: mixes, finish)
259-
} else {
260-
// Pre-compute the hash code
261-
val hash = scala.runtime.Statics.finalizeHash(seed, 0)
262-
Literal(Constant(hash))
263-
}
286+
val acc = ctx.newSymbol(ctx.owner, "acc".toTermName, Mutable | Synthetic, defn.IntType, coord = ctx.owner.span)
287+
val accDef = ValDef(acc, Literal(Constant(0xcafebabe)))
288+
val mixPrefix = Assign(ref(acc),
289+
ref(defn.staticsMethod("mix")).appliedTo(ref(acc), This(clazz).select(defn.Product_productPrefix).select(defn.Any_hashCode)))
290+
val mixes = for accessor <- accessors yield
291+
Assign(ref(acc), ref(defn.staticsMethod("mix")).appliedTo(ref(acc), hashImpl(accessor)))
292+
val finish = ref(defn.staticsMethod("finalizeHash")).appliedTo(ref(acc), Literal(Constant(accessors.size)))
293+
Block(accDef :: mixPrefix :: mixes, finish)
264294
}
265295

266296
/** The `hashCode` implementation for given symbol `sym`. */

0 commit comments

Comments
 (0)