bug
bug copied to clipboard
Multiple nested object initialization deadlock
Repro case below
import scala.concurrent._
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
trait Foo {
def a = Inner.a
private[this] object Inner { val a = 1 }
}
trait Bar { self: Foo =>
def b = Inner.b
private[this] object Inner {
val b = Await.result(Future(a), 1.seconds)
}
}
object Baz extends Foo with Bar {
val c = 1
}
object Main extends App {
println(Baz.c)
println(Baz.b) // Throws timeout here
}
Imported From: https://issues.scala-lang.org/browse/SI-9428?orig=1 Reporter: Haney Maxwell (hmrmaxwell) Affected Versions: 2.10.4
Haney Maxwell (hmrmaxwell) said: For context, we've run into this while using the cake pattern.
@szeiger said: Assigning to 2.12.0-M5 for consideration in 2.12. Changing lazy initialization semantics would probably break binary compatibility of the generated code.
@szeiger said: This should be taken into account for http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html
still reproduces on 2.13.16
Scala 3.6.3 rejects it with
[error] ./S.scala:12:24
[error] object Inner cannot have the same name as object Inner in trait Foo -- cannot define object member with the same name as a object member in self reference self.
[error] (Note: this can be resolved by using another name)
[error] private[this] object Inner {
[error] ^
so I'm going to add the "fixed in Scala 3" label (we can remove it if someone steps forward with a Scala 3 reproduction)
compatibly
import scala.concurrent._, duration._, ExecutionContext.Implicits.global
trait Foo {
def a = InnerFoo.a
private object InnerFoo { val a = 1 }
}
trait Bar { self: Foo =>
def b = InnerBar.b
private object InnerBar {
val b = Await.result(Future(a), 1.seconds)
}
}
object Baz extends Foo with Bar {
val c = 1
}
object Main {
def main(args: Array[String]): Unit = {
//println(Baz.a) // uncomment for scala 2
println(Baz.c)
println(Baz.b) // Throws timeout here
}
}
Not sure but maybe Scala 3 works because the static method called by bootstrap is on Bar instead of InnerBar.
Scala 2 works by breaking the classloading cycle, by preloading Foo stuff.
I would have guessed something about improved lazy vals; I'm not sure if they are at par now.