2

I'm struggling with some Modern Fortran wrappers to some MPI scatter/gather routines. I am trying to have a wrapper interface that only has an array on input and returns the MPI-operated result on output, for several derived types, doing something like this:

type(mytype), allocatable :: chunk(:),whole(:)

! [...] chunk descends from previous parts of the code

! Get global array
whole = gatherv(array=chunk,receiver_node=cpuid)

I'm doing this using functions that return allocatable arrays. However, I'm getting segmentation fault on both gcc 6.2.0 and gcc 7.1.0 whenever I return a non-allocated result.

The reason I need a non-allocated result is that sometimes I need to gather the whole array only on a specified CPU, so I don't want to waste memory on all other nodes: the receiver node returns an allocated array with the data, and all other nodes receive an empty and deallocated array.

This is sample code that reproduces the issue:

program test_allocatable_fun
    implicit none

    integer, allocatable :: my_array(:)
    integer :: n

    n = 3; my_array = unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
    n =-3; my_array = unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
    n = 5; my_array = unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
    n = 0; my_array = unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)

    return


    contains

    function unallocated_array(n) result(array)
        integer, intent(in) :: n
        integer, allocatable :: array(:)
        integer :: j
        if (n>0) then
            allocate(array(n))
            array(:) = [(j,j=1,n)]
        else
            if (allocated(array)) deallocate(array)
        end if
    end function unallocated_array

end program test_allocatable_fun

The segmentation fault happens at the assignment line, i.e.:

my_array = unallocated_array(n)

Has any of you had the same issue before? Or, am I violating anything in the standard? I can't see why a function returning an allocatable array should be forced to have the return value allocated. Isn't it the same as having an intent(out) dummy variable in a subroutine?

Federico Perini
  • 1,414
  • 8
  • 13
  • 1
    While I think some more about this, why not allocate the array to `0` size ? I suspect that might be the right way to go. That way your code doesn't need to be checking all the time for the allocation status of the returned array. – High Performance Mark Jan 08 '18 at 17:26
  • 1
    Thanks @HighPerformanceMark for the comment - allocating to a 0-sized array fixes the issue. Apart from being able to use the `size()` intrinsic what would the difference be between an unallocated and a 0-size, allocated array in memory? – Federico Perini Jan 08 '18 at 17:29
  • 1
    There's very little you can do with an unallocated array (or scalar) other than test its allocation status. As you've discovered if you're not sure an array is allocated or not then every time you touch it you have to check its status and deal with the two return possibilities along different code paths. Allocated-to-size-0 arrays can be treated, pretty much, as any other allocated array. – High Performance Mark Jan 08 '18 at 17:46

2 Answers2

3

A function result is not the same as a dummy argument with the intent(out) attribute. It differs in a significant way here in that a non-pointer function result must always be defined when execution of the function terminates. This is covered by Fortran 2008 12.6.2.2 p4.

It is necessary, but not sufficient, for an allocatable function result (any object) to be allocated to be defined.

To some extent you can consider this in the way that a function result is always referenced (otherwise the function wouldn't be executed). An actual argument not defined may also not be referenced, but such referencing wouldn't be "automatic".

As mentioned in comments, the function result may be allocated to be an array of size zero. Zero-sized arrays are always of defined value.

You can see some comparison of zero-sized arrays and not-allocated arrays in this other question.

francescalus
  • 30,576
  • 16
  • 61
  • 96
0

I suspect any allocatable array in a main program contains data of a global nature. I usually put such variables in a module. This way this variable does not need to be passed around and can be allocated and deallocated in the main program, subroutines, and/or functions.

Since the array is the only return value, I changed it to a subroutine. How does this work for you? P.S. 'implicit none' should be in every module, program, function, and subroutine.

module fun
   implicit none
   integer, allocatable :: my_array(:)
end module fun

program test_allocatable_fun
   use fun
   implicit none

   integer :: n

   n = 3; call unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
   n =-3; call unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
   n = 5; call unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)
   n = 0; call unallocated_array(n); print *, 'n=',n,' allocated(array)=',allocated(my_array)

   return

   contains

subroutine unallocated_array(n)

    use fun
    implicit none

    integer, intent(in) :: n
    integer :: j
    if (n>0) then
        allocate(my_array(n))
        my_array(:) = [(j,j=1,n)]
    else
        if (allocated(my_array)) deallocate(my_array)
    end if
end subroutine unallocated_array

end program test_allocatable_fun
Dan Sp.
  • 1,419
  • 1
  • 14
  • 21