bug icon indicating copy to clipboard operation
bug copied to clipboard

Multiple nested object initialization deadlock

Open scabug opened this issue 10 years ago • 6 comments

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
}

scabug avatar Aug 05 '15 20:08 scabug

Imported From: https://issues.scala-lang.org/browse/SI-9428?orig=1 Reporter: Haney Maxwell (hmrmaxwell) Affected Versions: 2.10.4

scabug avatar Aug 05 '15 20:08 scabug

Haney Maxwell (hmrmaxwell) said: For context, we've run into this while using the cake pattern.

scabug avatar Aug 05 '15 20:08 scabug

@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.

scabug avatar Apr 07 '16 17:04 scabug

@szeiger said: This should be taken into account for http://docs.scala-lang.org/sips/pending/improved-lazy-val-initialization.html

scabug avatar May 18 '16 11:05 scabug

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)

SethTisue avatar Feb 25 '25 20:02 SethTisue

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.

som-snytt avatar Feb 26 '25 08:02 som-snytt