SOURCEd allocation for dynamic array handling

I’m trying to find the most compact/clear way to reallocate an array to a different size, keeping both boilerplate and allocations to a minimum and am looking at SOURCEd allocation.

In the example, with gfortran:

  • if the new array is longer, all data is copied from a, and the trailing elements are not initialized (nice)
  • if the new array is shorter, sourcing it to a longer array leads to a segfault.
program compact_resize
   implicit none

   integer :: i,j
   integer, allocatable :: a(:),longer(:),shorter(:)

   a = [(i,i=1,10)]
   print *, 'a=',a

   ! works
   allocate(longer(20), source = a)
   print *, 'b=',longer

   ! segfault
   allocate(shorter(5), source = a)
   print *, 'c=',shorter

end program compact_resize

Is what I’m doing violating any standards? the Intel documentation says " If object is an array, s-spec must appear and the number of s-specs must equal the rank of object, or source-expr must appear and have the same rank as object and the shape of object is that of source-expr". In other words, it seems like the option of BOTH specifying the size of the array and an array source-expr is not considered.

On Metcalf/Reid/Cohen it states that “if allocation is for an array, source-expr may be an array of the same rank, otherwise source-expr must be scalar”. also “because the bounds and shape of the allocated item are not taken from the source, making a clone of an array has to be done [by specifying its bounds]”, like

allocate(a(lbound(b,1):ubound(b,1)), source = b)

So apparently there’s a constraint on the rank, but not on their size, gfortran is doing it right, but of course it’s overflowing the copy when the new array is shorter than the former…

If you know they are shorter, you could apply bounds to the source:

allocate(shorter(5), source = a[:5])

Or more general:

allocate(shorter(new_size), source = a[:min(new_size, size(a))])
1 Like

This program demonstrates an array constructor with an implied do loop and also reshape with pad to grow an array:

program main
implicit none
real, allocatable :: x(:), y(:), z(:)
integer :: i
x = [10.0, 20.0]
y = [x, (0.0,i=1,3)]
z = reshape(x, shape=[5], pad=[0.0])
print "(*(f4.1,1x))", y,z
end program main
! output: 10.0 20.0  0.0  0.0  0.0 10.0 20.0  0.0  0.0  0.0

I have tweeted about using move_alloc to do so more efficiently.

1 Like

Nice alternatives, thank you both. So I’m guessing the best option is still the sourced allocation, but unfortunately, more bound checking lines will always be necessary like

   pure subroutine resize(array,newsize)
        type(mytype), intent(inout), allocatable :: array(:)
        integer, intent(in) :: newsize

        integer :: copysize
        type(mytype), allocatable :: tmp(:)

        copysize = min(newsize,merge(ubound(array,1),0,allocated(array))
        call move_alloc(from=tmp,to=array)

   end subroutine resize