Search code examples
fortranlocal-variablesstack-memory

Why would using uninitialized variables in Fortran ever work?


I have been working on some 30+ year old Fortran code, trying to figure out why it just intermittently doesn't work for seemingly random reasons. One set of inputs works for one user but not another user, code that works when compiled with -O1 or -O3 but seg faults with -O2, and other weird nonsense.

After about a week of picking through code that hasn't been touched for 30 years, I tracked down the problem--there are A LOT of uninitialized variables, and it's basically random whether or not the random garbage that winds up in these variables crashes the entire program. I honestly have no idea how this code ever worked.

Now that I go back to my customer and tell them what's going on, their response is "if this is a problem why has the code worked for the last 30 years...?" Which is a fair question--why HAS this code worked for them for the last 30 years? I think I'm the first one to rebuild it from source since then, did Fortran compilers in the 90s zero out the stack or something? Why did the programmers of decades ago think it reasonable to put variables on the right-hand side of an assignment operator without regard for whether those variables had been initialized first?


Solution

  • Retired Fortran compiler developer here. The reason it (generally) used to work is that prior to Fortran 90, local variables could be static, not on the stack, and linkers typically initialized static data to zero. Yes, there were FORTRAN 77 compilers that supported stack allocation and recursion, but you could write a completely standard-conforming compiler without stack allocation.

    Because "it worked", it was presumed that this was the correct approach by programmers. It's also why so many F90+ compilers had to add an option to zero-initialize variables (or to make all local variables SAVE, another thing the standard didn't say), to make old broken code continue to work.

    There are many other things programmers did that were not valid but "worked", such as deliberately accessing arrays out of bounds, mismatching argument types, jumping into the middle of loops, etc., etc. As compilers got more advanced, many of these "tricks" stopped working, and users complained. I long since lost count of the times I had to tell users that, no, the compiler didn't break their code, it was always broken.