According to the Fortran standard:
The INTENT (OUT) attribute for a nonpointer dummy argument specifies that the dummy argument becomes undefined on invocation of the procedure
However this simple code gives me 5 as output, so it seems the argument didn't become undefined at the start of the procedure (in this case a subroutine).
subroutine useless(a)
integer, intent(out) :: a
print *,a
end subroutine useless
program test
integer :: n=5
call useless(n)
end program test
What am I getting wrong? It seems that intent(inout) and intent(out) are the same.
intent(inout) and intent(out) are certainly not the same. You have noted why, although you don't draw the correct conclusion. On entering the subroutine useless a is undefined, rather than defined.
Having a variable "undefined" means that you cannot rely on a specific behaviour when you reference it. You observed that the variable a had a value 5 but this does not mean that the only value you could observe is 5. In particular "undefined" does not mean "takes a specific value like NaN".
Your code is not standard conforming because of this reference to an undefined variable. See Fortran 2008 6.2 (similar meaning will be somewhere in Fortran 90 as initially tagged). Of particular note is that the compiler doesn't have to point out your mistake.
With intent(inout) the variable a would be defined when referenced and it will be guaranteed to have the value 5 (for a conforming processor).
More widely, there are other differences between the two intent attributes and this "coincidental" appearance of the similarity of the definition of the variable a could be more troublesome.
Allocatable arrays and objects with deferred type parameters, for example, are deallocated; derived types become undefined (and any allocatable components deallocated) and components with default initialization are "re-initalized"; pointers have their association status become undefined.
All of these latter things have potential for very awkward results, much more so than with a scalar integer, if they are referenced without being defined first.
Related
With Intel's ifort versions 2021.6.0+/ ifx 2022.1.0+ I am receiving error messages during compilation, specifically error #8284 about argument mismatch with an interface, which I suspect is erroneous.
Before I proceed to escalate this with the Intel team, I was wondering;
is the following code Standard conforming? I was under the impression that it was indeed legal and that the compiler is incorrect in this case.
MWE
program main
use iso_c_binding
call foo("Fortran Arg")
contains
function istring_(o) result(v)
character(len=*), intent(in) :: o
character(len=:, kind=c_char), allocatable :: v
v = trim(o)//c_null_char
end function istring_
subroutine foo(fileName)
interface
subroutine C_API(fileName) bind(C, name="foo")
use, intrinsic :: iso_c_binding
character(len=1, kind=c_char), dimension(*), intent(in) :: fileName
end subroutine C_API
end interface
character(len=*), intent(in) :: fileName
call C_API(fileName=istring_(fileName))
end subroutine foo
end program main
#include "stdio.h"
void foo(const char* fileName) { printf("%s\n", fileName); }
Output
/app/example.f90(21): error #8284: If the actual argument is scalar, the dummy argument shall be scalar unless the actual argument is of type character or is an element of an array that is not assumed shape, pointer, or polymorphic. [FILENAME]
call C_API(fileName=istring_(fileName))
---------^
Fix
Replacing the istring_ call in the interface with a local variable compiles which seems to indicate to me that this is a potential bug/regression.
--- tmp.f90 2023-01-05 10:19:30.778230819 +0000
+++ tmp2.f90 2023-01-05 10:19:36.342418329 +0000
## -18,6 +18,8 ##
end subroutine C_API
end interface
character(len=*), intent(in) :: fileName
- call C_API(fileName=istring_(fileName))
+ character(len=:), allocatable :: fileNameC
+ fileNameC = istring_(fileName)
+ call C_API(fileName=fileNameC)
end subroutine foo
end program main
Additional Info
GFortran, Intel's older versions compilers and flang-trunk all can compile the MWE.
Your program may be non-conforming, in which case the compiler is allowed to respond with any gibberish it chooses.
This doesn't mean that there is no issue to report to the vendor.
Consider the problematic line:
v = trim(o)//c_null_char
This violates the Fortran standard in the case that default character (corresponding to o and trim(o)) has different kind type parameter from that of c_null_char (the C character kind if that exists). While unlikely, this is possible. (Even more extreme is the case that c_char has the value -1 in which case the program is even more non-conforming.)
However, even in this case we have to concern ourselves with quality of implementation issues.
Let's pretend the compiler has default and C characters of different kind. If our compiler rejects the source code with the message
v = trim(o)//c_null_char
^
|-------------------------------|
Error: too many chickens crossing the road at |
we'd quite legitimately be a touch annoyed with the compiler vendor. We'd consider this a bug (or a too trendy company we want to avoid).
The Intel compiler isn't rejecting your program because of this reason (and it's likely the case that C and default characters are the same).
Let's apply the same QoI assessment to the actual error message.
If the actual argument is scalar, the dummy argument shall be scalar unless the actual argument is of type character or is an element of an array that is not assumed shape, pointer, or polymorphic. [FILENAME]
A compiler of high quality would have the error message meaningful when produced. Here, the actual argument is a scalar and the dummy argument is an array, so the error message is not wildly out a place.
(The standard's requirement is actually slightly more restrictive than the error message notes, but this can be accepted.)
The actual argument is of type (C) character making the error message unhelpful.
Additional to the temporary variable as a workaround, there are several others available:
call C_API(fileName=(istring_(fileName)))
call C_API(fileName=istring_(fileName)//c_char_'')
These add weight to the belief that the code is conforming, but the point of my answer here is that conformance doesn't actually matter in terms of whether the error message is valuable. Even if something is wrong with the subroutine reference, or program as a whole, a high quality compiler would not report in this way.
For completeness, let's look at that actual argument in more detail (and convince ourself that the subroutine reference is legitimate).
The subroutine reference
call C_API(fileName=istring_(fileName))
is to the subroutine C_API, with the actual argument istring_(fileName) using the keyword fileName.
We're allowed to use that keyword, and filename is the name of the dummy argument.
istring_(filename) is a (non-variable) primary expression, because istring_ is the name of the accessible internal function of the same host.1 This primary is evaluated before the subroutine reference and we note that the function result is a scalar C character. Exactly as we and the error message are expecting.
1 Note that you don't have implicit none in the program. You want to use this (or implicit none (external)) to ensure that an external integer function isn't used by a typographical mistake. In which case the compiler error would be entirely correct.
In Fortran language, providing an argument that has not the TARGET attribute to a procedure with a dummy argument that has the TARGET attribute should lead to an invalid code. However, when the following code is compiled with gfortran (5.1.0) or ifort (14.0.0), no error is detected and the program behaves like the argument actually has the TARGET attribute. Am I wrong when I say it is an invalid code or is this a compiler defect?
program pointerization
implicit none
integer, dimension(3) :: A
integer, dimension(:), pointer :: ptr_A
A = [1, 2, 3]
call pointerize(A, ptr_A)
print*, "A=", ptr_A
contains
subroutine pointerize(tab, ptr_tab)
integer, dimension(:), intent(in), target :: tab
integer, dimension(:), pointer :: ptr_tab
ptr_tab => tab
end subroutine
end program
You are correct that you have invalid code. It isn't, though, for the reason you say.
Within a procedure, it is legal for a dummy argument to have the target attribute even though the associated actual argument hasn't. Essentially, we are allowed to have pointers targeting the entity within the procedure as long as the lifetime of this pointing doesn't exceed the lifetime of the procedure.
In the case of this question, the dummy argument tab is allowed to have the target attribute even though the associated actual argument A hasn't. Even the pointer assignment statement ptr_tab => tab within the procedure is legal.
What is important, however, is that outside the procedure, where the actual argument hasn't the target attribute, we can't affect that entity through a pointer. The Fortran standard ensures this doesn't happen in the following way (Fortran 2008 C.9.4, see also 12.5.2.4; similar exists in Fortran 2018):
If a nonpointer dummy argument has the TARGET attribute and the corresponding actual argument does not, any pointers that become associated with the dummy argument, and therefore with the actual argument, during
execution of the procedure, become undefined when execution of the procedure completes.
That is, in the case of the question, on completion of pointerize ptr_A is no longer of defined association status. Deferencing this pointer in the main program's print statement is not allowed.
For interest, compiling the example code with nagfor results in the run-time diagnostic
Runtime Error: aaa.f90, line 9: Reference to dangling pointer PTR_A
Target was RETURNed from procedure POINTERIZATION:POINTERIZE
Program terminated by fatal error
Abort (core dumped)
But equally, just because the pointer association is undefined, this doesn't mean that you can't get the results you expect. Such checking is a nice thing from a compiler but it isn't required that it should fail.
Consider:
program main
real, allocatable, dimension(:) :: foo
integer n
n=10
call dofoo(foo,n,1)
allocate(foo(n))
call dofoo(foo,n,0)
end program main
subroutine dofoo(foo,n,mode)
real foo(n)
integer i,n,mode
if(mode.eq.1)then
n=6
return
endif
do i=1,n
foo(i)=i
enddo
return
end subroutine dofoo
Is there anything wrong with the above code? (It works with gfortran) I pass in an un-allocated array the first time, but I don't touch it -- Is there anything in the standard that could cause this to behave in a system dependent way?
You've almost answered your own question. Yes, by the standard, it is always illegal to pass an unallocated allocatable arrays as an actual argument if you don't have an interface in scope.
If you have an interface in scope it is only legal if the dummy argument is also allocatable.
And yes I've been bitten by it. My work around has been to allocate to zero size before the call.
The answer by Ian Bush correctly states that the use in the question is not allowed. We can be more precise, though. (References in parentheses to the Fortran 2018 standard.)
There are three cases where an unallocated allocatable actual argument may be used:
when the dummy argument is also allocatable (only since Fortran 2003, or Fortran 95+TR-15581) (15.5.2.6 p.2)
when the dummy argument is an optional ordinary argument (only since Fortran 2008) (15.5.2.12 p.1, 15.5.2.4 p.7)
when the procedure is an intrinsic inquiry function (15.5.2.4 p.7, 16.1 p.2)
There is no exception for "unused" dummy arguments. These restrictions apply whether the allocatable argument is an array or a scalar.
Any other use means that the program is non-conforming (15.5.2.4 p.7, 15.5.2.7 p.2).
In a conforming program, each of these acceptable cases has an explicit interface available. An allocatable or optional dummy argument requires one (15.4.2.2 p.1(3)) and an accessible intrinsic procedure always has an explicit interface available (15.4.2.1 p.1).
These requirements on the program are not ones a compiler is required to be able to analyse. Such a non-conforming program doesn't necessarily mean that you will see problems when compiling and running. In the case of the program of the question, which doesn't attempt to dereference in a bad way, you may well get away with it. However, it's not good to write non-conforming programs and very bad to rely on them "working".
There are some ways where things may go wrong:
run-time checks
compile-time checks
the dummy having the value attribute (again, explicit interface required in this case)
That is, your program could behave differently with some compilers/compiler options. Your compiler may refuse to compile the program if it notices that you should have an explicit interface available. The program may abort when entering the subroutine if the runtime checks whether the argument is allocated. The runtime may do something very unexpected if trying to make an anonymous definable copy of the array with the value attribute when the actual argument isn't allocated.
For the question there's another non-compliance to worry about. The explicit-length dummy argument foo(n) means that the actual argument must have at least n elements. An unallocated array doesn't have at least n elements (for any, even zero, n). If foo was intent(out) a compiler would be within its rights doing something to "undefine" these elements of the actual argument. This may fail.
In a subroutine or function an input variable can be defined with intent(in) and the compiler assures that within the subroutine the variable can not be altered. As soon as the variable is passed (by reference) to another subroutine this subroutine is able to alter the variable without compiler warning.
This was tested with gfortran with the 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 are:
Is this the normal behaviour for all compilers?
Is there a way to force the compilers to assure that the variable is really constant and that alterations would be presented as compiler errors? I mean something like the const keyword in C/C++ which is also checked against the called functions which also need to assure that the constant is treated accordingly and that no reference is escaping.
I found the possibility to pass the variable to the subroutine by "value" via passing it trough an expression like test((i)). For numeric variables, this is understandable and ok, but this seems to work with gfortran for arrays, derived types and pointers, too. Does this work with other compilers, too? Is it a safe way to protect my local variables?
With sufficient compiler options gfortran generates a warning for your example, that an implicit interface is used.
If you make the interface explicit by placing the subroutines into a module, and use intents for all 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 error message:
call doSomethingNasty(i)
1
Error: Procedure argument at (1) is INTENT(IN) while interface specifies INTENT(INOUT)
When pass the argument "(i)" you are passing an expression rather than a variable. The expression is not definable and thus should not be used as an actual argument for an "out" or "inout" dummy argument.
Another approach for argument "safety": you can also use the "value" attribute in the declaration of a dummy argument to essentially make a local copy of the argument and guarantee that the actual argument won't be altered.
Edit:
As kemiisto pointed out, "contains" also makes the interface known. I don't like "contains" because the variable scoping ... all variables of the parent program are visible. Try this test code out:
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
As soon as the variable is passed (by reference)
A warning note: Fortran standard does not specify how variables are passed (by reference, by value or in any other way). This is implementation dependent. Fortran is quite different from C/C++. Better stop to think in C-way. It will be misleading.
1) Yes and no. It is implementation dependent. First of all INTENT attribute specifies your intentions. As you can see in Fortran standard, Section 5.3.10, NOTE 5.17 (you can get the Final Draft of so-called Fortran 2008 by link at the beginning of this page http://fortranwiki.org/fortran/show/Fortran+2008):
Argument intent specifications serve several purposes in addition to
documenting the intended use of dummy arguments. A processor can check
whether an INTENT (IN) dummy argument is used in a way that could
redefine it. [...]
compiler ("processor") can (not should) check such things.
Secondly (as I already mentioned) you can not be sure that for argument with INTENT(IN) compiler will choose to pass it by value and not by reference. In this case the choice was by reference. At least it seems that i in test subroutine was passed by reference. The next subroutine. The default INTENT is INOUT. That is why it is possible to change the value of argument i (with unspecified that's why default INTENT) in doSomethingNasty. Once again i was passed by reference. Or maybe it even was "copy-in/copy-out". Such freedom exists to allow compiler perform optimizations.
2) No. If I understand you correctly you need something similar to constant references (to achieve what is called "const correctness"). But we do not even have references in Fortran, so obviously there are no constant references.
3) There is a way to protect local variables. As M. S. B. pointed out in his answer place your subroutines in MODULEs (or in CONTAINS section of main program) and always specify INTENT attributes for variables. I've tried to compile 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 that other compilers (Pathscale, IBM) will behave the same way.
Good practice dictates that subroutine arguments in Fortran should each have a specified intent (i.e. intent(in), intent(out) or intent(inout) as described this question):
subroutine bar (a, b)
real, intent(in) :: a
real, intent(inout) :: b
b = b + a
...
However, not specifying an intent is valid Fortran:
subroutine bar (a, b)
real, intent(in) :: a
real :: b
b = b + a
...
Are there any real differences beyond compile time checking for an argument specified as intent(inout) and an argument without a specified intent? Is there anything I should worry about if I'm retrofitting intents to older, intent free, code?
According to The Fortran 2003 Handbook by Adams, et al., there is one difference between an intent(inout) argument and argument without specified intent. The actual argument (i.e., in the caller) in the intent(inout) case must always be definable. If the intent is not specified, the argument must be definable if execution of the subroutine attempts to define the dummy argument. definable means setting the value: dummy_arg = 2.0. Clearly the actual argument should be a variable if this is done. For intent(inout) the actual argument must be definable whether or not the subroutine does this. Without no intent specified, it depends on what happens on that particular invocation of the subroutine -- if the subroutine doesn't define the variable, it is OK; if it does, than there is a problem -- cases such as writing to an actual argument that is a constant will obviously cause problems.
This doesn't mean that the compiler will diagnose all of these cases -- what the standard requires a compiler to diagnose is a different issue. It would be close to impossible to detect all errors of the intent-not-specified case requirement at compile time, since violations depend on the run-time flow of the code. It is much easier for the compiler to diagnose the intent(inout) case and warn you of problems with the code.
Your question prompts me to wonder (lots to do right now) whether you might encounter a difference in behaviour if your code passes a PARAMETER as an actual argument which your sub-program then attempts to write to. Without an INTENT declaration the compiler might let this go, leading to odd behaviour. With the declaration I'd expect a compile-time error.
You and I might think that there is no difference between INOUT and no INTENT declaration, but don't forget that there are a lot of old Fortran programs out there and that compatibility with older language versions is an important feature of new standards. If it was correct (but dodgy) FORTRAN77 then a lot of people expect their code to remain correct (still dodgy) with a Fortran 90+ compiler.
A quick read of the 2003 standard does indicate that there is a difference between INOUT and no INTENT, but more close reading is required. If you do test this let us know your conclusions; if I have time later I will test it myself and let you know.
To understand the role of intent in/out, you need to know that internally, Fortran effectively passes variables by reference. This isn't always the same as actually passing by reference.
If you pass an internal subsection of an 2-D array to a subroutine, ie: data(i1:i2, j1:j2), then Fortran copies that data into a contiguous section of memory, and passes the new address to the routine. Upon returning, the data is copied back into its original location.
By specifying INTENT, the compiler can know to skip one of the copying operations.
It not only acts as a failsafe for modifying data that you want to remain unchanged, but also can speed up your code when dealing with large datasets.
To somehow expand M.S.B.'s answer, omitting intent gives you more flexibility, at the cost of less compile-time checking. Without intent, you can pass a literal constant if you know the code branch will not attempt to modify it, with intent(inout) the compiler will probably bark at you if you try it. This may be useful with "dual" procedures that will modify or not some argument depending on the options, for example:
subroutine copy(x,a,readwrite)
integer :: x, a
integer, intent(in) :: readwrite
if (readwrite == 0) then
x = a
else
a = x
end if
end subroutine
If you want to add intent to x and a, it must be inout for both, because both are potentially read and modified. But that will not allow you to write e.g.
call copy(x,3,0) ! equiv. to x=3
call copy(42,a,1) ! equiv. to a=42
This example is quite silly, but think of a more elaborate subroutine that can read from or write to a file with some complex formatting. (I'm not saying that's the best solution, but it's something you can easily find.)