Aladdin - Scala Bugtracking
[#734] project: specification priority: low category: bug
submitter assigned to status date submitted
Sean Martin open 2006-09-07 11:51:38.0
subject Field initialization order inconsistent in mixin compositions.
code
class Global {
  object NoSymbol;
  object analyzer extends Analyzer {
    val global : Global.this.type = Global.this;
  }
}
trait Analyzer {
  val global : Global;
  val initial = global.NoSymbol;
}
class Other {
  val global : Global = new Global;
  object analyzer extends Analyzer {
    val global : Other.this.global.type = Other.this.global;
  }
}
object Main {
  def main(args : Array[String]) : Unit = {
    val globalx = new Global;
    Console.println(globalx.analyzer.initial);
    val other = new Other;
    Console.println(other.analyzer.initial);
  }
}
what happened
When we run main:
test.Global$NoSymbol$@337d0f
Exception in thread "main" java.lang.NullPointerException
	at test.Analyzer$class.$init$(Analyzer.scala:5)
	at test.Other$analyzer$.(Other.scala:5)
	at test.Main$$anon$0.analyzer(Main.scala:7)
	at test.Main$.main(Main.scala:10)
	at test.Main.main(Main.scala)
It turns out that the global val in Analyzer is initialized first or last dependent on whether it is located ins\ ide Global or not. Here is the Javap output for Global.analyzer's constructor:
public test.Global$analyzer$(test.Global);
  Code:
   0:	aload_1
   1:	aconst_null
   2:	pop
   3:	aconst_null
   4:	if_acmpeq	26
   7:	aload_0
   8:	aload_1
   9:	putfield	#15; //Field $outer:Ltest/Global;
   12:	aload_0
   13:	aload_1
   14:	putfield	#17; //Field global:Ltest/Global;
   17:	aload_0
   18:	invokespecial	#22; //Method java/lang/Object."":()V
   21:	aload_0
   22:	invokestatic	#28; //Method test/Analyzer$class.$init$:(Ltest/Analyzer;)V
   25:	return
   26:	new	#30; //class java/lang/NullPointerException
   29:	dup
   30:	invokespecial	#31; //Method java/lang/NullPointerException."":()V
   33:	athrow
And here is the Javap output for Other.analyzer's constructor:
public test.Other$analyzer$(test.Other);
  Code:
   0:	aload_1
   1:	aconst_null
   2:	pop
   3:	aconst_null
   4:	if_acmpeq	29
   7:	aload_0
   8:	aload_1
   9:	putfield	#16; //Field $outer:Ltest/Other;
   12:	aload_0
   13:	invokespecial	#21; //Method java/lang/Object."":()V
   16:	aload_0
   17:	invokestatic	#27; //Method test/Analyzer$class.$init$:(Ltest/Analyzer;)V
   20:	aload_0
   21:	aload_1
   22:	invokevirtual	#32; //Method test/Other.global:()Ltest/Global;
   25:	putfield	#34; //Field global:Ltest/Global;
   28:	return
   29:	new	#36; //class java/lang/NullPointerException
   32:	dup
   33:	invokespecial	#37; //Method java/lang/NullPointerException."":()V
   36:	athrow
what expected I don't see an explanation for this differing behavior in the specification. We should initialize fields always either before or after mixin initializers are called.
[back to overview]
Changes of this bug report
Martin  edited on  2006-09-12 12:28:38.0
It's complicated. The spec says valdefs are initialized after superconstructor calls. But if we did this, many classes would break (most of nsc, for instance). We need to revisit this with a more general solution, such as lazy field initialization.
Martin  edited on  2006-12-22 13:04:28.0