Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
28445 Discussions

Must a dummy argument have intent(in), intent(inout) or intent(out)?

alexisgm
Beginner
799 Views
Hi everyone,

I'm trying to code up a recursive subroutine. These always give me headaches, and in my testing I came accross a weird example where if I specify any kind of intent for a dummy variable the compilation fails, but if I instead don't specify any intent, the program compiles and runs fine. The variable in question is the "x" variable in the code below.

I really don't understand this. Any thoughts? Thanks,

Alexis


module module1
integer:: n
contains

recursive subroutine sub1(x)
integer, intent(inout):: x !Doesn't compile with any sort of intent specifier!!!
integer:: y
y = 0
if (x < n) then
x = x + 1
y = x**2
print *, 'x = ', x,', y = ', y
call sub1(x+1)
print *, 'x = ', x,', y = ', y
end if
end subroutine sub1

end module module1

program main
use module1
integer:: x = 0
print *, 'Enter number of repeats'
read (*,*) n
call sub1(x)
end program main




0 Kudos
11 Replies
Steven_L_Intel1
Employee
799 Views

Your code is incorrect even without the INTENT - it's just that with INTENT the compiler can see that it's incorrect and complains.

The problem is that you call sub1 with an expression as the argument, and then assign to that argument with the statement:

x = x+1

That is not allowed - if the actual argument is an expression or constant (or an array section with vector subscript), you may not change the definition status of the corresponding dummy argument in the called routine.

So if you say INTENT(IN), it complains that you're assigning to an INTENT(IN). If you say INTENT(INOUT) or (OUT), it complains that you're passing an expression to an argument that may be redefined.

One solution would be to copy X into a local variable and then modify that, though I'm not sure what you have in mind for after the call to x returns.

0 Kudos
jimdempseyatthecove
Honored Contributor III
799 Views

There are a few coding problems.

One is what you mentioned. (modifying the result of an expression or literal)

A second is his statement "X=X+1" incrimented the nest level, thus not requiring a second increment attempted on the call statement.

A third problem is FORTRAN is call by reference. Therefore "X=X+1" incriments the X on all levels. To correct for thisa corisponding "X=X-1" must be included or the use of "Xnext = X+1", "call sub(Xnext)"

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
799 Views
Jim, I don't quite get your third problem. If x was passed as a variable, then I'd agree, though it seems as if that may have been the intention. But as written, an expression is passed and therefore no changes to x in the called routine are visible to the caller.
0 Kudos
alexisgm
Beginner
799 Views
Thank you for clearing things up. The code that I posted just originated from some experimentation with the purpose of understanding how recursive routines worked.

I now understand why the code isn't valid when any sort of INTENT is used. What had originally surprized my was that the code compiled and ran when I didn't include and INTENT statements, which prompted me to think that maybe I was missing something.

In general then, is it good programming practive to always include INTENT statements about all the variables passed to a routine?

Thanks for your help,

Alexis



0 Kudos
Steven_L_Intel1
Employee
799 Views
In my opinion, yes, specifying INTENT is always good. It improves error detection and can increase optimization - especially when IN or OUT is used.
0 Kudos
Jugoslav_Dujic
Valued Contributor II
799 Views
Passing expressions to modifiable dummies is
a) Prohibited by the Standard, as Steve explained
b) mostly harmless, as you discovered in the case without INTENT

The reason why it worked originally is that a typical implementation is that the compiler will appoint a new temporary stack location for the result, evaluate the expression there and send its address to the routine. Since your routine was recursive, that will happen in every its instance. Upon return, the stack location containing the expression will be deallocated.

There are many reasons why you should refrain from it though (better safe than sorry). Consider the following:

x=42
call foo(x+1) !foo modifies its argument (thus it's illegal).
call bar(x+1)

having seen x+1 repeatedly, the compiler might decide to evaluate x+1 only once, and reuse the location for the subsequent call to bar. If foo modifies its argument, the actual argument to bar will not be 43...

0 Kudos
jimdempseyatthecove
Honored Contributor III
799 Views

Steve,

The problem correlates to your observation and suggestion that since the argument X is being modified by the recursive subroutine that the programmer should not use an expression on the call statement. You also mentioned not using a literal nor other potential problem makers.

In the case of the expression as the as (to be) modified argument, the programmer has no control if the result is created into a temporary stack variable or created a static area used by the subroutine. Presumably, identifying the subroutine as recursive would place compiler temporaries on the stack.

Would you know how the Fortran standards committee addressed this issue?

I do see some value in passing an expression as a convenient way of creating an unnamed temporary. There may be a problem with the optimization should you issue call RecursiveFoo(X+0) and expecting X to remain untouched. Or in the case of (X+1) that the compiler "remembers" (X+1) is stored in a temporary when on return the temporary was modified ((X+1)+1)...

Passing a literal could also present problems in that you then could potentially change the value of a PARAMETER "variable" as well as constant (assuming it were in R/W section).

I do recall back when I programmed in FORTRAN II that the compiler was perfectly happy to let you modify a literal (via dummy arg). In fact some programmers purposely did this to obfuscate their source code. (Made borrowing someone's proprietary code difficult).

Jim Dempsey

0 Kudos
Steven_L_Intel1
Employee
799 Views
The Fortran standards committee says "thou shalt not". You cannot write a standards-conforming program that can tell the difference. If you do it, as Jugoslav says, most compilers will create a temporary on the stack for the expression and discard it after the return, but that's an implementation-dependent behavior and you can't count on it.

Passing constants is, as you say, another thing. For a couple of years, Intel Fortran allocated constants in writeable memory so you could indeed "change the value of 3", though other things tended to hide that fact. More recently, constants are in read-only storage, which led to complaints when "working" programs started getting access violations. We went through the same thing in the transition from DVF 5 to 6. (Unfortunately we lost this feature in the transition to the Intel code generator but we've got it back now.) We've also added an "-assume writeable_constants" switch which makes stack temps for those too (no, you can't change the value of 3 for the caller.)
0 Kudos
jimdempseyatthecove
Honored Contributor III
799 Views

Steve,

If a "working" program got access violations by attempting to modify dummy arguments that were constant, and if the modifications were other than to same value, then even by copying the constant to a temp variable might break some of those "working" programs. i.e. the intent was to modify the constant. (You can't please everybody.)

"-assume writeable_constants" is a good solution since most of the complaints might be from recursive routines starting with a constant.

I might suggest you extend this to

"-assume:SubroutineName writeable_constants"

In this manner only the calls to a given subroutine (or function) will have the constants copied to stack variables and not all constant args for all subroutines. This will permit the programmer, who is not permitted to modify source code, to have some degree of control over performance by editing the make file (or optimizations in the IDE).

Jim Dempsey

0 Kudos
Lorri_M_Intel
Employee
799 Views

Small nit: the correct command line switch is -assume:[no]protect_constants

where protect_constants means we make a copy-on-the-stack before calling the routine, and noprotect_constants (the default) means we don't.

- Lorri

0 Kudos
Steven_L_Intel1
Employee
799 Views

Ah, right. Got confused with the C++ compiler switch. (Since I write the release notes for both compilers, I sometimes have a hard time keeping the options separate.)

0 Kudos
Reply