I like this integer example better than the logical example because for the integer every random bit pattern results in a valid integer value, but in the logical case only two of those patterns result in valid fortran logical values. And that is what you are seeing here (in both cases), random (meaning arbitrary in this context) bit patterns.
There could be two things that are occuring, maybe even both together. First, the compiler sees
subroutine foo(k)
integer , intent(out) :: k
end subroutine foo
The intent(out) tells the compiler that it can ignore any incoming value of k. It can reset it to zero, it can keep its original value, it can reset it to whatever is in the contents of register 0 at that moment, it can examine an environment variable, and so on. It can do whatever it wants. The fortran way of describing that state is that the value is undefined. Maybe the compiler does something different with different options. Maybe there is an option to initialize values to zero, or to some other given bit pattern. Maybe the behavior depends on an optimization level option. Those things are all allowed because those options do not violate any requirements in the standard. But regardless, the value is undefined from the perspective of fortran.
The standard does define in other situations what happens with intent(out) declarations. To give one example, if the dummy argument is allocatable, then it is deallocated upon entry to the subroutine. If it is a complicated object, say a linked list with a million levels, that simple declaration then results in the recursive deallocations of all subobjects within that derived type. So intent(out) should not be taken as a benign, passive, attribute in general, it should be regarded as active, and meaningful, with purpose.
Ok, then the second thing that the compiler sees is
k = 55
call foo(k)
The compiler has the explicit interface for foo(), so it knows that the argument is intent(out). Let’s assume for the moment that the module is compiled separately from the main program, so it does not know what is within foo(), but only its interface. So just from that interface, it knows that foo() is allowed to ignore the incoming value of k and return something else. That something else can include an undefined value, that possibility is allowed. So if the value of k is immediately overwritten by foo(), why should it assign the value just before? It knows that it doesn’t need to do that, that statement can be ignored, and it can be ignored at any optimization level. It is the same as, for example
k = 55
k = 42
The compiler knows that the initial assignment has no consequences, so it is allowed to ignore it at any optimization level.
How can the programmer know if a statement like that is removed? I think only by looking at the compiled code (either intermediate code or the assembler code), or by using a debugger to step through the code. However that examination occurs, the compiler is allowed to remove dead code, and this is a type of dead code. You might think you could change the code to look at the value:
k = 55
write(*,*) k
k = 42
But that changes the program, and now the first assignment is no longer dead – the assignment must either occur as written or the program must behave “as if” it is executed that way. To give an example of the latter, this last code could be executed as
write(*,*) 55
k = 42
That is, the value of 55 could be sent directly to the i/o library without it ever being placed into the memory location associated with the variable k. And further, this behavior could occur with any level of optimization, the language standard does not care as long as it behaves “as if” the original code would behave.
So that is what I meant when I said that optimization was a red herring. It is something that is irrelevant, a distraction, something to lead you astray as you search for the problem. This is not an error introduced by the compiler optimization, rather the real issue is that the return value from the subroutine is undefined.
A common expression in fortran when the program behavior is undefined in some way is that “anything can happen, it could even start WW III”. Printing an undefined value in fortran is like that, it could even start WW III. These days, that expression doesn’t seem very funny.