object log {
  val b = new collection.mutable.StringBuilder
  def apply(s: Any): Unit = b.append(s)
  def check(s: String) = {
    val bs = b.toString
    assert(s == bs, bs)
    b.clear()
  }
}

package t9697 {
  abstract class WA extends DelayedInit {
    override def delayedInit(x: => Unit): Unit = x
    val waField = "4"
  }

  class C {
    def b(s: String) = log(s)
    val cField = "1"

    {
      val dummyLocal = "2"
      new WA {
        val anonField = "3"
        b(cField)
        b(dummyLocal)
        b(anonField)
        b(waField)
      }
    }
  }
}

package sd229 {
  class Broken {
    def is(ee: AnyRef) = {
      new Delayed {
        log(ee)
      }
    }
  }

  class Delayed extends DelayedInit {
    def delayedInit(x: => Unit): Unit = x
  }
}


// already fixed in 2.11.8, crashes in 2.10.6
package t4683a {
  class A { log("a") }
  class B { log("b") }
  class Bug extends DelayedInit {
    log("bug")
    def foo(a: A): B = new B
    def delayedInit(init: => Unit): Unit = init
  }
}

// already fixed in 2.12.0-RC1, crashes in 2.11.8
package t4683b {
  class Entity extends DelayedInit {
    def delayedInit(x: => Unit): Unit = x
    
    class Field
    
    protected def EntityField[T <: Entity: reflect.ClassTag] = new Field
    
    def find[T <: Entity: reflect.ClassTag]: Unit = {
      Nil.map(dbo => {
        class EntityHolder extends Entity {
          val entity = EntityField[T]
        }
      })
      log("find")
    }
  }
}

package t4683c {
  trait T extends DelayedInit {
    def delayedInit(body: => Unit) = {
      log("init")
      body
    }
  }
}

package t4683d {
  class C extends DelayedInit {
    def delayedInit(body: => Unit): Unit = body
  }
  class Injector {
    def test: Object = {
      val name = "k"
      class crash extends C {
        log(name)
      }
      new crash()
    }
  }
}

package t4683e {
  class DelayedInitTest {
    def a = log("uh")
    class B extends DelayedInit {
      a
      def delayedInit(body: => Unit): Unit = body
    }
  }
}

package t4683f {
  class Foo extends DelayedInit {
    log("fooInit")
    def delayedInit(newBody: => Unit): Unit = {
      log("delayedInit")
      inits = {
        val f = () => newBody
        if (inits == null) {
          log("initsNull")
          List(f)
        } else
          f :: inits
      }
    }
    def foo = log("foo")
    var inits: List[() => Unit] = Nil
  }
  
  class Bar extends Foo {
    log("barInit")
    def bar = foo
    def newBaz: Foo = new Baz 
    private class Baz extends Foo {
      log("bazInit")
      bar
    }
  }
}

package t4683g {
  trait MatExpWorld { self =>
    class T extends Runner { val expWorld: self.type = self }
  }
  
  trait Runner extends DelayedInit {
    def delayedInit(init: => Unit): Unit = init
    val expWorld: MatExpWorld
  }
}


object Test extends App {
  new t9697.C()
  log.check("1234")

  new sd229.Broken().is("hi")
  log.check("hi")

  val a: t4683a.A = new t4683a.A
  var b: t4683a.B = null
  new t4683a.Bug {
    val b = foo(a)
  }
  log.check("abugb")

  new t4683b.Entity().find[t4683b.Entity]
  log.check("find")

  val f = (p1: Int) => new t4683c.T { log(p1) }
  f(5)
  log.check("init5")

  new t4683d.Injector().test
  log.check("k")

  val dit = new t4683e.DelayedInitTest()
  new dit.B()
  log.check("uh")

  val fuu = new t4683f.Foo
  log.check("delayedInitinitsNull")
  fuu.inits.foreach(_.apply())
  log.check("fooInit")
  assert(fuu.inits == Nil) // the (delayed) initializer of Foo sets the inits field to Nil

  val brr = new t4683f.Bar
  log.check("delayedInitinitsNulldelayedInit") // delayedInit is called once for each constructor
  brr.inits.foreach(_.apply())
  log.check("barInitfooInit")
  assert(brr.inits == Nil)

  val bzz = brr.newBaz
  log.check("delayedInitinitsNulldelayedInit")
  bzz.inits.foreach(_.apply())
  log.check("bazInitfoofooInit")
  assert(bzz.inits == Nil)

  val mew = new t4683g.MatExpWorld { }
  val mt = new mew.T
  assert(mt.expWorld == mew)
}
