FORTRAN - allocatable array in subroutine - arrays

I'm trying to use an allocatable array in a subroutine but the compiler complains that
Error: Dummy argument 'locs' with INTENT(IN) in variable definition context (ALLOCATE object) at (1)
The only thing I could find was that I am supposed to use an explicit interface, which I am doing. Here the relevant code for the subroutine:
RECURSIVE SUBROUTINE together(locs, LL, RL)
INTEGER, DIMENSION(:,:), ALLOCATABLE, INTENT(IN) :: locs
INTEGER, INTENT(IN) :: LL, RL
ALLOCATE(locs(LL,RL))
END SUBROUTINE together

The compiler's error message is one descriptive of the problem. With INTENT(IN) you are saying that the object will not change, but you then go on to attempt to ALLOCATE it.
Yes, an explicit interface will be required for the calling, but that isn't the problem.
The Fortran 2008 standard says in section 5.3.10 that
A nonpointer object with the INTENT (IN) attribute shall not appear in a variable denition context
Allocation is one such context: section 16.6.7, point (11).

The locs dummy argument is allocatable, and has the INTENT(IN) attribute - the intent attribute here indicating that the calling procedure is providing information to the subroutine.
A consequence of the INTENT(IN) attribute is that you cannot change the allocation status (or value) of locs. Your ALLOCATE statement is attempting to do just that.

Try allocating your array in your main program, then when locs is pushed to your subroutine, use INTENT(INOUT) to tell the compiler you also want to change the contents of your array.

Related

Fortran: modules and array size [duplicate]

The following code, combining module procedures and external procedures:
module module_dummy
implicit none
contains
subroutine foo(a)
real, intent(inout) :: a(:)
call bar(a)
end subroutine foo
end module module_dummy
program main
use module_dummy
implicit none
integer, parameter :: nelems = 100000000
real, allocatable :: a(:)
allocate( a(nelems) )
a = 0.0
call foo(a)
print *, a(1:10)
deallocate(a)
end program main
subroutine bar(a)
implicit none
real, intent(inout) :: a(:)
a = 1.0
end subroutine bar
seems to fail either:
with a segmentation fault
printing a block of 0.000 instead of a block of 1.000
on any platform I have tried so far. The problem is related to the implicit interface declaration of bar, and in fact the issue can be solved adding in any way an explicit interface, e.g. using:
module module_dummy
implicit none
contains
subroutine foo(a)
interface
subroutine bar(x)
real, intent(inout) :: x(:)
end subroutine bar
end interface
real, intent(inout) :: a(:)
call bar(a)
end subroutine foo
end module module_dummy
or declaring bar inside a module to be used by module_dummy.
Anyhow I really don't understand what is the error in the first place. What I have found on the Fortran 90 standard (sec. 12.3.2.4) says that:
The type, type parameters, and shape of dummy arguments of a procedure
referenced from a scoping unit where the interface of the procedure is
implicit must be such that the actual arguments are consistent with
the characteristics of the dummy arguments.
In this case the rule seems to be respected, as a is always declared as
real, intent(inout) :: a(:)
So, what am I missing in the interpretation of the standard that makes the previous code wrong?
Dummy arguments that are assumed shape must have an explicit interface at their point of reference. F90 12.3.1.1 item 2c.
Practically, assumed shape arrays are passed by passing a descriptor - a little structure that describes the bounds and the location of storage of the array. Ye-olde F77 explicit shape and assumed size arrays are passed simply by passing the address of the first element. Without the explicit interface the compiler doesn't know that it needs to build and pass the descriptor - hence chaos and confusion results.

Warning for explicit-shape array reshaping

Let all routines be inside modules.
If I pass the array real*8 aa(5,3) to a routine
subroutine sub(bb)
real*8, intent(in) :: bb(2,5)
...
end subroutine
with the statement call sub(aa) this will compile without a warning and the first 2 columns of aa will fill the bb array. The elements of the arrays aa and bb are aligned very differently.
If instead the routine is written
subroutine sub(bb)
real*8, intent(in) :: bb(:,:)
...
end subroutine
then bb would have the same shape and storage order as aa.
Q: The first behavior is quite dangerous if one forgets that there are explicit-size declarations in a routine. Can I make the compiler warn when explicit-shape arrays change shape/alignment?
I am not aware of a compiler option to warn about this as it is a perfectly legitimate practise using the storage association - we have several questions and answers about this concept. It can be quite useful.

Declaration of dummy argument explicit shape arrays in Fortran

I thought that the specification of an explicit shape dummy argument array in a subroutine can involve any integer variables, including other dummy variables (usual cases), module variables, and local variables of the present subroutine. But it turns out that local variables (which are not dummy variables) can not be used in the specification.
An example is as follows:
module mp
implicit none
contains
subroutine p(b)
integer :: m=4, n=4 !not integer,parameter :: m=4, n=4
integer :: b(m,n)
end subroutine p
end module mp
gfortran will raise Error: Variable 'm' cannot appear in the expression at (1)
For this example, I can use integer,parameter :: m=4, n=4 to avoid this, but I do not understand why the original case does not work, considering the fact that the bounds/extents of explicit shape arrays do not need to be known at compile time. A modified version of the above example works:
module mp
implicit none
integer :: m=4, n=4
contains
subroutine p(b)
integer :: b(m,n)
end subroutine p
end module mp
Considering the slight difference between the two examples, I expect both of them work, but in fact the former does not. Could someone explain the reason?
Update: I found out that this is a very subtle issue because it depends on whether the subroutine is contained in a module or is standalone, it also depends on the version of gfortran. I have posted examples in the answer region.
Formally, there are requirements on what the bounds in such an explicit shape array are. These don't simply map to "don't have to be known at compile time".
For an array explicit shape, the array bounds must be specification expressions. Often, such bounds must be constant expressions, but this is not the case for dummy arguments. This partly gives rise to the (erroneous) thought that they needn't be known at compile time.
However, the constraints for a specification expression must still be met. These can be found in Fortran 2018 10.1.11. In particular, a local variable (even a saved one) may not appear in a specification expression.
For the examples of the question, using named constants such as with
integer, parameter :: m=4, n=4
is allowed in a specification expression. Indeed, the specification expressions m and n are even constant expressions in this case.
If we had
function p(b,m,n)
integer m, n, b(m,n)
end function
then we have valid specification expressions for the array bounds, even though m and n are not constants.
The right workaround is to put the procedure body in a BLOCK construct:
module mp3
contains
subroutine p(b)
implicit none
integer :: m=4, n=4
BLOCK
integer :: b(m,n)
END BLOCK
end subroutine p
end module mp3
The fact that it works in gfortran-8 as a standalone subroutine should be reported on bugzilla. You've got a nice minimal example there.
EDIT: I hadn't noticed that b was a dummy argument. I was thinking more in terms of something like
module mp3
contains
subroutine p(x)
implicit none
real x
integer :: m=4, n=4
BLOCK
integer :: b(m,n)
END BLOCK
end subroutine p
end module mp3
But as the example stands the BLOCK approach just can't work. Also gfortran 8.1.0 rejects the form with the standalone subroutine:
subroutine p(x)
implicit none
real x
integer :: m=4, n=4
integer :: b(m,n)
end subroutine p
Error: Variable 'm' cannot appear in the expression at (1)
(As it should)
Finally, I found out that this is a very subtle issue because it depends on the version of gfortran and also depends on whether the subroutine is contained in a module or is standalone.
Neither gfortran-4.8 or gfortran-8 can compile successfully the following code:
module mp3
contains
subroutine p(b)
implicit none
integer :: m=4, n=4
integer :: b(m,n)
end subroutine p
end module mp3
But if we consider a standalone subroutine as follows:
subroutine p(b)
implicit none
integer :: m=4, n=4
integer :: b(m,n)
end subroutine p
Then gfortran-4.8 still reject this form, but gfortran-8 accepts this, which may be just a bug in gfortran-8 because further testing (by user5713492) indicates that gfortran-8.1.0 also rejects this form.
In summary, local variables of a subroutine are not allowed in the specification expression of dummy argument arrays.
Using local non-constant variables in specification expressions is not very often needed. So it is not a terrible idea to forbid this usage.

Pass 2d array to subroutine as 1d argument fortran

I want to pass a 2d array to a subroutine and treat this array as a 1d argument. I tried to pass it in this way: subroutine(array(1,:)). This works if I define the arrays explicitly. However, if the arrays are allocatable, I get the following error: Actual argument for 'array' must be ALLOCATABLE
How can I make this work?
Here is some short sample code, which gives the error above:
program array
implicit none
integer,dimension(:,:),allocatable::i
allocate(i(2,2))
i(1,1)=1
call array_method(i(1,:))
contains
subroutine array_method(i)
implicit none
integer,allocatable,dimension(:),intent(in)::i
write(*,*) i(1)
end subroutine array_method
end program array
If I change code to explicitly defined arrays, like below, it works. However, I want to do it with allocatable arrays.
program array
implicit none
integer,dimension(2,2)::i
i(1,1)=1
call array_method(i(1,:))
contains
subroutine array_method(i)
implicit none
integer,dimension(2),intent(in)::i
write(*,*) i(1)
end subroutine array_method
end program array
If I change the argument array in the subroutine to allocatable and leave the passed argument in the main program as explicitly defined array (for example (2,2)), I still get the same error.
In the subroutine array_method (first one), you give the allocatable attribute to the dummy argument. This requires that the actual argument also has that attribute.
However, the actual argument is i(1,:) which is not allocatable, even though i itself is.
Now, depending on what you want to do in the first case, the dummy argument doesn't need to have the allocatable attribute. Unless you want to change the allocation status (which you can't given intent(in)), or use the "real" bounds of i, you can do without it.

Use of unlimited polymorphic type for array operation in Fortran 03/08 (gfortran compiler)

I'd like to realize useful array operations (add element, remove element, different realizations by allocatable/pointer/binary tree structures) by class(*) feature (unlimited polymorphism). I use gfortran 5.0 that should handle such a feature. I need it for not repeating identical code for each type I use.
This should look something like
function add_element(array,element)
class(*),intent(in)::array(:)
class(*),intent(in)::element
class(*)::add_element(size(array)+1)
add_element=[array,element]
end function
The problem is that when I try to use this function with some definite type, I have an error with returning result. I can not assign class(*) to some definite type variable without select type, and I surely don't want to have select type structures every time I use it. And inside a subroutine I should not know anything of types I want to use, because I will create many of them.
I tried some variants with move_alloc, source, tried to use subroutine with intent(out) argument etc. It didn't work. I think it should be defined in argument attributes, the same as size (with source keyword?) but didn't find an example or a definition of such structure in standard. Of course I will study this standard more (I'm not a professional programmer but physicist trying to make my programs testable, checkable and more comfortable to change) and will simply repeat this code now in waiting for better solution, but maybe anybody knows where to search it in the standard or some book? I think this is not only about arrays but use of class(*) at all as I think there should be methods that don't know of types...
Don't know if I should add examples of other not working forms of this subroutine or what it says about the error - or a question will be unfocused. It can be compiled, but in all cases assigning to definite type in call doesn't work. For argument intent(out) or (inout) it can not go from dummy argument to actual argument. Reallocation from source makes an object which has type (and a result of assigning in my example too), but the type is hidden... and I can't use the select type in subroutine because I don't know the type.
Also I don't know constructs that could check "the same type as" or something in this context...
This is not an easy problem You can use select type, but Fortran doesn't have anything like type is(type_of(x)). On the other hand, there are the SAME_TYPE_AS() and EXTENDS
TYPE_OF() intrinsics, but you cannot use them as type guards.
It is necessary to assure, that the dynamic types of both array and element are the same.
I think this is a deficiency in the standard.
But still, there is an error in your approach. You should make the function result allocatable, to be able to allocate it to correct dynamic type:
class(*), allocatable ::add_element(:)
You may think something along the lines of: (UNTESTED! compiles with gfortran-4.9 ifort14)
allocate(add_element(size(array)+1), mold=array)
But how to actually transfer the values I don't know and I am worried it might not be possible without resorting to some dirty tricks.
You cannot even use transfer and that is where I see real deficiency. Eventhough you can call transfer with polymorphic mold
transfer(element, add_element(1))
you have no way to assign it to the array element
add_element(1) = transfer(element, add_element(1))
My opinion is that Fortran lacks an option for the type guards that just ensures that two variables have the same dynamic type.
You may think something along the lines of: (UNTESTED! compiles with gfortran-4.9 ifort14)
function add_element(array,element)
use iso_c_binding
implicit none
class(*),intent(in)::array(:)
class(*),intent(in)::element
class(*), allocatable ::add_element(:)
type(c_ptr) :: tmp
interface
function memcpy(dest, src, n) bind(c)
use iso_c_binding
integer(c_intptr_t),value :: dest, src
integer(c_size_t) :: n
type(c_ptr) :: memcpy
end function
end interface
allocate(add_element(size(array)+1), mold=array)
tmp = memcpy(loc(add_element(size(array)+1)), &
loc(array), &
size(array, kind=c_size_t) * storage_size(array, c_size_t)/8_c_size_t )
tmp = memcpy(loc(add_element(size(array)+1)), &
loc(array(1)), &
storage_size(element, c_size_t)/8_c_size_t )
end function
CLASS(*) is a facility that basically allows runtime type safe but type agnostic storage. You are trying to use it as a compile time type parameterisation mechanism. It isn't terribly appropriate for that, and the language doesn't directly support an alternative means.
Traditionally type parameterisation is done by placing the common parts of the procedures to be parameterised in a separate file, and then including that file as appropriate, perhaps in a module that uses implicit typing to specify the type to be parameterised.
If you must use CLASS(*), you practically need to write and use a wrapper type. If all you are wrapping is basic array operations, then this will be far more trouble than it is worth.
In client code (versus your common procedures) to extract the thing that has been stored you generally need to use SELECT TYPE (you can use pointer assignment if the type of the data has BIND(C) or SEQUENCE, but this isn't type safe).
TYPE :: Wrapper
CLASS(*), ALLOCATABLE :: item
END TYPE Wrapper
FUNCTION add_element(array, element)
TYPE(Wrapper), INTENT(IN) :: array(:)
CLASS(*), INTENT(IN) :: element
TYPE(Wrapper), INTENT(OUT) :: add_element(SIZE(array)+1)
! If you want to enforce type consistency (at runtime)...
IF (SIZE(array) > 0) THEN
IF (.NOT. SAME_TYPE_AS(array(1)%item, element)) THEN
STOP 'Objects not of same type!'
END IF
END IF
add_element(:SIZE(array)) = array
add_element(SIZE(add_element))%item = element
END FUNCTION add_element
FUNCTION get(scalar)
TYPE(Wrapper), INTENT(IN) :: scalar
CLASS(*), ALLOCATABLE :: get
get = scalar%item
END FUNCTION get
...
TYPE(Wrapper), ALLOCATABLE :: array(:)
array = [ Wrapper :: ]
array = add_element(array, 'cat')
array = add_element(array, 'dog')
DO i = 1, SIZE(array)
SELECT TYPE (item => get(array(i)))
TYPE IS (CHARACTER(*))
PRINT "(A)", item
END SELECT
END DO

Resources