fortranvariable-assignmentallocatable-array

Assignment of allocatable array with itself values


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.


Solution

  • 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.