Many Fortran compilers provide the AUTOMATIC and STATIC statements and attributes to explicitly define when a variable should be shared between invocations of a subroutine and when they should be allocated to the stack each time a subroutine is invoked.
Is there an equivalent in the ISO Fortran standards (any of them)?
Fortran since FORTRAN 77 has the SAVE statement (and a SAVE attribute since Fortran 90) to share the variables between invocations. Automatic variables can be forced by declaring the procedure recursive (Fortran 90).
Related
I'm working with an older Fortran code using subroutine calls like this:
call sub(A,,C)
With the corresponding subroutine of the form:
subroutine sub(A,B,C)
So in practice the argument B is optionally omitted here.
However, I am now creating explicit interfaces via the compiler options /warn:interface /gen-interface. I do this to debug interface errors in the code. This results in the following error (from Intel Visual Fortran compiler 19.1.0057.16):
error #8127: A null argument is not permitted when calling a Fortran routine which has an explicit interface defined. [sub]
I could make the argument B optional and move it to the end of the argument list. This would involve updating a lot of function calls. Also, I am not sure if it works in general, as e.g. several arguments may be omitted in this way in different combinations for different function calls.
What is the correct way to modernise the above code to work correctly with explicit interfaces?
Edit: The use of two consecutive commas (,,) is also a deprecated (?) language feature denoting a null data item. From the Oracle Fortran 77 language reference:
A null data item is denoted by two consecutive commas, and it means the corresponding array element or complex variable value is not to be changed. Null data item can be used with array elements or complex variables only. One null data item represents an entire complex constant; you cannot use it for either part of a complex constant.
In the comments below it was noted that this relates to the data input process, and does not apply to my problem.
The Compaq Fortran for OpenVMS manual (Compaq Fortran is a predecesor of Intel Fortran, but OpenVMS is a different operating system) has this:
If you do not want to specify a value for a required parameter, you
can pass a null argument by inserting a comma (,) as a placeholder in
the argument list. If the routine requires any passing mechanism other
than the default, you must specify the passing mechanism in the CALL
statement or the function call.
My understanding is that this feature would most likely be useful when calling procedures created in other programming languages. For example various system calls or other calls to procedures written in C. Such are also the examples offered by the Compaq Fortran manual. You can also see it in the VSI Fortran for OpenVMS manual.
The Oracle Fortran manual calls this a Fortran 77 feature accessible with a -f77 flag:
Allow null actual arguments. For example: CALL FOO(I,,,J) has two null
arguments between the first I and the final J argument.
But this is not and never has been legal Fortran. Under the hood a null pointer is passed instead if the address of an actual argument.
The way to modernize it is to go to Fortran 90 and later and use optional arguments.
subroutine sub(A,B,C)
...
real, optional :: B
In Fortran 2018 this can also be used for procedures written in other programming languages thanks to the Fortran-C interoperability and bind(C). One defines it appropriately in the interface block for such a procedure.
The call with an actual argument with B not present looks like:
call SUB(A_actual,C=C_actual)
These are the named ("keyword") arguments, also introduced in Fortran 90. You can also use them when all actual arguments are present.
E.g., Fortran 90 function with no arguments? How does FORTRAN interact with optional arguments?
In fortran 95, if you assign a variable at declaration
integer :: var = 0
it is equivalent to
integer, save :: var = 0
and the variable is therefore preserved after routine execution (is equivalent to static in C speak) and does not get reinitialized when called again. What is the rationale/technical issue behind such (IMHO dangerous) behavior ?
I don't think that there is some rationale behind such behavior.
But as far as I know, Stefano, you used wrong terminology. In your code there is no assignment statement only variable (var) initialization using initialization expression (0).
integer :: var = 0 ! type declaration & initialization
integer :: var ! type declaration
var = 0 ! assignment
So it seems that it was just committee design decision. If we have such expression (with equality sign in type declaration statement) it is initialization not assignment. And initialization takes place only once during program (and not procedures) execution.
However there might be some historical reasons for such decision. Take a look at this thread.
Today such behavior is dangerous because many other widely used languages follows another conventions about initialization/assignment.
Many old FORTRAN 77 and earlier compilers statically allocated all variables. Many programmers relied upon this behavior -- this was technically a bug in their programs since unless they used the "SAVE" qualifier in the declaration (or added a plain SAVE statement to every procedure) the value of the variable was undefined upon reentry to a procedure. But since in those days programs tended to be tied to a particular platform and compiler for years, programmers got away with this. This is a very common "gotcha" in porting legacy FORTRAN 77 code to a modern Fortran >= 90 compilers. Most compilers provide compile-time switches to restore this behavior, such as the fno-automatic option of gfortran. Most likely the committee viewed variables that were initialized in their declaration as very likely to need the SAVE attribute -- in my opinion, a reasonable design decision. I think what is most different from other languages, and easiest to confuse the multi-lingual programmer, is that the initialization is done only once.
I am a newbie to Fortran. Please look at the code below:
c main program
call foo(2)
print*, 2
stop
end
subroutine foo(x)
x = x + 1
return
end
In some implementations of Fortran IV, the above code would print a 3. Why is that? Can you suggest an explanation?
How do you suppose more recent Fortran implementations get around the problem?
Help is very much appreciated. Thank You.
The program breaks the language rules - the dummy argument x in the subroutine is modified via the line x = x + 1, but it is associated with something that is an expression (a simple constant). In general, values that result from expressions cannot be modified.
That specific code is still syntactically valid Fortran 2008. It remains a programming error in Fortran 2008 - as it was in Fortran IV/66. This isn't something that compilers are required to diagnose. Some may, perhaps with additional debugging options, and perhaps not till runtime.
Because the program breaks the language rules anything could happen when you run the program. Exactly what depends on the code generated by the compiler. Compilers may have set aside modifiable storage for the value that results from the expression such that it internally looks like a variable (the program might print three and the program carries on), that modifiable storage might be shared across the program for other instances of the constant 2 (suddenly the value of 2 becomes three everywhere!), the storage for the value of the constant might in non-modifiable memory (the program may crash), the compiler may issue an error message, the program may get upset and sulk in its bedroom, the program might declare war on a neighbouring nation - it is a programming error - what happens is unspecified.
As of Fortran 90, facilities were introduced into the language to allow programmers to write new code that is practical for compilers to check for errors such as these (and in some cases compilers are required to check for errors if they are to be regarded as standard conforming).
For the code as presented, the main program and the subroutine are to be regarded as separately compiled - the main program is unaware of the details of the subroutine and vice versa (it is possible that the subroutine could be compiled long after the main program, on a different machine, with the outputs of the two being linked together at some later stage - without fancy link time behaviour or static analysis it is therefore not possible to resolve errors such as this). Language rules are such that when compiling the main program the compiler must implicitly assume the details of the interface of the subroutine based only on the way the subroutine is referenced - inside the main program the subroutine has an implicit interface.
Fortran 90 introduced the concept of an explicit interface, where the compiler is explicitly told what the interface of the subroutine in various ways, and can then check that any reference to the subroutine is consistent with that interface. If a procedure is a module procedure, internal procedure or intrinsic procedure - that interface is automatically realized, alternatively for external subprograms, procedure pointers, etc, the programmer can explicitly describe the interface using an interface block.
In addition, Fortran 90 introduced the intent attribute - a characteristic of a dummy argument of a procedure that is also then a characteristic of the interface for a procedure. The intent of the argument indicates to the compiler whether the procedure may define the argument (it also may implications for default initialization and component allocation status) and hence whether an expression could be a valid actual argument. x in subroutine foo would typically be declared INTENT(INOUT).
Collectively these new language features provide a robust defence against this sort of programming error when using compilers with a basic level of implementation quality. If you are starting with the language then it is recommended that these new features become part of your standard approach - i.e. use implicit none, all procedures should generally be module procedures or internal procedures, use external procedures only when absolutely required, always specify dummy argument intent, use free form source.
from the source code published in the programmer's manual of a commercial program, I have isolated a code snippet which puzzles me quite a lot.
The function below is expected to be called multiple times by a kernel and is supposed to implement the temporal behaviour of a component in a system consisting of many interconnected components (I have removed the Input/Output parameters from the function prototype because they are irrelevant to the point I intend to rise).
To distinguish between different instances of the same block type the kernel pass an instance number in the INFO(1) element.
As far as I have understood, the designer of this program took a great deal of effort trying to save the time spent in copying the values of the parameters from the PAR vector to local variables with meaningful names at each call (as if they were not aware of the optimizations a compiler can do). It seems to me that they wanted to assign them to the local variables only in the first call, or when the caller switch to a different instance of the same type.
However I can't understand how this could work if the local variables are not declared static with the "save" keyword. Does FORTRAN store local variables statically i.e. not on a stack? (I am sorry if the question sounds stupid, I am used to the C/C++ languages)
Thank you.
SUBROUTINE TYPE151(PAR, INFO, *)
IMPLICIT NONE
INTEGER*4 INFO(15), IUNIT
DOUBLE PRECISION PAR, QMAX
PARAMETER (NP=1)
DIMENSION PAR(NP)
! First call
IF (INFO(7).EQ.-1) THEN
IUNIT = INFO(1)
QMAX = PAR(1)
RETURN 1
ENDIF
! later calls
IF(INFO(1).NE.IUNIT) THEN
IUNIT = INFO(1)
QMAX = PAR(1)
ENDIF
! Making use of QMAX in some ways...
RETURN 1
END SUBROUTINE TYPE151
Storage methods are not part of the language standard. Old FORTRAN compilers (FORTRAN 77 and earlier) frequently stored all variables statically. The language requires that you use "SAVE" for variables for which the values should be retained across calls to the procedure. But many programmers ignored this requirement and relied on the behavior that all variables retained their values because of the typical design of compilers in the FORTRAN 77 era.
Modern Fortran compilers typically use memory differently and local variables of procedures do not always retain their values if SAVE is omitted. This frequently causes bugs when old programs are compiled with current compilers. Compilers typically provide an option to restore the old behavior. Otherwise it could be a great deal of work to identify all variables in a large legacy program that needed to have the SAVE attribute added to their declaration.
In fortran 95, if you assign a variable at declaration
integer :: var = 0
it is equivalent to
integer, save :: var = 0
and the variable is therefore preserved after routine execution (is equivalent to static in C speak) and does not get reinitialized when called again. What is the rationale/technical issue behind such (IMHO dangerous) behavior ?
I don't think that there is some rationale behind such behavior.
But as far as I know, Stefano, you used wrong terminology. In your code there is no assignment statement only variable (var) initialization using initialization expression (0).
integer :: var = 0 ! type declaration & initialization
integer :: var ! type declaration
var = 0 ! assignment
So it seems that it was just committee design decision. If we have such expression (with equality sign in type declaration statement) it is initialization not assignment. And initialization takes place only once during program (and not procedures) execution.
However there might be some historical reasons for such decision. Take a look at this thread.
Today such behavior is dangerous because many other widely used languages follows another conventions about initialization/assignment.
Many old FORTRAN 77 and earlier compilers statically allocated all variables. Many programmers relied upon this behavior -- this was technically a bug in their programs since unless they used the "SAVE" qualifier in the declaration (or added a plain SAVE statement to every procedure) the value of the variable was undefined upon reentry to a procedure. But since in those days programs tended to be tied to a particular platform and compiler for years, programmers got away with this. This is a very common "gotcha" in porting legacy FORTRAN 77 code to a modern Fortran >= 90 compilers. Most compilers provide compile-time switches to restore this behavior, such as the fno-automatic option of gfortran. Most likely the committee viewed variables that were initialized in their declaration as very likely to need the SAVE attribute -- in my opinion, a reasonable design decision. I think what is most different from other languages, and easiest to confuse the multi-lingual programmer, is that the initialization is done only once.