Was this feature made available in a recent Fortran Standard? - arrays

I was unpleasantly surprised to find that a code I work with would not run when built with gcc v. 4.4.6 because of array size mismatches. It worked fine when built with gcc v. 4.7.3. I created a minimal working example to show the root of the problem:
program main
implicit none
integer, allocatable, dimension(:,:) :: array_a
integer, allocatable, dimension(:,:) :: array_b
allocate(array_a(5,2))
allocate(array_b(2,1))
array_a = 1
array_b = array_a
print *, array_a
print *, array_b
end program main
When built with gcc v. 4.4.6, it crashes during runtime with error:
At line 13 of file main.f90 Fortran runtime error: Array bound
mismatch, size mismatch for dimension 1 of array 'array_b' (1/4)
When build with gcc v. 4.7.3, it produces the following output:
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
Note, it automatically resized 'array_b' to match the size of 'array_a'. Is this a 'feature' that is offered by a newer Fortran standard that I'm seeing?

You are using a Fortran 2003 feature - automatic array reallocation on assignment. It was not yet implemented in gcc-4.4.
This feature means that the array b, which was allocated to a nonconforming shape before is automatically reallocated to the shape of the right hand side of the assignment. You have to use recent compiler versions (not only GCC), for Fortran 2003 features.

As Vladimir says, it's a feature of Fortran 2003. If you look at Section 7.4.1.3 of the 2003 working document you'll see,
If variable is an allocated allocatable variable, it is deallocated if expr is an array of different shape or any of the corresponding length type parameter values of variable and expr differ. If variable is or becomes an unallocated allocatable variable, then it is allocated with each deferred type parameter equal to the corresponding type parameters of expr, with the shape of expr, and with each lower bound equal to the corresponding element of LBOUND(expr).
Note 7.36 shows that if you want array_b to keep its shape, you need to declare the line as
array_b(1:2,1) = array_a(3:4,2)
of whatever elements of array_a you want.

Related

non-contiguous data and temporary array creation

A similar question was answered in Fortran runtime warning: temporary array. However, the solutions do not quite help me in my case.
Inside a subroutine, I have a subroutine call as:
subroutine initialize_prim(prim)
real(kind=wp), dimension(2, -4:204), intent(out) :: prim
call double_gaussian(prim(1, :))
end subroutine initialize_prim
subroutine double_gaussian(y)
real(kind=wp), dimension(-4:204), intent(out) :: y
integer :: i
do i = -4, 204
y(i) = 0.5 * ( &
exp(-((r(i) - r0))**2) + exp(-((r(i) + r0)/std_dev)**2))
end do
end subroutine double_gaussian
This gives an error message saying that fortran creates a temporary array for "y" in "double_gaussian". Having read a bit about continguous arrays, I understand why this error appears.
Now, looking at my whole program, it would be very tedious to invert the order of the arrays for "prim", so that solution is not really possible.
For creating assumed-shapes in "double_gaussian", I tried doing,
real(kind=wp), dimension(:), intent(out) :: y
integer :: i
do i = -4, 204
y(i) = 0.5 * ( &
exp(-((r(i) - r0))**2) + exp(-((r(i) + r0)/std_dev)**2))
end do
end subroutine double_gaussian
This, however, causes fortran to crash with the error message
"Index '-4' of dimension 1 of array 'y' below lower bound of 1".
It seems that for the assumed-shape format, the indexing is nonetheless assumed to start with 1, whereas it starts at -4 as in my case.
Is there a way to resolve this issue?
I think that you have perhaps misinterpreted a compiler warning as an error. Usually compilers issue a warning when they create temporary arrays - it's a useful aid to high-performance programming. But I'm not sure a compiler ever regards that as an error. And yes, I understand why you might not want to re-order your array just to avoid that
As for the crash - you have discovered that Fortran routines don't automagically know about the lower bounds of arrays which you have carefully set to be other than 1 (nor their upper bounds either). If it is necessary you have to pass the bounds (usually only the lower bound, the routine can figure out the upper bound itself) in the argument list.
However, it rarely is necessary, and it doesn't seem to be in your code - the loop to set each value of the y array could (if I understand correctly) be replaced by
y = 0.5 * (exp(-((r - r0))**2) + exp(-((r + r0)/std_dev)**2))
PS I think that this part of your question, about routines not respecting other-than-1 array lower bounds, is almost certainly a duplicate of several others asked hereabouts but which I couldn't immediately find.

Dynamic allocation of string arrays in Fortran does not resize

Consider the following Fortran program:
program test
character(len=:), allocatable :: str
allocate(character(3) :: str)
print *, len(str)
str = '12345'
print *, len(str)
end program
When I run this I get the expected result:
3
5
That is, the character string was resized from 3 to 5 when str was set to '12345'. If, instead, I use an array of dynamic strings this is not true. Example:
program test
character(len=:), allocatable :: str(:)
allocate(character(3) :: str(2))
print *, len(str(1)), len(str(2))
str(1) = '12345'
print *, len(str(1)), len(str(2))
end program
When I run this I get:
3 3
3 3
So the set of str(1) did not change the length the string. I get the same behavior with ifort 16.0.2 and gfortran 5.3.1. My question is this behavior consistent with the latest Fortran standard or is this a bug in the compilers?
This is the correct behaviour. An element of an allocatable array is not itself an allocatable variable and cannot be re-allocated on assignment (or in any other way). Also, all array elements must have the same type - including the string length.
Expanding on #Vladimir F's answer, you could achieve what you have in mind with a few changes to your code, like the following (which creates a jagged array of character vectors):
program test
type :: CherVec_type
character(len=:), allocatable :: str
end type CherVec_type
type(CherVec_type) :: JaggedArray(2)
allocate(character(3) :: JaggedArray(1)%str)
allocate(character(3) :: JaggedArray(2)%str)
print *, len(JaggedArray(1)%str), len(JaggedArray(2)%str)
JaggedArray(1)%str = "12345"
print *, len(JaggedArray(1)%str), len(JaggedArray(2)%str)
end program
which creates the following output:
$gfortran -std=f2008 *.f95 -o main
$main
3 3
5 3

What is really going on with zero sized arrays in Fortran?

I have seen several questions about passing unallocated arrays into functions or subroutines that cause runtime faults where the recommended solution was to allocate it in the calling procedure with zero length. I did some experiments:
program test
integer, allocatable :: A(:)
allocate(A(3))
print*, size(A)
deallocate(A)
allocate(A(-5:-3))
print*, size(A)
deallocate(A)
allocate(A(0))
A(0) = 9
print*,A(0),'Size:',size(a)
deallocate(a)
allocate(A(-4))
print*,size(A)
deallocate(A)
allocate(A(-4:-4))
print*,size(A)
end
The first one is obvious. An array of length 3. The second one takes advantage of a cool feature of Fortran, defining your own index limits. It is an array of length 3 with the elements A(-5), A(-4), and A(-3).
Third one gets dicey. I allocate it with a zero. I can assign a value! It prints: 9 Size: 0
The fourth one prints a size of zero too!
Apparently, the correct way to allocate an array of length 1 with an index of -4 is the last one.
Question: On the third and fourth ones, did I just write to memory that likely belongs to some other variable (except of course this example has no other variables)?
The third fragment is invalid given the attempt at defining and referencing the element A(0). A zero sized array has no elements (otherwise its size would not be zero) - this includes there being no element that happens to have index zero.
There is no such invalid definition or reference in the fourth fragment. The allocated array again has zero size (which is what the language says happens when you specify an array with a dimension that has an upper bound that is less than the lower bound (the default lower bound is one if otherwise unspecified)), but nothing interesting is then done with that allocated array.
You could construct an equivalent example without using allocatable objects.
Apart from that the code is invalid, if the question is "whether A(0) is accessing some memory region that should not be accessed", I guess it is very likely so. Although the result is compiler-dependent (and does not prove anything), we can see some info using c_loc, for example,
program test
use iso_c_binding, only: c_loc
implicit none
integer, target :: i
integer, allocatable, target :: X(:), A(:), B(:)
allocate( X( 1 ), A( 0 ), B( 1 ) )
print *, "X:"
do i = 0, 2
print *, i, c_loc( X( i ) ), X( i )
enddo
print *, "A:"
do i = 0, 2
print *, i, c_loc( A( i ) ), A( i )
enddo
print *, "B:"
do i = 0, 2
print *, i, c_loc( B( i ) ), B( i )
enddo
end
Then gfortran-7.2 (with no option) gives
X:
0 140362656006716 0
1 140362656006720 0 <-- (*1)
2 140362656006724 -1879048192
A:
0 140362656006732 -1879048192
1 140362656006736 0
2 140362656006740 -1879048192
B:
0 140362656006748 -1879048192
1 140362656006752 0 <-- (*2)
2 140362656006756 -1879048192
and Oracle Studio fortran 12.5 (with no option) gives
X:
0 20258316 0
1 20258320 0 <-- (*1)
2 20258324 0
A:
0 20258348 0
1 20258352 0
2 20258356 0
B:
0 20258380 0
1 20258384 0 <-- (*2)
2 20258388 0
where the valid memory region allowed for us (= user) to access is only (*1) and (*2). Since the array elements above are accessed contiguously on memory, the memory location other than (*1) and (*2) may represent some other data/metadata/buffer etc, and anything could happen if we modify those values inadvertently (including WW3 or more recently, huge digital currency theft..?). As well known, we can check this usually with an option like gfortran-7 -fcheck=all, which gives
X:
At line 11 of file test.f90
Fortran runtime error: Index '0' of dimension 1 of array 'x' below lower bound of 1
or f95 -C test.f90 (for Oracle)
X:
****** FORTRAN RUN-TIME SYSTEM ******
Subscript out of range. Location: line 11 column 31 of 'test.f90'
Subscript number 1 has value 0 in array 'X'
We get similar errors for A(0), so it is not allowed to access A(0) (simply because A is an empty array). (The exception to this is when A is accessed via argument or storage association etc, but I guess this is another story...)

Creating a subset of an array of derived type in Fortran doesn't work, what am I missing?

I am having a problem trying to use subsets of an array containing instances of a type in Fortran. Creating the subset renders the contents garbage. Essentially it boils down to:
class(myType), allocatable :: instances(:)
...allocate/initialize instances here...
doSomethingWithInstances( instances ) ! Works
doSomethingWithInstances( instances((/1,2/)) ) ! Doesn't work
Here is a complete code that reproduces the problem, in this code the correct output is the values of the integers assigned to each of the instances i.e "3, 8, 16", when the subroutine is called on the subset (/1,2/) of the array it should therefore print "3, 8" however it instead prints "3, 192552":
module test
! Simple type contains integer
type :: myType
integer :: n
end type
contains
! Output the integer for myType t
subroutine saySomething(t)
class(myType) :: t
print *, t%n
end subroutine
end module
program main
use test
type(myType), allocatable :: instances(:)
! Declare an array of myType
allocate(instances(3))
instances(1) = myType(3)
instances(2) = myType(8)
instances(3) = myType(16)
! call saySomething for each element
! on the direct array and on the subset
! elements 1 and 2
print *, "Working:"
call saySomethingArray(instances)
print *, "Broken:"
call saySomethingArray(instances((/1,2/))) ! Here is the problem
contains
! Call saySomething on each element of the input array
subroutine saySomethingArray(instances)
class(myType) :: instances(:)
integer :: i
do i=1,size(instances)
call saySomething(instances(i))
enddo
end subroutine
end program
I wondered if it wasn't copying things correctly or something when it creates the subset, but I couldn't figure it out. Any help would be greatly appreciated :) cheers
In these cases please always tell us what is your compiler, its version and all flags used for compiling. This is a compiler bug. I can reproduce it with GCC 7. It runs correctly with Intel Fortran 16.
As a workaround, the error will go away if you declare instances in saySomethingArray as type instead of class. Also, printing the array as an array expression works correctly.
The problem also appears for other kinds of array expressions, not just vector subscripts.
Reported to GCC as https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84074. Maybe it will turn up to be a duplicate of some earlier bug. This is the MCVE ([mcve]):
type :: t
integer :: n
end type
type(t) :: array(2) = [t(1),t(2)]
call sub(array((/1,2/)))
contains
subroutine sub(a)
class(t) :: a(:)
integer :: i
print *, "loop a(i) :"
do i=1,size(a)
print *,a(i)%n
enddo
print *, "a%n :",a%n
print *, "a(1:size(a))%n :",a(1:size(a))%n
end subroutine
end program
Output:
> gfortran-7 vecsubs2.f90
> ./a.out
loop a(i) :
2
0
a%n : 1 2
a(1:size(a))%n : 1 2

ifort -> gfortran: array - Incompatible ranks 2 and 1 in assignment at (1)

I'm trying to move below Intel Fortran line to gfortran, but I get following error:
DOUBLE PRECISION, DIMENSION(0:0,0:0) :: value = (/ -999D99 /)
Incompatible ranks 2 and 1 in assignment at (1)
If I understand it correctly, we are creating a 2-dim array with 1 element. I came with following fix. Is this standard conforming?
DOUBLE PRECISION, DIMENSION(0:0,0:0) :: value = reshape ((/-999D99/), shape(value))
It is not allowed to make an assignment (even in initialization) between arrays of different ranks. Therefore the line
...DIMENSION(0:0,0:0) :: value = (/ -999D99 /)
is illegal.
Reshaping the right hand side to an array of rank 2
...DIMENSION(0:0,0:0) :: value = reshape ((/-999D99/), shape(value))
is a standard conforming solution, but it is easier to assign a scalar:
...DIMENSION(0:0,0:0) :: value = -999D99
Of course, this will work only if you have just 1 value. It will be assigned to all elements of the array on the left hand side.

Resources