Yes, it’s might be unintuitive, but it’s mostly due to code transformation happening to top-level statements.
However, it is possible to check proper initialisation of these under Scala 3.4 using -Ysafe-init-global (do not confuse it with -Ysafe-init existing in all Scala 3 releases). Just be aware that these 2 are still an experimental flags, and might sometimes lead to false-positives
> scala-cli run main.scala -O -Ysafe-init -Ysafe-init-global -S 3.4
[warn] ./main.scala:2:1
[warn] Access uninitialized field value o. Calling trace:
[warn] ├── @main def pdt={} [ main.scala:1 ]
[warn] │ ^
[warn] ├── val o=C() [ main.scala:5 ]
[warn] │ ^^^
[warn] └── class C extends o.CI: [ main.scala:2 ]
[warn] ^
fwiw, note that Scala 2 errors on this with “illegal cyclic reference involving class C”
I don’t see how it matters here that top-level statements are involved in the originally posted code. Scala 3.4.0 still errors at runtime if you wrap the whole thing to not be top-level anymore:
scala> object O:
| class C extends o.CI:
| class CI
| val o=C()
|
// defined object O
scala> O.o
java.lang.NullPointerException
... 35 elided
I don’t see how it matters here that top-level statements are involved in the originally posted code
I simply didn’t want to get into the details. Top-level statement are converted to an object, like the one you presented above, so it’s expected that is still fails (it also gives the same warnings under -Ysafe-init-global).
I believe it would be very beneficial from the users perspective that stuff like -Ysafe-init-global would be enabled by default in Scala 3 once they’re stable.
I also didn’t want to get into the details, but I was curious about why the exception has no message text. First, though, I was curious about the Scala 3 REPL wrapping because of the clinit in the OP stack trace. Then, second, my curiosity evaporated. I guess it’s lazy module val all the way down? but it’s not trivial to ask the REPL to tell me directly. In Scala 2, I would :javap to verify that I’m not misreading -Vprint trees.
My clever remark was going to be that it’s not necessary to “wrap” inside a REPL session, which always wraps (somehow).
Welcome to Scala 3.4.2-RC1-bin-SNAPSHOT-git-e54be6e (21.0.2, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> class C extends o.CI:
| class CI
| val o=C()
java.lang.NullPointerException
... 35 elided
scala> throw null
java.lang.NullPointerException: Cannot throw exception because "null" is null
... 33 elided
scala> null.asInstanceOf[String].length
java.lang.NullPointerException: Cannot invoke "String.length()" because "null" is null
... 33 elided
Anyway, it’s reassuring to know that, at the end of the day,