Doctor Fortran in "I've Come Here For An Argument"

By Steve Lionel (Intel) (19 posts) on March 31, 2009 at 11:57 am

One of the most fundamental aspects of Fortran programming is passing arguments to procedures.  It is also one of the most misunderstood aspects.  In this space today I'll try to make things clearer.

First, some terminology.  In Fortran, there are "actual arguments" and "dummy arguments".  An actual argument is what you put inside the parentheses in a call to a procedure.  This can be a variable, a named (PARAMETER) constant, a literal (such as 3), a procedure or an expression. "variable" here means more than just a variable name. It could be an array reference, a derived type component reference, a substring reference or a combination of any or all of these.  If you can put it on the left side of an assignment, it's a variable.

A dummy argument is the corresponding element in the argument list of the called procedure.  Some languages call these "formal arguments". When you call a procedure, each dummy argument becomes "associated" with its corresponding actual argument, if any.  (OPTIONAL dummy arguments do not get associated with omitted actual arguments.)  The rules for argument association are complex and have many special cases, and that's what I'll spend most of this post going over.

Argument Association

On a fundamental level, argument association is pretty simple.  Although most people would tell you that Fortran uses "pass by reference", meaning that the address of the actual argument is used for the dummy argument, that's not quite true.  The standard says that argument association is "usually similar to call by reference" and then adds "The actual mechanism by which this happens is determined by the processor." [Fortran 2003 Note 12.22]

There are some cases where what actually gets passed is not the original argument.  The most common case for this is when a non-contiguous array pointer or a array slice is passed to an argument for which the compiler does not see an explicit interface specifying that the corresponding dummy argument is an assumed-shape array.  Because the called routine is expecting a contiguous array, the compiler needs to make a copy of the actual argument, pass the copy, and then copy back any changes. The compiler can warn you about this at run-time if you use the option /check:arg_temp_created (Windows) or -check arg_temp_created (Linux/Mac OS). The compiler will generate a run-time test to see if the argument is actually contiguous and skip the copy if it is.

This would probably be a good time to mention INTENT.  If the compiler sees that the dummy argument is INTENT(OUT), it doesn't need to make the copy going in, and if it is INTENT(IN), it can skip the "copy out".  Explicit interfaces and explicit INTENT are always a good idea.

Another case where a copy is made is if the argument has the VALUE attribute.  By this I mean the Fortran 2003 VALUE attribute, not the one on a !DEC$ ATTRIBUTES directive.  In this case, the copy is made by the called routine on entry into a temporary, and any changes discarded on exit.

Sequence Association

A lot of Fortran programs, especially older ones, rely on sequence association.  What is it?  An actual argument that is an array element, an array expression or a character scalar (of default or _CHAR kind) represents a sequence of elements.  This sequence starts with the first element or character passed and continues to the end of the array or last character.  The corresponding dummy argument, which need not have the same number of dimensions (rank) as the actual argument, or the same character length, is associated with this sequence.

Let's take a simple example common to older code.

real a(10)
call sub(a(3))
...
subroutine sub (b)
real b(8)

When sub is called, b(1) will be associated with a(3), b(2) with a(4) and so on up through b(8) being associated with a(10).  Usually, the programmer would declare b as assumed-size with a dimension of (*), in which case the implied extent of b is 8 because that's the number of elements remaining in array a.

What happens if you declare b to be fewer or more than 8 elements?  Fewer is fine, but more is not.  The compiler may or may not detect this error.

There is a significant restriction regarding sequence association which more and more people are running into, especially as they "update" old code to include modern Fortran features such as pointers.  You don't get sequence association if the actual argument is assumed-shape or is an array with a "vector subscript".  Why?  Because the argument may not be contiguous and thus there can't be an association with a sequence of elements.

Here's where this gets most people into trouble... In general, a Fortran rule says that "if the actual argument is scalar, the corresponding dummy argument shall be scalar, unless the actual argument is of type default character, of type character with the C character kind, or is an element or substring of an element of an array that is not an assumed-shape or pointer array." [Fortran 2003 12.4.1.2]

If in the example above, array a was an assumed-shape array (might be a POINTER or ALLOCATABLE or a dummy argument itself) and you passed a(3), the compiler would give you error #6585 quoting the text from the standard shown in the previous paragraph. The workaround for an assumed-shape array would be to pass an array slice, but you can't then depend on sequence association and the dummy argument has to be no larger than the slice you passed.

You'll also get this error (as error #8299) if you are passing a scalar that isn't an array element, such as a constant 1 or a scalar variable, to an array dummy argument.  In some cases you can use an array constructor to turn the scalar into an array, such as [1], but you will lose sequence association benefits.

Now, what's the deal with that exception for character types when the dummy is an array of default character kind or C kind?  This lets you pass a normal character value or variable to a dummy argument declared as an array of single characters, as this is the official "interoperable" mapping for C arguments declared as "char*". Note that you'll have to supply any trailing NUL yourself - Fortran won't do it for you.

Rules, Rules, Rules

Here are just a few of the other language rules that must be followed when passing arguments. You can read them all in section 12.4.1.2 of the Fortran 2003 standard.

The compiler can help a lot with making sure the rules are followed if you are using explicit interfaces (modules, contained procedures or INTERFACE blocks.)  Otherwise, use the /warn:interface and /gen-interface (Windows), -warn interface and -gen-interface (Linux, Mac OS) options to have the compiler do interface checking for all your Fortran routines.  On Windows, these options are enabled by default in new Visual Studio projects.

My advice is to always use explicit interfaces and to use proper INTENT attributes as well.

That's all for this round. If you have comments on this post, please add them below.  If you are looking for tech support, please post in our user forums.

Categories: Software Engineering

Comments (12)

March 31, 2009 6:04 PM PDT


garry weil
wow Fortran is still active.it has even been updated.iam curious what application domains are still using fortran
April 2, 2009 5:27 PM PDT

Clay Breshears (Intel)
Total Points:
15,225
Status Points:
15,225
Black Belt
Excellent explanations and advice. Can't wait for "Doctor Fortran in 'Getting Hit on the Head Lessons'." :-)
April 29, 2009 11:27 AM PDT

greg_thorwald
Total Points:
520
Status Points:
20
Brown Belt
Hi Steve,

Thank you for the argument explanation.
When using array slices as arguments, are there memory use considerations? For example, does the program memory needed at run-time become larger or smaller if I pass an array slice x(500:1000) to a subroutine? Would it be better to pass the entire array x() to the subroutine and access the particular rows within the subroutine? Or is the amount of memory used unaffected by array slice arguments?

Garry: to answer your question, we use Fortran for Finite Element Analysis (FEA), which is useful for structural, heat transfer, and fracture mechanics computational analysis.

Regards,
Greg
April 29, 2009 11:39 AM PDT

Steve Lionel (Intel)
Total Points:
115,025
Status Points:
115,025
Black Belt
Greg,

If the compiler decides it needs to create a temporary copy of the array slice, then that copy will occupy additional memory. By default, this copy is made on the stack, and if the stack is of sufficient size, it will not increase the virtual memory used by the application. The compiler has a "heap-arrays" option (/heap-arrays or -heap-arrays) that will cause this copy to be allocated on the dynamic heao - this is especially useful when the copy is too large for the stack.

If the routine being called accepts the array as assumed shape, DIMENSION(:), then a copy is not made. This requires an explicit interface to be visible to the caller. Otherwise, the compiler will do a run-time test to see if the slice is contiguous and will avoid the copy if it is. You can ask the compiler to give you a run-time diagnostic if it made a copy for an array argument by specifying /check:arg_temp_created or -check arg_temp_created.

Please do use array slices when you need to. But, avoid using them when you don't.
May 19, 2009 5:32 PM PDT

shafaatihawaii.edu
Total Points:
15
Registered User
is ifort a file for windows or linux?if i just install intel visual fortran is it enough for other softwares using fortran link properly?where can i find ifort.exe
May 20, 2009 5:47 AM PDT

Steve Lionel (Intel)
Total Points:
115,025
Status Points:
115,025
Black Belt
ifort.exe is one of the files of the Intel Visual Fortran for Windows product - it's the executable used to invoke the compiler. For other software to invoke the compiler, a number of environment variables have to be set properly. This is best done by starting a command prompt session using the "Fortran build environment" shortcut provided in the Start menu for Intel Visual Fortran. It can be done from another script by calling the "ifortvars.bat" file, whose location is given by "%IFORT_COMPILER11%bin". It takes an argument of "IA32", "INTEL64" or "IA64" to specify the target platform. Once this is done, the "ifort" command will call the compiler (and linker if required.)

For Linux, the Intel Fortran Compiler for Linux also provides an "ifort" for a similar purpose. For more information, read the compiler documentation on use from the command line.
September 14, 2009 11:59 AM PDT

krodberg
Total Points:
5
Registered User
I just started using the Intel Visual Fortran 11 compiler and ran into what I think is a similar problem to what is defined above. I am trying to compile some code from the USGS which I have compiled with CVF yet I get an error with IVF. I need IVF to compile Modflow as a DLL for an OpenMI project.

I've clipped out a small snip-it and set it up to compile in CVF and show the error I get in IVF. Is there a way to compile it in Intel without revising the USGS code?

module a
REAL, SAVE, DIMENSION(:,:,:), POINTER ::SUB
end module a

program test
use a, only: sub
character*4 text(4)
integer iswocf(3)

KQ = 1
IF(ISWOCF(3).GE.0) CALL ULAPRW(SUB(1,1,KQ),TEXT(3),KSTP,KPER,NCOL,NROW,K,ISWOCF(3),IOUT)

end

SUBROUTINE ULAPRW(BUF,TEXT,KSTP,KPER,NCOL,NROW,ILAY,IPRN,IOUT)
CHARACTER*16 TEXT
DIMENSION BUF(NCOL,NROW)
return
end

Error 1 error #7938: Character length argument mismatch. [TEXT]
Error 2 error #7836: If the actual argument is scalar, the corresponding dummy argument shall be scalar unless the actual argument is an element of an array that is not an assumed-shape or pointer array, or a substring of such an element. [BUF]
Error 3 Compilation Aborted (code 1)
September 14, 2009 12:11 PM PDT

Steve Lionel (Intel)
Total Points:
115,025
Status Points:
115,025
Black Belt
krodberg, your code snippet has two errors which the compiler, more or less, correctly notes. The first one is that you pass TEXT(3) to an argument declared as CHARACTER*16. With sequence association, TEXT(3) is only 8 characters, comprised of TEXT(3) and TEXT(4), but you pass it to an argument declared CHARACTER*16 and this is not allowed. That said, I know that the compiler doesn't check this sort of thing properly and even if TEXT had more elements to satisfy the standard, you might still get the error. This will be fixed in a future update.

The second error is very much what is described in this article - POINTER arrays do not participate in sequence association, so it's not allowed to pass a pointer array element to an argument that is an array.

That said, you can likely eliminate both of these error messages by turning off generated interface checking, which is the default on Windows in the Visual Studio environment. The property Fortran > Diagnostics > Check Routine Interfaces should be set to No. This won't "fix" your incorrect program, but it will prevent the compiler from noticing the errors. You may also need to turn off string and array bounds checking. Of course, this also means that you may get wrong results if the code improperly accesses the arguments.

If you need more help, please ask in our support forum at http://software.intel.com/en-us/forums/intel-visual-fortran-..... r-windows/
October 23, 2009 5:28 AM PDT

carl_caves
Total Points:
40
Registered User
I am trying to compile a code and I get this error:

error #8299: A scalar actual argument must be passed to a scalar dummy argument unless the actual argument is of type character or is an element of an array that is neither assumed shape nor pointer. [VAL]
CALL writeone_ik4( ds_f_saa, ipix, INT(0, 4) )
----------^

I haven't written this code, so it is very difficult to modify it. Is there any compilation option in order to solve this problem.

The compilation option that I am using is:
ifort -c -O3 -fPIC -static

The Intel fortran compiler version is: Intel(R) Fortran Compiler Professional for applications running on IA-32, Version 11.1 Build 20090630 Package ID: l_cprof_p_11.1.046

Does anyone have any suggestion to solve this problem?

Thank you in advanced.
October 23, 2009 7:15 AM PDT

Steve Lionel (Intel)
Total Points:
115,025
Status Points:
115,025
Black Belt
I'd need to see the declaration of writeone_ik4 to give you specific advice. There is no compiler option to remove the error, since it is clear that there is an explicit interface visible for writeone_ik4.

This error message is telling you that the VAL dummy argument to writeone_ik4 is an array, but that you have passed a scalar to it, which is not legal. The solution might be as simple as enclosing the actual argument in [] brackets to make it an array, but this works only if the routine does not want to write to the argument.

If you need more help, please ask in our user forum (http://software.intel.com/en-us/forums/intel-fortran-compile.....-mac-os-x/)
October 23, 2009 10:04 AM PDT

carl_caves
Total Points:
40
Registered User
Steve, the declaration of writeone_ik4 is as follows:

cal writeone_ik4_ ( ds_Xtrack_idx,ipix, (/gome2_curr_ixtrack /) )

cal writeone_ik4_ ( ds_iscan ipix, (/curr_iscan /) )


cal writeone_ik4_ ( ds_Xtrack_dim, iscan, (/nxtrack_per_scan(gome_curscan /) )
cal writeone_ik4_ ( ds_utc_year, iscan, (/ gome2_utc_year /) )
cal writeone_ik4_ ( ds_utc_doy, iscan, (/ jday /) )

Any suggestion?

Thank you.

October 23, 2009 10:52 AM PDT

Steve Lionel (Intel)
Total Points:
115,025
Status Points:
115,025
Black Belt
That's not the declaration - those are uses. But it does suggest the solution. Replace INT(0,4) with [INT(0,4)] or, if you want to be consistent with the others, (/INT(0,4)/) which means the same thing - make an array out of the value.

Trackbacks (4)


Leave a comment  

To obtain technical support, please go to Software Support.
Name (required)*

Email (required; will not be displayed on this page)*

Your URL (optional)


Comment*