Forcing the inclusion of (in) declared variables in Fortran as a constant also in called routines / functions

In a subroutine or function, the input variable can be defined using the intent (in), and the compiler assures that the variable cannot be changed in the subroutine. As soon as a variable is passed (by reference) to another routine, this routine can change the variable without warning the compiler.

This has been tested with gfortran with 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:

  • Is this normal behavior for all compilers?
  • Is there a way to make compilers guarantee that the variable is really constant and that the changes will be presented as compiler errors? I mean something like the const keyword in C / C ++, which is also checked for called functions, which should also ensure that the constant is handled accordingly and that no link escapes.
  • I found the ability to pass a variable to the value subroutine by passing it through an expression like test((i)) . For numeric variables, this is understandable and normal, but it seems to work with gfortran for arrays, derived types, and pointers. Does this work with other compilers too? Is there a safe way to protect my local variables?
+6
source share
2 answers

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

If you make the interface explicit by placing the routines in the module and using the intentions for all the 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 an error message:

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

When passing the argument to ((i), you pass an expression, not a variable. The expression is undefined and therefore should not be used as the actual argument for the argument "out" or "inout".

Another approach for the security argument: you can also use the value attribute in the declaration of a dummy argument to essentially make a local copy of the argument and ensure that the actual argument is not changed.

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

 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 
+7
source

As soon as the variable is passed (by reference)

Cautionary note: Fortran does not specify how variables are passed (by reference, by value, or in any other way). It depends on the implementation. Fortran is very different from C / C ++. Better stop thinking on the C-shaped path. This will be misleading.

1) Yes and no. It depends on the implementation. First of all, the INTENT attribute indicates your intentions. As you can see in the Fortran standard, section 5.3.10, NOTE 5.17 (you can get the final draft of the so-called Fortran 2008 from the link at the top of this page http://fortranwiki.org/fortran/show/Fortran+2008 ):

The argumentation goal specifications serve several purposes in addition to documenting the intended use of dummy arguments. The processor can check if the dummy argument INTENT (IN) is used in a way that could override it. [...]

the compiler (“processor”) can (should not) check such things.

Secondly (as I already mentioned) you cannot be sure that for the argument with INTENT (IN) the compiler will select the transmission by value, and not by reference. In this case, the choice was by reference. At least it seems that I was passed by reference in the test routine. The following routine. INTENT defaults to INOUT. That is why it is possible to change the value of argument i (with unspecified what, why is INTENT by default) in doSomethingNasty. Once again I was accepted by reference. Or perhaps it was even “copy / copy”. Such freedom exists to allow the compiler to perform optimizations.

2) No. If I understand you correctly, you need something similar to permalinks (to achieve the so-called "match accuracy"). But we don’t even have links in Fortran, so obviously there are no permalinks.

3) There is a way to protect local variables. As MSB pointed out in his answer, put your routines in MODULE (or in the CONTAINS section of the main program) and always specify the INTENT attributes for the variables. I tried compiling the code below with different Fortran compilers available to me

 PROGRAM main INTEGER :: i i = 21 CALL test(i) WRITE (*,*) "21 expected, but is 42: ", i CONTAINS SUBROUTINE test(i) INTEGER, INTENT(IN) :: i CALL do_something_nasty(i) END SUBROUTINE test SUBROUTINE do_something_nasty(i) INTEGER, INTENT(INOUT) :: i i = 42 END SUBROUTINE do_something_nasty END PROGRAM main 

and all compilers (GNU, Intel, Open64, Portland, and g95) issued an error message. I think other compilers (Pathscale, IBM) will behave the same.

+4
source

Source: https://habr.com/ru/post/896090/


All Articles