Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
29276 Discussions

Passing unallocated allocatable arrays to subroutines

jirina
New Contributor I
3,628 Views
I am not sure how to do following:

I have two allocatable arrays declared as:
[bash]real*4, allocatable :: Array1(:,:,:), Array2(:,:,:)[/bash]
Later, based on user's input, it can happen that only one of them is allocated; the other one is not, because it is not needed:
[bash]if ( use1 ) allocate ( Array1(nx,ny,nz), stat=ierr )
if ( use2 ) allocate ( Array2(nx,ny,nz), stat=ierr )[/bash]
Now, I have a subroutine which can work with both arrays and does so based on logical variables use1, use2. My question is whether I can declare the subroutine
[bash]subroutine doSomething ( nx, ny, nz, Array1, Array2, use1, use2 )

integer*4 nx, ny, nz

real*4 Array1(nx,ny,nz)
real*4 Array2(nx,ny,nz)

logical*4 use1, use2

if ( use1 ) call doSomethingWith1 ( nx, ny, nz, Array1 )
if ( use2 ) call doSomethingWith1 ( nx, ny, nz, Array2 )[/bash]
and call it
[bash]call doDomething ( nx, ny, nz, Array1, Array2, use1, use2 )
[/bash]
Put another way, I don't know how to handle passing those arrays which were not allocated. I am afraid that declaring them with a specific dimensions in the subroutine is not the right way.

Would anybody have any idea how this could and should be done?
0 Kudos
10 Replies
John4
Valued Contributor I
3,628 Views
You can use the ALLOCATABLE attribute for the array arguments, for example:

!----------------------------------------------------------------
subroutine doSomethingWith1 (Array)
real, allocatable, intent(INOUT) :: Array(:,:,:)
integer :: nx, ny, nz

if (ALLOCATED(Array)) then
nx = UBOUND(Array, DIM = 1)
ny = UBOUND(Array, DIM = 2)
nz = UBOUND(Array, DIM = 3)
else
return
endif
end subroutine
!----------------------------------------------------------------

Notice that nx,ny,nz become redundant as arguments, since you can use the SIZE or UBOUND intrinsics to inquire for their values ---or ignore them if Array is not allocated.

0 Kudos
anthonyrichards
New Contributor III
3,627 Views
Why not just add:

if( ALLOCATED(Array1) )calldoSomethingWith1(nx,ny,nz,Array1)
if( ALLOCATED(Array2) )calldoSomethingElseWith2 (nx,ny,nz,Array2)

and forget the call to doSomething?
0 Kudos
jirina
New Contributor I
3,628 Views
I used a very simplified example. In the original code, I have many arrays and they are passed to various subroutines which use some of the arrays based on what the user specifies. It is not possible to separate the code like you suggested. Anyway, thank you for your suggestion.

I will try using what John suggested; I like the fact that once I allocate or don't allocate an array, I can pass it to any subroutine where it is just declared as
[fxfortran]real, allocatable, intent(INOUT) :: Array(:,:,:)[/fxfortran]
I would like to ask one more question - I tried to google passing allocatable arrays and I found information that "An allocatable array cannot be passed to a procedure when in an un-allocated state. But this can be done with a pointer array." Is this true?
0 Kudos
anthonyrichards
New Contributor III
3,628 Views
What John suggests works, both in CVF and IVF, so long as you provide an explicit interface for DoSomeThingWith1 in the calling program. e.g.

program allocatesub

implicit none

interface
subroutine doSomethingWith1 (Array)
real*4, allocatable, intent(INOUT) :: Array(:,:,:)
end subroutine
end interface

real*4, allocatable :: a1(:,:,:)

print *, 'Hello World'

call dosomethingwith1(a1)
allocate (a1(1,2,3))
call dosomethingwith1(a1)
deallocate(a1)

end program allocatesub

subroutine doSomethingWith1 (Array)
real*4, allocatable, intent(INOUT) :: Array(:,:,:)
integer :: nx, ny, nz

if (ALLOCATED(Array)) then
nx = UBOUND(Array, DIM = 1)
ny = UBOUND(Array, DIM = 2)
nz = UBOUND(Array, DIM = 3)
print *, 'Array allocated, dimensions are: ',nx,', ',ny,', ',nz
else
print *, 'Array NOT allocated'
return
endif

end subroutine
0 Kudos
John4
Valued Contributor I
3,628 Views

That restriction was lifted for Fortran 95+. Keep in mind that when you pass an allocatable array to a procedure, the INTENT applies to the allocation status ---e.g., INTENT(OUT) means that the array is always deallocated on procedure entry.

And, as anthonyrichards said, you'll always need an explicit interface whenever arguments have the allocatable attribute. Such interface is provided by an interface block or by placing the procedure(s ) in a module.

You can check the following link for more details:

http://fortranwiki.org/fortran/show/Allocatable+enhancements

0 Kudos
karma_kid
Beginner
3,628 Views
On a related note (possibly a different thread), I'm wondering why the Intel Fortran 90 compiler will let it go unnoticed by default. Only when I turn on the runtime checking does it flag a fatal error and cease execution. Shouldn't it either
  1. flag the calling routine at compile time with a warning/error, or
  2. issue a runtime warning instead of a severe error?
In other words, the generated code should either fail in both cases or should run the same to completion in both cases. I do not expect a runtime check to change execution behavior (outside of showing the additional requested information for "checking" and its additional wallclock time). Am I thinking sensibly? Here is what I am seeing:

yr-fe1.lanl.gov> cat alloc.f90

[fortran]Program testalloc
!
! When compiled with Intel's "-check pointers" or
! "-C" options, this fails at runtime. Otherwise 
! it runs fine.
!
real, dimension (:), allocatable :: array
call subrout(array)
stop
end

subroutine subrout(receipt)
print *, ' success'
return
end[/fortran]


yr-fe1.lanl.gov> ifort -V
Intel Fortran Compiler for applications running on Intel 64,
Version 10.0 Build 20070426 Package ID: l_fc_p_10.0.023
Copyright (C) 1985-2007 Intel Corporation. All rights reserved.

yr-fe1.lanl.gov> ifort alloc.f90
yr-fe1.lanl.gov> a.out
success
yr-fe1.lanl.gov> ifort -check pointers alloc.f90
yr-fe1.lanl.gov> a.out
forrtl: severe (408): fort: (8): Attempt to fetch from allocatable
variable ARRAY when it is not allocated

...truncated...
0 Kudos
TimP
Honored Contributor III
3,628 Views
As you have demonstrated, run-time checking can very well change the behavior of an invalid program. You have added an option which requires checking validity of the argument to a subroutine which previously was permitted to ignore the argument.
0 Kudos
Steven_L_Intel1
Employee
3,628 Views
The program is not legal Fortran - it violates the following rule: "Except in references to intrinsic inquiry functions, if the dummy argument is not allocatable and the actual argument is allocatable, the actual argument shall be allocated." Our check for this is enabled only when /check:pointer is on, which it is not by default.

In your test case, since the subroutine never touches the argument "receipt", it appears to succeed. But it is still invalid.
0 Kudos
karma_kid
Beginner
3,628 Views
Wow, quick response, thank you both.

The text I found in the standard (and in the Metcalf&Reid book) was that the "ALLOCATABLE attribute must not be used on a dummy argument". I get the same behavior from the Intel compiler when declaring the "receipt" array to have the allocatable attribute (ie. real, dimension (:), allocatable :: receipt). Of course, the standard never dictates when a compiler should issue an error message, so questions arise about consistent behavior. Should the generated code behave the same regardless of the runtime check? For illegal code, should a compiler user expect to see an error msg at compile time rather than run time? If the error is severe, why wait so long? Why would a severe error message be optional? What other severe errors do I miss by using only default compilation parameters?

The compiler itself is neither right nor wrong. We're just wondering why it behaves this way.
0 Kudos
Steven_L_Intel1
Employee
3,628 Views
The book you're reading is for Fortran 95. Fortran 2003 allows ALLOCATABLE dummy arguments (and function return values and derived type components.)

The rule you are breaking is not one the compiler can see at compile time and isn't required to diagnose at all.

You may be missing a lot. Another option that can be useful is adding /gen_interface and /warn:interface. (In 11.1 you don't need /gen_interface). I'll comment that in 10.0 this feature is somewhat buggy, but it is very helpful. As of 10.1 (I think) it is the default in new VS projects.
0 Kudos
Reply