- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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...
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
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.)
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page