What is the explicit difference between the fortran intents (in,out,inout)?

13,470

Solution 1

Intents are just hints for the compiler, and you can throw that information away and violate it. Intents exists almost entirely to make sure that you only do what you planned to do in a subroutine. A compiler might choose to trust you and optimize something.

This means that intent(in) is not pass by value. You can still overwrite the original value.

program xxxx  
    integer i  
    i = 9  
    call sub(i)  
    print*,i ! will print 7 on all compilers I checked  
end  
subroutine sub(i)  
    integer,intent(in) :: i  
    call sub2(i)  
end  
subroutine sub2(i)  
    implicit none  
    integer i  
    i = 7  ! This works since the "intent" information was lost.  
end

program xxxx  
    integer i  
    i = 9  
    call sub(i)  
end  
subroutine sub(i)  
    integer,intent(out) :: i  
    call sub2(i)  
end  
subroutine sub2(i)  
    implicit none   
    integer i  
    print*,i ! will print 9 on all compilers I checked, even though intent was "out" above.  
end  

Solution 2

  • intent(in) - looks like pass by value (and changes of this are not reflected in outside code) but is in fact pass by reference and changing it is prohibited by the compiler. But it can be changed still.
  • intent(out) - pass somehow by reference, in fact a return argument
  • intent(inout) - pass by reference, normal in/out parameter.

Use intent(out) if is is plain out, to document your design. Do not care for the very little performance gain if any. (The comments suggest there is none as intent(in) is technically also pass by reference.)

Solution 3

It's not clear if parts of the OP's questions were actually answered. In addition, certainly there seems to be much confusion and various errors in the ensuing answers/discussions that may benefit from some clarifications.

A) The OP's question Re

" then I also want to know why one ever wants to use intent(out), since the intent(inout) requires less work (no copying of data)."

may not have answered, or at least too directly/correctly.

First, to be clear the Intent attributes have at least TWO purposes: "safety/hygiene", and "indirect performance" issues (not "direct performance" issues).

1) Safety/Hygiene: To assist in producing "safe/sensible" code with reduced opportunity to "mess things" up. Thus, an Intent(In) cannot be overwritten (at least locally, or even "globally" under some circumstances, see below).

Similarly, Intent(Out) requires that the Arg be assigned an "explicit answer", thus helping to reduce "rubbish" results.

For example, in the solution of perhaps the most common problem in computational mathematics, i.e. the so-called "Ax=b problem", the "direct result/answer" one is looking for is the values for the vector x. Those should be Intent(Out) to ensure x is assigned an "explicit" answer. If x was declared as, say, Intent(InOut) or "no Intent", then Fortran would assign x some "default values" (probably "zero's" in Debug mode, but likely "rubbish" in Release mode, being whatever is in memory at the Args pointer location), and if the user did not then assign the correct values to x explicitly, it would return "rubbish". The Intent(Out) would "remind/force" the user to assign values explicitly to x, and thus obviating this kind of "(accidental) rubbish".

During the solution process, one would (almost surely) produce the inverse of matrix A. The user may wish to return that inverse to the calling s/r in place of A, in which case A should be Intent(InOut).

Alternatively, the user may wish to ensure that no changes are made to the matrix A or the vector b, in which case they would be declared Intent(In), and thus ensuring that critical values are not overwritten.

2 a) "Indirect Performance" (and "global safety/hygiene"): Although the Intents are not directly for influencing performance, they do so indirectly. Notably, certain types of optimisation, and particularly the Fortran Pure and Elemental constructs, can produce much improved performance. These settings typically require all Args to have their Intent's declared explicitly.

Roughly speaking, if the compiler knows in advance the Intent's of all vars, then it can optimise and "stupidity check" the code with greater ease and effectiveness.

Crucially, if one uses Pure etc constructs, then, with high probability, there will be a "kind of global safety/hygiene" as well, since Pure/Elemental s/p's can only call other Pure/Elemental s/p's and so one CANNOT arrive at a situation of the sort indicated in "The Glazer Guy's" example.

For example, if Sub1() is declared as Pure, then Sub2() must also be declared as Pure, and then it will be required to declare the Intents at all levels, and so the "garbage out" produced in "The Glazer Guy's" example could NOT happen. That is, the code would be:

Pure subroutine sub_P(i)
    integer,intent(in) :: i
    call sub2_P(i)
end  subroutine sub_P

Pure subroutine sub2_P(i)
    implicit none
!        integer i          ! not permitted to omit Intent in a Pure s/p
    integer,intent(in) :: i
    i = 7   ! This WILL NOT WORK/HAPPEN, since Pure obviates the possibility of omitting Intent, and Intent(In) prohibits assignment ... so "i" remains "safe".
end  subroutine sub2_P

... on compile, this would produce something like

" ||Error: Dummy argument 'i' with INTENT(IN) in variable definition context (assignment) at (1)| "

Of course, sub2 need not be Pure to have i declared as Intent(In), which, again would provide the "safety/hygiene" one is looking for.

Notice that even if i was declared Intent(InOut) it would still fail with Pure's. That is:

Pure subroutine sub_P(i)
    integer,intent(in) :: i
    call sub2_P(i)
end  subroutine sub_P

Pure subroutine sub2_P(i)
    implicit none
    integer,intent(inOut) :: i
    i = 7   ! This WILL NOT WORK, since Pure obviates the possibility of "mixing" Intent's.
end  subroutine sub2_P

... on compile, this would produce something like

"||Error: Dummy argument 'i' with INTENT(IN) in variable definition context (actual argument to INTENT = OUT/INOUT) at (1)|"

Thus, strict or wide reliance on Pure/Elemental constructs will ensure (mostly) "global safety/hygiene".

It will not be possible to use Pure/Elemental etc in all cases (e.g. many mixed language settings, or when relying on external libs beyond your control, etc).

Still, consistent usage of Intents, and whenever possible Pure etc, will produce much benefit, and eliminate much grief.

One can simply get into the habit of declaring Intents everywhere all the time when it is possible, whether Pure or not ... that is the recommended coding practice.

... this also brings to the fore another reason for the existence of BOTH Intent(InOut) and Intent(Out), since Pure's must have all Arg's Intent's declared, there will be some Args that are Out only, while others are InOut (i.e. it would be difficult to have Pure's without each of In, InOut, and Out Intents).

2 b) The OP's comments expecting "performance improvements "since no copying is required" indicates a misunderstanding of Fortran and its extensive use of pass by reference. Passed by reference means, essentially, only the pointers are required, and in fact, often only the pointer to the first element in an array (plus some little hidden array info) is required.

Indeed, some insight may be offered by considering "the old days" (e.g. Fortran IV, 77, etc), when passing an array might have been coded as follows:

Real*8 A(1000)

Call Sub(A)

Subroutine Sub(A)

Real*8 A(1) ! this was workable since Fortran only passes the pointer/by ref to the first element of A(1000)
                ! modern Fortran may well throw a bounds check warning

In modern Fortran, the "equivalent" is to declare A as Real(DP) A(:) in the s/r (though strictly speaking there are various settings that benefit from passing the array's bounds and declaring explicitly with the bounds, but that would a lengthy digression for another day).

That is, Fortran does not pass by value, nor "make copies" for Args/Dummy vars. The A() in the calling s/r is the "same A" as that used in the s/r (Of course, in the s/r, one could make a copy of A() or whatever, which would create additional work/space requirements, but that is another matter).

It is for this reason primarily that the Intent's do not directly impact performance to a great extent, even for large array Arg's etc.

B) Regarding the "pass by Value" confusion: Although the various response above do confirm that using Intent is "not pass by value", it may be helpful to clarify the matter.

It may help to change the wording to "Intent is always pass by reference". This is not the same as "not pass by value", and it is an important subtlety. Notably, not only are Intents "byRef", Intent can PREVENT pass by value.

Although there are special/much more complex settings (e.g. mixed-language Fortran DLL's etc) where much additional discussion is required, for the most part of "standard Fortran", Args are passed by Ref. A demonstrations of this "Intent subtlety" can be seen in a simple extension of "The Glazer Guys" example, as:

subroutine sub(i)
    integer, intent(in) :: i, j
    integer, value     :: iV, jV
    call sub2(i)
    call sub3(i, j, jV, iV)
end
subroutine sub2(i)
    implicit none
    integer i
    i = 7  ! This works since the "intent" information was lost.
end
subroutine sub3(i, j, jV, iV)
    implicit none
    integer, value, Intent(In)          :: i    ! This will work, since passed in byRef, but used locally as byVal
    integer, value, Intent(InOut)       :: j    ! This will FAIL, since ByVal/ByRef collision with calling s/r,
                                                ! ||Error: VALUE attribute conflicts with INTENT(INOUT) attribute at (1)|
    integer, value, Intent(InOut)       :: iV   ! This will FAIL, since ByVal/ByRef collision with calling s/r,
                                                ! ... in spite of "byVal" in calling s/r
                                                ! ||Error: VALUE attribute conflicts with INTENT(INOUT) attribute at (1)|
    integer, value, Intent(Out)         :: jV   ! This will FAIL, since ByVal/ByRef collision with calling s/r
                                                ! ... in spite of "byVal" in calling s/r
                                                ! ||Error: VALUE attribute conflicts with INTENT(OUT) attribute at (1)|
    jV = -7
    iV = 7
end

That is, anything with an "Out" aspect to it must be "byRef" (at least in normal settings), since the calling s/r is expecting "byRef". Thus, even if all the s/r's declare Args as "Value", they are "byVal" only locally (again in standard settings). So, any attempt by the called s/r to return an Arg that is declared as Value with any sort of Out Intent, will FAIL due to the "collision" of the passing styles.

If it must be "Out" or "InOut" and "Value", then one cannot use Intent: which is somewhat more than simply saying "it is not pass by value".

Share:
13,470
Karl Yngve Lervåg
Author by

Karl Yngve Lervåg

Updated on June 03, 2022

Comments

  • Karl Yngve Lervåg
    Karl Yngve Lervåg almost 2 years

    After searching for a while in books, here on stackoverflow and on the general web, I have found that it is difficult to find a straightforward explanation to the real differences between the fortran argument intents. The way I have understood it, is this:

    • intent(in) -- The actual argument is copied to the dummy argument at entry.
    • intent(out) -- The dummy argument points to the actual argument (they both point to the same place in memory).
    • intent(inout) -- the dummy argument is created locally, and then copied to the actual argument when the procedure is finished.

    If my understanding is correct, then I also want to know why one ever wants to use intent(out), since the intent(inout) requires less work (no copying of data).