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

Is this a scalac optimization bug or am I relying on undocumented behavior?

No replies
Stefan Zeiger
Joined: 2008-12-21,
User offline. Last seen 27 weeks 3 days ago.

Hi,

I've run into a problem in ScalaQuery where I use global objects for
tables which are then cloned behind the scenes to get different
instances of those tables in a query. Here's a simple snippet which
demonstrates the problem:

//----------------------------------------------------------------------------
object ObjectIdentityTest {
def main(args: Array[String]) { new ObjectIdentityTest() }

class Base extends Cloneable {
override def clone() = super.clone()
def foo = { println(" hashCode = "+this.hashCode()); true }
}

object A extends Base { def bar = List(1).filter(_ => foo) }

val B = new Base { def bar = List(1).filter(_ => foo) }
}

class ObjectIdentityTest {
import ObjectIdentityTest._

object C extends Base { def bar = List(1).filter(_ => foo) }

println("Original A:")
A.foo
A.bar

val clonedA = A.clone().asInstanceOf[A.type]
println("Cloned A:")
clonedA.foo
clonedA.bar

println("Original B:")
B.foo
B.bar

val clonedB = B.clone().asInstanceOf[B.type]
println("Cloned B:")
clonedB.foo
clonedB.bar

println("Original C:")
C.foo
C.bar

val clonedC = C.clone().asInstanceOf[C.type]
println("Cloned C:")
clonedC.foo
clonedC.bar
}
//----------------------------------------------------------------------------

The output looks like this:

Original A:
hashCode = 837043581
hashCode = 837043581
Cloned A:
hashCode = 1125883825
hashCode = 837043581 <-- this is the problem
Original B:
hashCode = 1251033058
hashCode = 1251033058
Cloned B:
hashCode = 183062162
hashCode = 183062162
Original C:
hashCode = 1894479961
hashCode = 1894479961
Cloned C:
hashCode = 1613816448
hashCode = 1613816448

For B and C this is as expected but for object A, calling bar() on a
clone calls foo() on the original object. I suspect that the closure in
bar() is constructed ahead of time and holds its own reference to the
enclosing object even though it is used in a def.

I'm not sure if this is a bug because A is supposed to be a singleton.
OTOH, I can always clone() it and get a new object which behaves
incorrectly.

I wouldn't expect such a behavior from a regular named class and indeed
that works fine. But what about the val B with the anonymous class?
That's also a singleton (unless you clone it) yet it works. The same
goes for an object inside a class (instead of inside an object or package).

So, is this a compiler bug? If not, in which cases can I safely rely on
cloned objects to work correctly?

Thanks,
-sz

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