Warning for explicit-shape array reshaping - arrays

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.

Related

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.

Variables being deleted in Fortran Arrays?

I have a following code, with an abstract type, inherited type and a short program, where I'm creating an object and storing it in an array.
module m
implicit none
type :: container
class(a), allocatable :: item
end type container
type, abstract :: a
integer, public :: num
end type a
type, extends(a) :: b
integer, public :: num2
end type b
end module m
program mwe
use m
implicit none
class(a), allocatable :: o1
class(container), allocatable :: arr(:)
o1 = b(1, 2)
allocate(arr(2))
arr(1) = container(o1)
select type(t => o1)
type is(b)
write(*,*) t%num, t%num2
end select
select type(t => arr(1)%item)
type is(b)
write(*,*) t%num, t%num2
end select
end program mwe
The problem is, that the output looks like this:
1 2
1 0
As can be seen, the same variable stored in the array has the second variable nullified. Why is that happening? Is it because the array is of type a, which only contains the first variable?
I'm compiling the code with ifort version 18.0.3.
I believe
arr(1) = container(o1)
is invalid Fortran 2008. This is an intrinsic assignment statement, but section 7.2.1.2 of the standard says that
In an intrinsic assignment statement, (1) if the variable is polymorphic it shall be allocatable and not a coarray.
As far as I can see, arr(1) is polymorphic but not allocatable, so a standards-compliant compiler should issue an error and abort compilation.
If my reasoning is correct, the fact that Intel Fortran compiler compiles this code is a compiler bug and should be reported to Intel.
As with ripero's answer one could say that any output from the program is valid. However, we can make a simple modification to the code to make it correct Fortran.1 This answer is concerned with this modified version.
I would call this unexpected output and seek the help of the compiler vendor.
Using a structure constructor with polymorphic allocatable components is one of those new areas in Fortran. Compilers may take a while to catch up or do it correctly.
I have tested your code with Intel Fortran 18.0.2 and see the same output.
For your question
Is it because the array is of type a, which only contains the first variable?
No: in the select type part with the output t is a non-polymorphic entity of type b.
You may work around this problem by avoiding using the structure constructor:
arr(1)%item = o1
I also see that Intel compilers before 18.0.2 do something different still.
1 With the declaration
class(container), allocatable :: arr(:)
arr is polymorphic and allocatable. As ripero notes, this means that arr(1), the element of arr is polymorphic. However, as an array element, arr(1) is not itself polymorphic and so may not be on the left-hand side of an intrinsic assignment statement. We can change the code in two ways: provide defined assignment, or make arr not polymorphic. In the code of the question there seems no reason to have the container polymorphic, so I'll consider
type(container), allocatable :: arr(:)
Further, as discussed in comments on the question, if you wish to work with gfortran 8, or earlier, to see what happens, you should also modify the code in the question so that the definition of the derived type container comes after the definition of the derived type a.

How to pass assumed size arrays from fortran90/95 to fortran77 and back?

I'm working on a project, where the results of a numerical simulation program are to be optimized to fit measured behavior. I wrote some freeform Fortran routines to extract specific data and perform some preliminary calculations, which work fine. For the optimization purpose, i plan to use a local search algorithm provided here: http://mat.uc.pt/~zhang/software.html#bobyqa
I pass some arguments like dimensions and parameter vectors into the Fortran 77 routine, and the problem is, that the transferred argument arrays don't reach the other side. Only the first element will show up in an array with dimension 1.
I found some helpful answers in How to use Fortran 77 subroutines in Fortran 90/95? and tried to contain all 77 code in a module but i still don't get it done.
An explicit interface helps to get all variables into level1 f77 subroutine, but when stuff is beeing passed to another (level2), where assumed size arrays are to be constructed, 1-dimensional arrays are generated if at all.
I compile the f77 code first using ifort -c -fixed (and tried -f77rtl), then f90 and link all together.
Why are the assumed size arrays not generated properly?? The test program from vendor works fine!
How can i pass all needed data through and back in a defined way, without using explicit interfaces? Or is there a way to define suitable interfaces?
Here some example code:
program main_f90
use types
implicit none
real(dp) :: array(N)
interface
subroutine sub77_level1(array)
implicit real*8 (a-h,o-z)
real*8, intent(inout) :: array
dimension array(:)
end subroutine
end interface
[...fill array...]
call sub77_level1(array)
end
subroutine sub77_level1(array)
implicit real*8 (a-h,o-z)
integer i1, i2, i3, i4
dimension array(:)
[...modify array...]
call sub77_level2(array(i1), array(i2), array(i3), i4)
return
end
subroutine sub77_level2(array_1, array_2, array_3, i4)
implicit real*8 (a-h,o-z)
dimension array_1(*) array_2(*) array_3( i4, * )
[...modify...]
call sub_f90( <some other arrays, intent(in / out)> )
return
end

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.

FORTRAN - allocatable array in subroutine

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.

Resources