Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify / unify delaying of module / class initializers by the compiler / optimizer #112

Open
lrytz opened this issue Mar 30, 2016 · 8 comments
Assignees
Milestone

Comments

@lrytz
Copy link
Member

lrytz commented Mar 30, 2016

In some situations the compiler delays module loads, which is not according to the spec.
Examples:

  • for a case class A, A.apply is replaced by new A SI-5304
  • accessing a nested module doesn't initialize the outer module: object O { ???; object I }; O.I
  • the inliner currently does not delay module initializations when inlining a method defined in a module, the module is loaded before the inlined method body

Changing this to follow the spec is not straightforward, see discussion on the above issue, and also linked issues.

The same question arises for static initializers of java classes.
For example, the inliner currently delays class initialization when inlining a static method.

$ cat Test.java
public class Test {
  static { System.out.println("Test init"); }
  public static int m() { return 1; }
}
$ cat Test.scala
object A extends App { println(Test.m) }
$ javac Test.java
$ qsc Test.scala
$ qs A
Test init
1
$ qsc -Yopt:l:classpath -Yopt-inline-heuristics:everything Test.scala
$ qs A
1

A related question is whether the compiler can assume that a module load is always non-null, which is not the case SI-9655.
The scala-js optimizer for example makes this assumption (scala/scala#2954 (comment)).

@sjrd
Copy link
Member

sjrd commented Mar 30, 2016

I had a discussion about this with @odersky at some point, related to final vals and "inline" proposals. Speaking of final val, there's another example for you: accessing a final val does not initialize the enclosing object.

I argue that we should generally relax the spec around object initialization. My take is that the spec should guarantee that an object is never initialized before it is first used, but that it can be initialized either at that moment or any time later in the execution, or never at all.
However, that is too lax, because it does not even guarantee that

object A {
  val x: Int = 5
}

A.x

will give 5 (it could give 0). So something must be said about accessing fields, like it is guaranteed that x and its "dependencies" have been initialized when x is read. We cannot just say that the object has been initialized before any of its fields are read, because that does not account for the behavior of final vals.

It is actually a very tricky topic. So far in Scala.js we indeed assume that a module load is always non-null (by UB'ing the alternative), but otherwise the back-end and the optimizer guarantee to keep exactly the initialization semantics that scalac gives us. Including things like http://scalapuzzlers.com/#pzzlr-010

@retronym
Copy link
Member

Let's consider a "worse is better" approach of starting with a whiltelist of common stdlib modules (like Predef) that the backend is free to elide or defer initialization.

@retronym
Copy link
Member

Here's a take on this issue by @paulp, in which he proposed an annotation to mark pure modules: scala/scala#2416

@paulp
Copy link

paulp commented Feb 27, 2017

accessing a final val does not initialize the enclosing object.

This omits a key condition.

object A {
  final val x1 = 5
  final val x2: Int = 5
}

Accessing x1 does not initialize A, accessing x2 does.

@sjrd
Copy link
Member

sjrd commented Feb 28, 2017

Yes when I say "final val" I usually mean "final val without an explicit type that constant-folds and gives a constant type".

@paulp
Copy link

paulp commented Feb 28, 2017

@sjrd of course that's what I mean when I say that too, like all of us with advanced degrees in compiler implementation detail. I was just looking out for the poor drive-by programmer who might stick his head in here.

@lrytz
Copy link
Member Author

lrytz commented Mar 1, 2017

Another example where we discard module loads: https://issues.scala-lang.org/browse/SI-10209. The doc on isExprSafeToInline says

References to modules [..] are side-effecting [..] but they are safe to inline because the expression result from evaluating them is always the same

I don't really understand that thinking, as inlining discards side effects.

@lrytz
Copy link
Member Author

lrytz commented Oct 5, 2018

scala/scala#7133 allows the optimizer to delay class loading, delay initialization of core modules (Predef), treat module loads as non-null. Can be disabled using optimzier flags.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants