As Erik explained, in the compiler's current model, initializers can run any time between program start and the first access to the global. There are some problems with that model that I still don't have answers to. I think we either need to come up with good answers to those problems, or implement a more conservative model as done in the PR above.
Let's just be honest that, if the compiler implements a conservative model of global initializer order, it cannot realistically be changed later in a way that violates that model. The source breakage would be unacceptable.
Problems:
- Crash on reentrant initialization
This results from cyclic dependencies on global initializers. This can only be solved by diagnostics. Static diagnostics can't catch every case, so we need a way to dynamically diagnose reentrant initialization.
The compiler's aggressive model makes this worse by exposing reentrant initialization on an access that is dynamically unreachable access X does not occur. Is it acceptable to diagnose even these cases as errors just as the compiler should for regular cyclic dependencies? Would that be too confusing? Would it prohibit any valid programming technique?
- Unspecified behavior resulting from unreachable access
Again, this is the access X does not occur case. Is it acceptable for intializer side effects to be observed even if the global is never dynamically accessed? If the answer is "no", then I suspect the answer to Problem #1 should similarly be that the compiler cannot diagnose unreachable reentrant initialization as an error.
This is especially problematic because -Onone and -O behavior will diverge, and I cannot envision any reliable diagnostic that could warn the programmer about that divergent behavior.
- Unspecified behavior resulting from reordering initialization
let a:Int = { print("a", terminator: ","); return 1 }()
func foo() {
print("foo", terminator: ",")
_ = a
}
-Onone always prints "foo,a"
-O could print "a,foo"
Use your imagination for how this might do something unexpected in a real application.
I don't like the idea of programmers relying on the order of initializer side effects.
I also don't like the idea of the compiler reordering side effects at -O unless it's possible to diagnose most cases where it would matter as a warning. Does anyone think it would ever be practical to enable such a diagnostic?