- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Strange equals implementation for case classes
Thu, 2011-06-02, 12:35
Hi,
mac-grek:caseclass grek$ scalac -versionScala compiler version 2.9.0.1 -- Copyright 2002-2011, LAMP/EPFLmac-grek:caseclass grek$ cat A.scala case class A(x: Int) mac-grek:caseclass grek$ scalac A.scala -Xprint:cleanup[[syntax trees at end of cleanup]]// Scala source: A.scalapackage <empty> { case class A extends java.lang.Object with ScalaObject with Product with Serializable { //not interesting stuff removed
override def equals(x$1: java.lang.Object): Boolean = A.this.eq(x$1).||({ { <synthetic> val temp1: java.lang.Object = x$1; if (temp1.$isInstanceOf[A]()) { <synthetic> val temp2: A = temp1.$asInstanceOf[A](); <synthetic> val temp3: Int = temp2.x(); val x$1: Int = temp3; if (A.this.gd1$1(x$1)) body%01(x$1){ x$1.$asInstanceOf[A]().canEqual(A.this) //this line looks suspicious } else { false } } else { false } } });
Let's check that LabelDef in detail:
mac-grek:caseclass grek$ scalac A.scala -Xprint:cleanup -Yshow-trees LabelDef( body%01 Ident("x$1") // sym=value x$1, sym.owner=method equals, sym.tpe=Int, tpe=Int, tpe.sym=class Int, tpe.sym.owner=package scala ) Apply( // sym=method canEqual, tpe=Boolean, tpe.sym=class Boolean, tpe.sym.owner=package scala Select( // sym=method canEqual, sym.owner=trait Equals, sym.tpe=(that: java.lang.Object)Boolean, tpe=(that: java.lang.Object)Boolean, tpe.sym=<none> Apply( // sym=method $asInstanceOf, tpe=A, tpe.sym=class A, tpe.sym.owner=package <empty>, tpe.decls=List(value x: ()Int, , value x: Int, , constructor A: (x: Int)A, , method copy: (x: Int)A, , method copy$default$1: ()Int, , method hashCode: ()Int, , method toString: ()java.lang.String, , method equals: (x$1: java.lang.Object)Boolean, , method productPrefix: ()java.lang.String, , method productArity: ()Int, , method productElement: (x$1: Int)java.lang.Object, , method canEqual: (x$1: java.lang.Object)Boolean, , method gd1$1: (x$1: Int)Boolean, , method productIterator: ()Iterator, , method productElements: ()Iterator, ) TypeApply( // sym=method $asInstanceOf, tpe=()A, tpe.sym=<none> Select( // sym=method $asInstanceOf, sym.owner=class Object, sym.tpe=[T0 >: ? <: ?]()T0, tpe=[T0 >: ? <: ?]()T0, tpe.sym=<none> Ident("x$1"), // sym=value x$1, sym.owner=method equals, sym.tpe=java.lang.Object, tpe=java.lang.Object, tpe.sym=class Object, tpe.sym.owner=package lang, "$asInstanceOf"), List( TypeTree() // sym=class A, tpe=A, tpe.sym=class A, tpe.sym.owner=package <empty>, tpe.decls=List(value x: ()Int, , value x: Int, , constructor A: (x: Int)A, , method copy: (x: Int)A, , method copy$default$1: ()Int, , method hashCode: ()Int, , method toString: ()java.lang.String, , method equals: (x$1: java.lang.Object)Boolean, , method productPrefix: ()java.lang.String, , method productArity: ()Int, , method productElement: (x$1: Int)java.lang.Object, , method canEqual: (x$1: java.lang.Object)Boolean, , method gd1$1: (x$1: Int)Boolean, , method productIterator: ()Iterator, , method productElements: ()Iterator, ) ) ), Nil // no argument ), "canEqual"), List( // 1 arguments(s) This("A") // sym=class A, sym.owner=package <empty>, sym.tpe=A, tpe=A, tpe.sym=class A, tpe.sym.owner=package <empty>, tpe.decls=List(value x: ()Int, , value x: Int, , constructor A: (x: Int)A, , method copy: (x: Int)A, , method copy$default$1: ()Int, , method hashCode: ()Int, , method toString: ()java.lang.String, , method equals: (x$1: java.lang.Object)Boolean, , method productPrefix: ()java.lang.String, , method productArity: ()Int, , method productElement: (x$1: Int)java.lang.Object, , method canEqual: (x$1: java.lang.Object)Boolean, , method gd1$1: (x$1: Int)Boolean, , method productIterator: ()Iterator, , method productElements: ()Iterator, ) ) ) )
We can see that parameter to label def is of type Int:Ident("x$1") // sym=value x$1, sym.owner=method equals, sym.tpe=Int, tpe=Int, tpe.sym=class Int, tpe.sym.owner=package scala
and the rest of the output confirms us that this LabelDef is trying to cast Int into A class which doesn't make sense.
What I'm missing here?
--
Grzegorz Kossakowski
mac-grek:caseclass grek$ scalac -versionScala compiler version 2.9.0.1 -- Copyright 2002-2011, LAMP/EPFLmac-grek:caseclass grek$ cat A.scala case class A(x: Int) mac-grek:caseclass grek$ scalac A.scala -Xprint:cleanup[[syntax trees at end of cleanup]]// Scala source: A.scalapackage <empty> { case class A extends java.lang.Object with ScalaObject with Product with Serializable { //not interesting stuff removed
override def equals(x$1: java.lang.Object): Boolean = A.this.eq(x$1).||({ { <synthetic> val temp1: java.lang.Object = x$1; if (temp1.$isInstanceOf[A]()) { <synthetic> val temp2: A = temp1.$asInstanceOf[A](); <synthetic> val temp3: Int = temp2.x(); val x$1: Int = temp3; if (A.this.gd1$1(x$1)) body%01(x$1){ x$1.$asInstanceOf[A]().canEqual(A.this) //this line looks suspicious } else { false } } else { false } } });
Let's check that LabelDef in detail:
mac-grek:caseclass grek$ scalac A.scala -Xprint:cleanup -Yshow-trees LabelDef( body%01 Ident("x$1") // sym=value x$1, sym.owner=method equals, sym.tpe=Int, tpe=Int, tpe.sym=class Int, tpe.sym.owner=package scala ) Apply( // sym=method canEqual, tpe=Boolean, tpe.sym=class Boolean, tpe.sym.owner=package scala Select( // sym=method canEqual, sym.owner=trait Equals, sym.tpe=(that: java.lang.Object)Boolean, tpe=(that: java.lang.Object)Boolean, tpe.sym=<none> Apply( // sym=method $asInstanceOf, tpe=A, tpe.sym=class A, tpe.sym.owner=package <empty>, tpe.decls=List(value x: ()Int, , value x: Int, , constructor A: (x: Int)A, , method copy: (x: Int)A, , method copy$default$1: ()Int, , method hashCode: ()Int, , method toString: ()java.lang.String, , method equals: (x$1: java.lang.Object)Boolean, , method productPrefix: ()java.lang.String, , method productArity: ()Int, , method productElement: (x$1: Int)java.lang.Object, , method canEqual: (x$1: java.lang.Object)Boolean, , method gd1$1: (x$1: Int)Boolean, , method productIterator: ()Iterator, , method productElements: ()Iterator, ) TypeApply( // sym=method $asInstanceOf, tpe=()A, tpe.sym=<none> Select( // sym=method $asInstanceOf, sym.owner=class Object, sym.tpe=[T0 >: ? <: ?]()T0, tpe=[T0 >: ? <: ?]()T0, tpe.sym=<none> Ident("x$1"), // sym=value x$1, sym.owner=method equals, sym.tpe=java.lang.Object, tpe=java.lang.Object, tpe.sym=class Object, tpe.sym.owner=package lang, "$asInstanceOf"), List( TypeTree() // sym=class A, tpe=A, tpe.sym=class A, tpe.sym.owner=package <empty>, tpe.decls=List(value x: ()Int, , value x: Int, , constructor A: (x: Int)A, , method copy: (x: Int)A, , method copy$default$1: ()Int, , method hashCode: ()Int, , method toString: ()java.lang.String, , method equals: (x$1: java.lang.Object)Boolean, , method productPrefix: ()java.lang.String, , method productArity: ()Int, , method productElement: (x$1: Int)java.lang.Object, , method canEqual: (x$1: java.lang.Object)Boolean, , method gd1$1: (x$1: Int)Boolean, , method productIterator: ()Iterator, , method productElements: ()Iterator, ) ) ), Nil // no argument ), "canEqual"), List( // 1 arguments(s) This("A") // sym=class A, sym.owner=package <empty>, sym.tpe=A, tpe=A, tpe.sym=class A, tpe.sym.owner=package <empty>, tpe.decls=List(value x: ()Int, , value x: Int, , constructor A: (x: Int)A, , method copy: (x: Int)A, , method copy$default$1: ()Int, , method hashCode: ()Int, , method toString: ()java.lang.String, , method equals: (x$1: java.lang.Object)Boolean, , method productPrefix: ()java.lang.String, , method productArity: ()Int, , method productElement: (x$1: Int)java.lang.Object, , method canEqual: (x$1: java.lang.Object)Boolean, , method gd1$1: (x$1: Int)Boolean, , method productIterator: ()Iterator, , method productElements: ()Iterator, ) ) ) )
We can see that parameter to label def is of type Int:Ident("x$1") // sym=value x$1, sym.owner=method equals, sym.tpe=Int, tpe=Int, tpe.sym=class Int, tpe.sym.owner=package scala
and the rest of the output confirms us that this LabelDef is trying to cast Int into A class which doesn't make sense.
What I'm missing here?
--
Grzegorz Kossakowski
Thu, 2011-06-02, 13:17
#2
Re: Strange equals implementation for case classes
2011/6/2 Daniel Sobral <dcsobral@gmail.com>
The fact that there are two x$1 was exactly the reason for more detailed look, that reveals this line:
LabelDef( body%01 Ident("x$1") // sym=value x$1, sym.owner=method equals, sym.tpe=Int, tpe=Int, tpe.sym=class Int, tpe.sym.owner=package scala ) This shows that LabelDef takes Int as parameter thus inside od LabelDef called body%01 x$1 has type Int.
--
Grzegorz Kossakowski
On Thu, Jun 2, 2011 at 08:34, Grzegorz Kossakowski
<grzegorz.kossakowski@gmail.com> wrote:
> Hi,
> override def equals(x$1: java.lang.Object): Boolean =
> A.this.eq(x$1).||({
> <synthetic> val temp1: java.lang.Object = x$1;
> if (temp1.$isInstanceOf[A]())
> val x$1: Int = temp3;
> x$1.$asInstanceOf[A]().canEqual(A.this) //this line looks
> What I'm missing here?
There are two x$1, the outer one being definitely an A. Since this
works, I assume the compiler knows which x$1 it is talking about, but
I'd expect it to use temp1 instead, indeed.
The fact that there are two x$1 was exactly the reason for more detailed look, that reveals this line:
LabelDef( body%01 Ident("x$1") // sym=value x$1, sym.owner=method equals, sym.tpe=Int, tpe=Int, tpe.sym=class Int, tpe.sym.owner=package scala ) This shows that LabelDef takes Int as parameter thus inside od LabelDef called body%01 x$1 has type Int.
--
Grzegorz Kossakowski
Thu, 2011-06-02, 14:37
#3
Re: Strange equals implementation for case classes
2011/6/2 Daniel Sobral <dcsobral@gmail.com>
You were right. I've got confused because I was thinking that LabelDef param shadows anything from outer scope which is not true. I looked into genICode and it turns out that x$1 inside of LabelDef block points to the right symbol and LabelDef's parameter is completely ignored.
I need to fix my code that relies on wrong assumptions.
Thanks.
--
Grzegorz Kossakowski
On Thu, Jun 2, 2011 at 08:34, Grzegorz Kossakowski
<grzegorz.kossakowski@gmail.com> wrote:
> Hi,
> override def equals(x$1: java.lang.Object): Boolean =
> A.this.eq(x$1).||({
> <synthetic> val temp1: java.lang.Object = x$1;
> if (temp1.$isInstanceOf[A]())
> val x$1: Int = temp3;
> x$1.$asInstanceOf[A]().canEqual(A.this) //this line looks
> What I'm missing here?
There are two x$1, the outer one being definitely an A. Since this
works, I assume the compiler knows which x$1 it is talking about, but
I'd expect it to use temp1 instead, indeed.
You were right. I've got confused because I was thinking that LabelDef param shadows anything from outer scope which is not true. I looked into genICode and it turns out that x$1 inside of LabelDef block points to the right symbol and LabelDef's parameter is completely ignored.
I need to fix my code that relies on wrong assumptions.
Thanks.
--
Grzegorz Kossakowski
On Thu, Jun 2, 2011 at 08:34, Grzegorz Kossakowski
wrote:
> Hi,
> override def equals(x$1: java.lang.Object): Boolean =
> A.this.eq(x$1).||({
> val temp1: java.lang.Object = x$1;
> if (temp1.$isInstanceOf[A]())
> val x$1: Int = temp3;
> x$1.$asInstanceOf[A]().canEqual(A.this) //this line looks
> What I'm missing here?
There are two x$1, the outer one being definitely an A. Since this
works, I assume the compiler knows which x$1 it is talking about, but
I'd expect it to use temp1 instead, indeed.