It was an unintentional side effect of FORTRAN's function call evaluation strategy in combination with an erroneous compiler optimization.
FORTRAN II introduced user-defined functions and subroutines with their arguments passed by reference. (Why, I don't know. It was probably more efficient than pass-by-value on IBM hardware of the time.)
Normally, pass-by-reference means you have to have to pass an l-value (like a variable) instead of an r-value. But the designers of FORTRAN decided to be helpful and let you pass r-values as arguments anyway. The compiler would automatically generate a variable for you. So, if you wrote:
CALL SUBFOO(X + Y, 4)
the compiler would convert this behind the scenes to something like
TEMP1 = X + Y
TEMP2 = 4
CALL SUBFOO(TEMP1, TEMP2)
There was also a common compiler optimization called a “literal pool”, that would consolidate multiple instances of the same numeric constant into the same auto-generated variable. (Several languages in the C family require this for string literals.) So, if you wrote
CALL SUBBAR(4)
CALL SUBBAZ(4)
this would be treated as if it were
FOUR = 4
CALL SUBBAR(FOUR)
CALL SUBBAZ(FOUR)
which seems like a perfectly reasonable thing to do until you have a subprogram that changes the value of its parameters.
SUBROUTINE SUBBAR(X)
!...lots of code...
X = 5
!...lots of code...
END SUBROUTINE SUBBAR
Boom! CALL SUBBAR(4)
changed the value of the 4 in the literal pool to a 5. And then you're left wondering why SUBBAZ
is assuming you passed it a 5 instead of the 4
you actually wrote in the code.
Newer versions of Fortran mitigate this problem by letting you declare the INTENT
of a variable as IN
or OUT
, and giving you an error (or at least a warning) if you pass a constant as an OUT
parameter.
4
for5
in the interned integers lists.4 = 5
was possible.