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?