The compiler is free to leave memory uninitialized when the compiler believes that this memory won't be read anyway. Depending on how the executable is laid out in memory, which changes between versions, some memory that is later copied might hold different values across versions. The memory that eventually fails the assert is not part of the executable; it's allocated RAM during program run, but garbage values (that were not intended to be read) were copied into it, or it was left uninitialized (unlikely because we could repro the crash easily).
The assert fails because a comparison between two integers fails. Even if, in 0.9.2, one side of the comparison was already garbage, maybe the garbage still compared correctly against the other integer, passing the assert.
Symptoms of memory corruption typically sound like magic: Sometimes, bugs come and go between application runs. Sometimes, values in the application behave seemingly randomly. Sometimes, bugs manifest on one system but not on another. Sometimes, bugs might only happen in release mode (nasty), or only in debug mode (better).
This type of bug shouldn't happen in normal D code, only when you deliberately reach for dark magic, you risk this. I've written about this particular form of memory corruption in a comparison of
tagged union vs. normal OO state pattern; my 0.9.6 D code behaves like a tagged union with incorrect access.
-- Simon