I'm learning Fortran and I'd like to encapsulate an array and a subroutine in a type. The problem appears to be in the type definition of the self-object.
This is the minimal test case I came up with:
module testing
implicit none
type test(para)
integer, len :: para
real, dimension(para) :: weights
contains
procedure :: testing => testing_test
end type
contains
subroutine testing_test(self)
class(test(*)) :: self
end subroutine
end module
Compiling this with gfortran raises this error:
module_test.f08:9:23:
procedure :: testing => testing_test
1
Error: Argument ‘self’ of ‘testing_test’ with PASS(self) at (1) must be of the derived-type ‘test’
It works when the array-length is fixed (so type%para doesn't exist)
Is what I'm trying to do (type with array of variable size and bound procedure) plain impossible or am I missing something regarding dummy argument definition?
Thanks to #Rodrigo for the idea, I finally found this bug (and patch):
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82943
To fix the issue, download the source, apply the mentioned patch and compile your own gfortran. (Or wait until it's in the repositories)
This is not really an answer but may provide a solution for some. gfortran-10 still has the same problem. However flang-7 compiles this example and it is available for Ubuntu since 19.10, and maybe other OS:es.
A previous answer points to a bug report and patch for gfortran. It is worth saying, though, that this is standard Fortran (2003) code.
What we have here is a type-bound procedure with passed-object dummy argument. The main restrictions of such an argument are that it is a:
scalar,
nonallocatable,
nonpointer object,
with all length-type parameters assumed.
Further, as the type is extensible the passed-object dummy argument must be polymorphic.
For the example of the question, all of the conditions are met.
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.
I'm learning Fortran and I'd like to encapsulate an array and a subroutine in a type. The problem appears to be in the type definition of the self-object.
This is the minimal test case I came up with:
module testing
implicit none
type test(para)
integer, len :: para
real, dimension(para) :: weights
contains
procedure :: testing => testing_test
end type
contains
subroutine testing_test(self)
class(test(*)) :: self
end subroutine
end module
Compiling this with gfortran raises this error:
module_test.f08:9:23:
procedure :: testing => testing_test
1
Error: Argument ‘self’ of ‘testing_test’ with PASS(self) at (1) must be of the derived-type ‘test’
It works when the array-length is fixed (so type%para doesn't exist)
Is what I'm trying to do (type with array of variable size and bound procedure) plain impossible or am I missing something regarding dummy argument definition?
Thanks to #Rodrigo for the idea, I finally found this bug (and patch):
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82943
To fix the issue, download the source, apply the mentioned patch and compile your own gfortran. (Or wait until it's in the repositories)
This is not really an answer but may provide a solution for some. gfortran-10 still has the same problem. However flang-7 compiles this example and it is available for Ubuntu since 19.10, and maybe other OS:es.
A previous answer points to a bug report and patch for gfortran. It is worth saying, though, that this is standard Fortran (2003) code.
What we have here is a type-bound procedure with passed-object dummy argument. The main restrictions of such an argument are that it is a:
scalar,
nonallocatable,
nonpointer object,
with all length-type parameters assumed.
Further, as the type is extensible the passed-object dummy argument must be polymorphic.
For the example of the question, all of the conditions are met.
I'm learning Fortran and I'd like to encapsulate an array and a subroutine in a type. The problem appears to be in the type definition of the self-object.
This is the minimal test case I came up with:
module testing
implicit none
type test(para)
integer, len :: para
real, dimension(para) :: weights
contains
procedure :: testing => testing_test
end type
contains
subroutine testing_test(self)
class(test(*)) :: self
end subroutine
end module
Compiling this with gfortran raises this error:
module_test.f08:9:23:
procedure :: testing => testing_test
1
Error: Argument ‘self’ of ‘testing_test’ with PASS(self) at (1) must be of the derived-type ‘test’
It works when the array-length is fixed (so type%para doesn't exist)
Is what I'm trying to do (type with array of variable size and bound procedure) plain impossible or am I missing something regarding dummy argument definition?
Thanks to #Rodrigo for the idea, I finally found this bug (and patch):
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82943
To fix the issue, download the source, apply the mentioned patch and compile your own gfortran. (Or wait until it's in the repositories)
This is not really an answer but may provide a solution for some. gfortran-10 still has the same problem. However flang-7 compiles this example and it is available for Ubuntu since 19.10, and maybe other OS:es.
A previous answer points to a bug report and patch for gfortran. It is worth saying, though, that this is standard Fortran (2003) code.
What we have here is a type-bound procedure with passed-object dummy argument. The main restrictions of such an argument are that it is a:
scalar,
nonallocatable,
nonpointer object,
with all length-type parameters assumed.
Further, as the type is extensible the passed-object dummy argument must be polymorphic.
For the example of the question, all of the conditions are met.
Suppose I have a subroutine which accepts two arrays as input. One is given intent(in) and the other is given intent(out). The latter is derived from the former in an arbitrary manner. However, what if I pass through the same actual argument for both dummy arguments? In general, the result will not be what was intended by the subroutine. See code snippet below.
The problem is, the compiler doesn't seem to care, even though I've given intent flags. I'm using Intel Visual Fortran Composer 12, with all diagnostics. Is there a better way of coding the subroutine, or some compiler option I'm missing, to make the code safer?
module foo
contains
subroutine sub_a()
implicit none
real::array(10,10)
call sub_b(array,array)
end subroutine
subroutine sub_b(array1,array2)
implicit none
real,intent(in)::array1(10,10)
real,intent(out)::array2(10,10)
!array2 is derived from array1 somehow
end subroutine
end module foo
This is called aliasing -- referring to the same item by two different names. In most cases this is not allowed in Fortran. Your example is not legal Fortran -- see http://software.intel.com/en-us/blogs/2009/07/10/doctor-fortran-in-ive-come-here-for-an-argument-side-2/, which has this specific case, of aliasing via the same actual argument used for two dummy arguments. Fortran compilers are not required to diagnose all violations of the rules of the language and this appears to be an example that the compiler is not recognizing.
Edit: aliasing is permitted. What is forbidden is changing the value of the dummy argument through the alias, here the other dummy argument. The rules are less restrictive for pointer and target arguments. The specific rules are described in "The Fortran 2003 Handbook" by Adams et al.
Putting parentheses around the argument that is intent(in) makes the code legal since you are effectively passing a copy:
call sub_b((array),array)
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.)