This page is no longer maintained — Please continue to the home page at www.scala-lang.org

Strange equals implementation for case classes

3 replies
gkossakowski
Joined: 2010-03-11,
User offline. Last seen 33 weeks 5 days ago.
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

dcsobral
Joined: 2009-04-23,
User offline. Last seen 38 weeks 5 days ago.
Re: Strange equals implementation for case classes

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.

gkossakowski
Joined: 2010-03-11,
User offline. Last seen 33 weeks 5 days ago.
Re: Strange equals implementation for case classes
2011/6/2 Daniel Sobral <dcsobral@gmail.com>
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

gkossakowski
Joined: 2010-03-11,
User offline. Last seen 33 weeks 5 days ago.
Re: Strange equals implementation for case classes
2011/6/2 Daniel Sobral <dcsobral@gmail.com>
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

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland