Execute the intent (in) the variables declared in Fortran as constants also in subroutines / functions called

advertisements

In a subroutine or function an input variable can be defined with intent(in) and the compiler assures that within the subroutine the variable can not be altered. As soon as the variable is passed (by reference) to another subroutine this subroutine is able to alter the variable without compiler warning.

This was tested with gfortran with the code:

program Test
    integer i
    i = 21 ! half the truth
    call test(i)
    write (*,*) "21 expected, but is 42: ", i
end program

subroutine test(i)
    integer, intent(in) :: i
    call doSomethingNasty(i)
end subroutine

subroutine doSomethingNasty(i)
    integer :: i
    i = 42 ! set the full truth ;-)
end subroutine

My questions are:

  1. Is this the normal behaviour for all compilers?
  2. Is there a way to force the compilers to assure that the variable is really constant and that alterations would be presented as compiler errors? I mean something like the const keyword in C/C++ which is also checked against the called functions which also need to assure that the constant is treated accordingly and that no reference is escaping.
  3. I found the possibility to pass the variable to the subroutine by "value" via passing it trough an expression like test((i)). For numeric variables, this is understandable and ok, but this seems to work with gfortran for arrays, derived types and pointers, too. Does this work with other compilers, too? Is it a safe way to protect my local variables?

With sufficient compiler options gfortran generates a warning for your example, that an implicit interface is used.

If you make the interface explicit by placing the subroutines into a module, and use intents for all arguments, gfortran will catch the problem:

module mysubs

contains

subroutine test(i)
    integer, intent(in) :: i
    call doSomethingNasty(i)
end subroutine

subroutine doSomethingNasty(i)
    integer, intent (inout) :: i
    i = 42 ! set the full truth ;-)
end subroutine

end module mysubs

program Test_intent_in

use mysubs

    integer i
    i = 21 ! half the truth
    call test(i)
    write (*,*) "21 expected, but is 42: ", i

end program Test_intent_in

gfortran gives error message:

call doSomethingNasty(i)
                          1
Error: Procedure argument at (1) is INTENT(IN) while interface specifies INTENT(INOUT)

When pass the argument "(i)" you are passing an expression rather than a variable. The expression is not definable and thus should not be used as an actual argument for an "out" or "inout" dummy argument.

Another approach for argument "safety": you can also use the "value" attribute in the declaration of a dummy argument to essentially make a local copy of the argument and guarantee that the actual argument won't be altered.

Edit: As kemiisto pointed out, "contains" also makes the interface known. I don't like "contains" because the variable scoping ... all variables of the parent program are visible. Try this test code out:

PROGRAM contains_tst

  INTEGER :: i, m

  i = 21
  m = 22
  CALL test(m)

  CONTAINS

    SUBROUTINE test(j)
      INTEGER, INTENT(IN) :: j
      write (*, *) i, j
    END SUBROUTINE test

END PROGRAM contains_tst