Search code examples
cfortrancray

Different number parameters calling C routine from FORTRAN 90


I am calling a C routine from a FORTRAN 90 code. All works fine, but I am wondering why and how when I call the C routine with less parameters that I should the compiler does not complain. What is the compiler doing here? I am using a Cray compiler.

test.c

extern "C" void test_(double* x, double* y, double* z){
    // do some work
}

driver.F90

MODULE DRIVER

! declare arrays
 DOUBLE PRECISION, DIMENSION(dim, dim), INTENT(IN) :: x
 DOUBLE PRECISION, DIMENSION(dim, dim), INTENT(IN) :: y

! call C subroutine
 CALL test(x, y)

END MODULE DRIVER

Solution

  • Fortran is a lot different than C when it comes to function calls. When passing arguments to a C routine, the compiler must know the type of each argument, so that it can generate the appropriate calling sequence - either put the arguments on the stack in the correct order and with the correct padding or put them in the expected registers. C compilers usually gather this information when then compile the code if the callee is defined before the caller. In all other cases, a function declaration in the form of a prototype should be provided.

    In Fortran all arguments are typically (with some exceptions) passed by address, which means that what actually gets passed to the callee is a set of memory pointers. Pointers look the same - they are always of the same type and hence passed the same way. Therefore a Fortran compiler can generate a function call without knowing what arguments the callee is actually expecting. This greatly simplifies the Fortran compilers but is then a source of myriads of possible errors, like calling a function with the wrong argument types or even with the wrong number of arguments, to name a few. Special programs, called linters (from the name of the C programs verification utility lint), usually have to be used in order to guarantee that no such errors are present. Modern Fortran compilers also try to be much stricter than the older ones and try their best to detect errors whenever possible.

    Modern Fortran versions provide the INTERFACE construct that allows explicit declaration of function interfaces, very much similar to function prototypes in C. Module subroutines and functions get their interfaces generated automatically and made available to the callers that USE the module. When calling a subroutine/function with an explicit interface, the compiler is able to verify the validity of the call, i.e. it checks if the number of arguments and their types matches the one in the interface.

    You should provide an interface for the external routine and then the compiler would be able to perform the checks. One usually uses the ISO_C_BINDING method in order to interface to C code:

    INTERFACE
      SUBROUTINE test(x, y, z) BIND(C, NAME="test")
        USE, INTRINSIC :: ISO_C_BINDING
        REAL(KIND=C_DOUBLE), INTENT(...) :: x  ! Put the proper intents
        REAL(KIND=C_DOUBLE), INTENT(...) :: y
        REAL(KIND=C_DOUBLE), INTENT(...) :: z
      END SUBROUTINE test
    END INTERFACE
    

    With this interface in place, CALL test(x, y) would result in compile-time error because of argument count mismatch.