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

Function with an allocatable array as the result

Robert_van_Amerongen
New Contributor III
2,202 Views
I do have some problems with this issue, I made a small example:

[fortran] MODULE ac_mod CONTAINS FUNCTION make_a(m,l) REAL,ALLOCATABLE :: make_a(:) INTEGER,INTENT(out) :: l, m INTEGER :: n = 4 INTEGER :: k ALLOCATE(make_a(1:n)) make_a = [(REAL(k),k=1,n)] write(*,*) 'in func', make_a l=10 m=20 RETURN END FUNCTION make_a END MODULE ac_mod ! PROGRAM ac USE ac_mod IMPLICIT none REAL,ALLOCATABLE :: a(:) INTEGER :: l, m ! INTEGER :: ios ! ALLOCATE(a, SOURCE = make_a(l,m)) ! <<< case 1 problem line ! ALLOCATE(a, SOURCE = make_a(l,m),STAT=ios) ! <<< case 2 problem line ! ALLOCATE(a(:), SOURCE = make_a(l,m),STAT=ios) ! <<< case 3 problem line ! ALLOCATE(a(*), SOURCE = make_a(l,m),STAT=ios) ! <<< case 4 problem line WRITE(*,*) 'results',a WRITE(*,*) 'returned arguments:',l, m ! WRITE(*,*) 'ios of allocate: ',ios WRITE(*,*) 'status allocated a:',ALLOCATED(a) READ(*,*) END PROGRAM [/fortran]An allocatable array A becomes allocated and filled with the result of the function make_a that is a function with an allocatable array as a function result.

In this form the program runs fine. The results of A are correct. The compiler, however, generates a warning #8199 that a shape specification for a must be supplied in the ALLOCATE statement.
If I add the STAT in the allocate statement, (case 2 line), this warning dissappears. Strange!
I tried an explicit shape specification (case 3 line) but the compiler gives error #6726 telling that an explicit shape specification is incorrect. Finally, if I tryan assumed size array (case 4 line), the compiler gives an internal compiler error.

So, I am afraid thateither I do not understand how to use the ALLOCATE statement in this situation properly, or the compiler lost its way, or both. Who has an idea what happens here?

Robert
0 Kudos
15 Replies
Les_Neilson
Valued Contributor II
2,202 Views
As a starter the ICE should be logged for fixing.

I am certainly no standards expert butto myunderstanding of F2003
" stat_variable, source_expr, and errmsg_variable must not be allocated within the ALLOCATE statement in which they appear."
(emphasis mine.)

SoI thinkthat your make_a function cannot appear in the allocate statement.

Les
0 Kudos
Steven_L_Intel1
Employee
2,202 Views
I don't get any messages for cases 1 or 2 in the current compiler (Update 9). I do get an internal compiler error for cases 3 and 4, but those are invalid syntax. (Still, a proper error message should be given - I will report that to the developers. ID is DPD200180670)

There is nothing wrong with the allocate in the function itself - that's legal F2003.
0 Kudos
Les_Neilson
Valued Contributor II
2,202 Views

There is nothing wrong with the allocate in the function itself - that's legal F2003.


Ah, so I misunderstood the F2003 notes on Allocate. :-(
not that I am surprised by that but I thought that because the functionresult was also declared as allocatable then it was non-standard.

I live and learn.
Les

0 Kudos
Steven_L_Intel1
Employee
2,202 Views
That is part of the "TR" that also included allocatable derived type components and dummy arguments. All three were incorporated into Fortran 2003.
0 Kudos
Robert_van_Amerongen
New Contributor III
2,202 Views
Steve and other: thanks for the reply.

I forgot to mention that the warning #8199 is solely due to the Fortran 2003 syntax requirement. (/stand:f03) Removing that requirement does the warning disappear. The question remains what should be the proper syntax so that the compiler does not complain.

Robert
0 Kudos
Steven_L_Intel1
Employee
2,202 Views
Thanks for the additional info.

In Fortran 2003, constraint C628 says, "An allocate-shape-spec list shall appear if and only if the allocate-object is an array."

In Fortran 2008, the corresponding constraint C633 says, "If allocate-object is an array either allocate-shape-spec-list shall appear or source-expr shall appear and have the same rank as allocate-object. If allocate-object is scalar, allocate-shape-spec-list shall not appear."

What we have here is a change between F03 and F08 where F08 allows the shape to be taken from the SOURCE= expression but F03 does not. You want the F08 behavior, which the compiler does provide. In the current version, restricting diagnostics to extensions to F08 is not yet supported. I note that while it will be supported in a future release, the current development version doesn't suppress the diagnostic here - I will report that. I also notice that if you add STAT=, the diagnostic goes away, which is really weird. I will report that too.

You can either supply the shape for the allocate, add STAT=, or suppress diagnostic 8199 (under Diagnostics > Disable Specific Diagnostics, put in 8199.
0 Kudos
Robert_van_Amerongen
New Contributor III
2,202 Views
Steve,

this makes thing very clear to me.
Thanks for yourhelp.

Robert
0 Kudos
Steven_L_Intel1
Employee
2,202 Views
These issues have been fixed for a release later this year.
0 Kudos
Robert_van_Amerongen
New Contributor III
2,202 Views
Steve, Thanks.

Robert
0 Kudos
Amalia_B_
Beginner
2,202 Views

I have a follow-up question to this old thread.  How would you be able to modify the simple code at the top, given by Robert, to have the returned array "a" start with a lower bound of 0?  Just modifying the allocation to "make_a(0:n)" in the function is fine, but how do you keep that lower bound indexing on return into the main program for the allocatable array "a"?   When I try to do this by the simple change in the function I get the following error:

Error 1  error #6726: An explicit-shape-spec in an ALLOCATE statement is incorrect.    
 

Amalia

0 Kudos
FortranFan
Honored Contributor III
2,202 Views

amalia.barrios@navy.mil wrote:

I have a follow-up question to this old thread.  How would you be able to modify the simple code at the top, given by Robert, to have the returned array "a" start with a lower bound of 0?  Just modifying the allocation to "make_a(0:n)" in the function is fine, but how do you keep that lower bound indexing on return into the main program for the allocatable array "a"?   When I try to do this by the simple change in the function I get the following error:

Error 1  error #6726: An explicit-shape-spec in an ALLOCATE statement is incorrect.    
 

Amalia

Is this what you're looking for?  You can do the specification of lower bound of your array on the caller side.

   MODULE m

      !..
      IMPLICIT NONE

      PRIVATE

      PUBLIC :: CreateRealArray

   CONTAINS

      FUNCTION CreateRealArray(ArrSize) RESULT(NewArray)

         !.. Argument list
         INTEGER, INTENT(IN) :: ArrSize
         !.. Function result
         REAL, ALLOCATABLE :: NewArray(:)

         !.. Local Variables
         INTEGER :: Istat
         INTEGER :: I

         IF (ArrSize > 0) THEN
            ALLOCATE(NewArray(1:ArrSize), SOURCE=[( REAL(I), I = 1, ArrSize )], STAT=Istat)
         ELSE
            ALLOCATE(NewArray(1), SOURCE=[( 0.0 )], STAT=Istat)
         END IF

         RETURN

      END FUNCTION CreateRealArray

   END MODULE m

   PROGRAM p

      USE m, ONLY : CreateRealArray

      IMPLICIT NONE

      !.. Local variables
      REAL, ALLOCATABLE :: a(:)
      INTEGER :: Istat

      !..
      ALLOCATE (a(0:4), SOURCE=CreateRealArray(5), STAT=Istat)

      IF (Istat == 0) THEN
         PRINT *, " a = ", a
         PRINT *, " SIZE of a = ", SIZE(a)
         PRINT *, " Lower Bound of a = ", LBOUND(a)
         PRINT *, " Upper Bound of a = ", UBOUND(a)
      ELSE
         PRINT *, " Allocation of a failed. STAT = ", Istat
      END IF

      STOP

   END PROGRAM p

The program result is

  a =  1.000000 2.000000 3.000000 4.000000
 5.000000
  SIZE of a =  5
  Lower Bound of a =  0
  Upper Bound of a =  4
Press any key to continue . . .

 

0 Kudos
IanH
Honored Contributor III
2,202 Views

The lower bound of the value that results from evaluating an array function result is always one, regardless of the lower bound of the result variable prior to the end of execution of the function.

(The result of evaluating an array function is an array value, array values are always considered to have a lower bound of one, array data objects (array names or array components) are the things that can have bounds other than one). 

So if you want the main program to have a different lower bound then you have to specify that in the allocate statement in the main program.

MODULE m
  IMPLICIT NONE
CONTAINS
  FUNCTION make_a()
    REAL, ALLOCATABLE :: make_a(:)
    ALLOCATE(make_a(10))
    make_a = 1.0
  END FUNCTION make_a
END MODULE m

PROGRAM p
  USE m
  IMPLICIT NONE
  REAL, ALLOCATABLE :: a(:)
  ALLOCATE(a(0:9), SOURCE=make_a())
  PRINT *, LBOUND(a)
END PROGRAM p

On the other hand, if make_a returned an object of derived type with an array component, then the bounds are carried across to the component in the main program.  This is because the bounds of a allocatable component are considered part of the value of an object of derived type (versus the bounds of an array value of derived type - which is just a different case of the first paragraph).

MODULE m2
  IMPLICIT NONE
  TYPE btype
    REAL, ALLOCATABLE :: component(:)
  END TYPE btype
CONTAINS
  FUNCTION make_b()
    TYPE(btype) :: make_b
    ALLOCATE(make_b%component(0:9))
    make_b%component = 1.0
  END FUNCTION make_b
END MODULE m2

PROGRAM p
  USE m2
  IMPLICIT NONE
  TYPE(btype) :: b
  b = make_b()
  PRINT *, LBOUND(b%component)
END PROGRAM p

 

0 Kudos
Amalia_B_
Beginner
2,202 Views

Thanks FortranFan and IanH for the sample code, however, in your examples it seems that I have to know APRIORI  the size of "make_a" before I can declare the array "a" with a lower bound of 0.  In the original sample the upper bound is determined within the function itself, so the size carries back to the main program but not the lower bound, apparently.  I'm looking to set "a" to the exact size (lower & upper bounds) via the "SOURCE =" attribute, where the size is determined within the function.

Looks like there's no easy way to do this without requiring an explicit interface, but a work around may be the derived type sample, correct?

Amalia

0 Kudos
IanH
Honored Contributor III
2,202 Views

Consider not using a function - make the thing that you want allocated with particular bounds an actual argument that corresponds to a dummy allocatable array argument with the intent(out) attribute.

As a bit of a guideline (reasonable exceptions exist), if a function is never going to be used for anything bar the right hand side of an assignment statement, then you should reconsider whether the thing that is a function should be a function.

(In the original example, the function modifies its arguments.  I consider that an anathema in any context bar C interoperability.)

0 Kudos
FortranFan
Honored Contributor III
2,202 Views

IanH wrote:

..

As a bit of a guideline (reasonable exceptions exist), if a function is never going to be used for anything bar the right hand side of an assignment statement, then you should reconsider whether the thing that is a function should be a function.

(In the original example, the function modifies its arguments.  I consider that an anathema in any context bar C interoperability.)

Different strokes for different folks, I guess!  Generally, we only create functions when it is indeed going to be used mainly as the right-hand side of an assignment i.e., to get "formula translation" for:

    y = f(x)

IanH wrote:

..

(In the original example, the function modifies its arguments.  I consider that an anathema in any context bar C interoperability.)

Agree, so we try to apply PURE attribute to all functions and INTENT(IN) to all its arguments, so we can get slapped when we tried to modify the function arguments. 

amalia.barrios@navy.mil wrote:

..

Looks like there's no easy way to do this without requiring an explicit interface, but a work around may be the derived type sample, correct?

Amalia

Amalia,

Not sure what all requirements you have, but from what you've indicated thus far, as suggested by IanH, functions returning a derived type with a zero-based array component or a SUBROUTINE procedure with an INTENT(OUT) allocatable array dummy argument appear to be your best options. 

0 Kudos
Reply