Skip to content

Make case class hashCode take class into account #2159

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

Merged
merged 4 commits into from
Apr 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -144,20 +144,21 @@ class SyntheticMethods(thisTransformer: DenotTransformer) {

/** The class
*
* package p
* case class C(x: T, y: T)
*
* gets the hashCode method:
*
* def hashCode: Int = {
* <synthetic> var acc: Int = 0xcafebabe;
* <synthetic> var acc: Int = "p.C".hashCode // constant folded
* acc = Statics.mix(acc, x);
* acc = Statics.mix(acc, Statics.this.anyHash(y));
* Statics.finalizeHash(acc, 2)
* }
*/
def caseHashCodeBody(implicit ctx: Context): Tree = {
val acc = ctx.newSymbol(ctx.owner, "acc".toTermName, Mutable | Synthetic, defn.IntType, coord = ctx.owner.pos)
val accDef = ValDef(acc, Literal(Constant(0xcafebabe)))
val accDef = ValDef(acc, Literal(Constant(clazz.fullName.toString.hashCode)))
Copy link
Member

Choose a reason for hiding this comment

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

scalac uses productPrefix (which is user definable), rather than the class name.

What is the definition of fullName for non-member classes?

Copy link
Contributor

Choose a reason for hiding this comment

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

scalac uses productPrefix (which is user definable), rather than the class name.

What's the motivation behind this?
Product prefix isn't required to even return the same value, so one can break hash-code by returning different values there.

Copy link
Member

@retronym retronym Apr 3, 2017

Choose a reason for hiding this comment

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

Neither are the case accessors (they could be vars).

I guess a motivation would be to have more stable hash codes for a local case class so that it wasn't volatile wrt fresh name suffixes. Perhaps this implementation acheives the same by inlining fullName, rather than calling this.getClass.toString).

I'm just pointing out the deviation.

Copy link
Contributor

Choose a reason for hiding this comment

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

AFAIK, The full name here won't include any fresh name suffices. They will be only added after Flatten. Two local classes with the same name will get the same constant.

Copy link
Member

Choose a reason for hiding this comment

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

Neither are the case accessors (they could be vars).

I see now, though, that a mutable productPrefix would be worse that a var accessor, as equals does not check that this.productPrefix == that.productPrefix.

val mixes = for (accessor <- accessors.toList) yield
Assign(ref(acc), ref(defn.staticsMethod("mix")).appliedTo(ref(acc), hashImpl(accessor)))
val finish = ref(defn.staticsMethod("finalizeHash")).appliedTo(ref(acc), Literal(Constant(accessors.size)))
Expand Down
8 changes: 4 additions & 4 deletions tests/run/caseClassHash.check
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
Foo(true,-1,-1,d,-5,-10,500.0,500.0,List(),5.0)
Foo(true,-1,-1,d,-5,-10,500.0,500.0,List(),5)
1383698062
1383698062
205963949
205963949
true
## method 1: 1383698062
## method 2: 1383698062
## method 1: 205963949
## method 2: 205963949
Murmur 1: 1383698062
Murmur 2: 1383698062