- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Is this a scalac optimization bug or am I relying on undocumented behavior?
Tue, 2010-08-03, 15:38
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