Reputation: 151
I would like to know whether it is possible in modern Fortran to assign an allocatable array using itself, or part of it, to do it. Here it is a simple example:
module modu
implicit none
type :: t
integer :: i
end type
contains
subroutine assign(a,b)
type(t), allocatable, intent(out) :: a(:)
type(t), intent(in) :: b
allocate(a(1))
a(1) = b
end subroutine
end module
!----------------------
program test
use modu
implicit none
type(t), allocatable :: a(:)
allocate(a(1))
a(1)%i = 2
call assign(a, a(1))
print*, a(1)%i
end program
This code gives the corect answer with ifort 18 and returns "Segmentation fault" with gfortran 7.4.
NOTE: The original problem was a bit more complex since call assign(a, a(1))
should be replaced by call assign(a, a(1)+b)
having operator + properly overloaded, but the conclusion (respect ifort and gfortran) is the same.
NOTE: In the thread checking for self-assignment in fortran overloaded assignment, @IanH makes a distinction between call assign(a,a)
and call assign(a,(a))
but I believe that it does not solve this problem because I have allocatable arguments.
NOTE: In the thread Automatic array allocation upon assignment in Fortran, @francescalus explains automatic allocation on intrinsic assignment but again I believe that it does not apply here.
Upvotes: 1
Views: 1137
Reputation: 60008
This call
call assign(a, a(1))
is forbidden by the aliasing rules of Fortran. You are passing the same value in different arguments and neither is pointer
or target
and you are modifying one of them.
Then, the compiler deallocates a
because it is intent(out)
. That means that the old a(1)
no longer exists. But b
still points there. Then you allocate a new a
somewhere else. Then you try to use b
in
a(1) = b
and that is bound to fail because it points to some undefined piece of memory. You are just lucky with Intel Fortran, but your code is illegal. Maybe Intell allocated the new a
to the same place where the old a
was, but that is pure luck.
It will work if you do
type(t), allocatable, intent(inout) :: a(:)
type(t), value :: b
deallocate(a)
allocate(a(1))
a(1) = b
I am not sure, why it crashes with intent(out)
and value
. It may be a problem with the compiler, reported as bug 92178.
Upvotes: 6
Reputation: 32366
It is possible to assign to an allocatable array using array elements of that same array. For example, for a
allocatable
a = [a(2), a(1)]
is valid and has the expected result. This is the same whether a
is an intrinsic type (for intrinsic assignment) or of derived type (for intrinsic or defined assignment). The right-hand side is treated as evaluated fully as an expression before the left-hand side becomes affected.
However, using call assign(a,a(1))
is not the same thing as this assignment.
The point in the answer by IanH you reference is relevant here. Defined assignment would be like call assign(a,(a(1)))
rather than call assign(a,a(1))
.
This is important because of the aliasing restrictions mentioned in Vladimir F's answer which I won't repeat. Using call assign(a,(a(1)))
removes the aliasing because (a(1))
is an expression with value that of the array element rather than the array element itself. This is similar to the value
attribute Vladimir F mentions, but without creating a definable entity.
Defined assignment uses the (rhs)
construct precisely to avoid this aliasing issue.
Regarding your gfortran results, IanH created a bug report for GCC in response to that other question. It may well be relevant in explaining the surprise you have here.
Upvotes: 4