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

Behavior of function-returned array pointers of dynamic size

gtokic
Beginner
5,007 Views

I'm trying to use array pointers as an output from a function. The pointer target is a function variable whose size is not fixed. I've noticed that when using these returned pointers in other parts of the code, the values the pointer refers to are somewhat corrupted.

I've provided in the code below an example of what I'm talking about. The three different test functions differ based on how the return array size is assigned. For the code below, these are

  1. determined from a passed c_ptr (my actual use for the code requires me to use c_ptr, not a regular Fortran pointer or array)
  2. determined by a global variable
  3. explicitly defined in the function itself
module math
  implicit none
  private
  public    :: rvfunc_rvarg

  abstract interface
    function rvfunc_rvarg(x,params)  result(res)
      use, intrinsic :: iso_c_binding
      implicit none
      real,intent(in)      :: x(:)
      type(c_ptr),value    :: params
      real,pointer         :: res(:)
    end function rvfunc_rvarg
  end interface
end module math

program test_func_pointers
  use,intrinsic   :: iso_c_binding,  only : c_loc,c_ptr,c_f_pointer,c_double,c_null_ptr
  implicit none
  integer         :: nf
  type(c_ptr)               :: params
  real,target               :: p
  real                      :: x(6)

  x = 5.0
  nf = 10
  p = real(nf)
  params = c_loc(p)

  print *,'Test func 1'
  call test_sub(test_func1,params,x)

  print *,'Test func 2'
  call test_sub(test_func2,c_null_ptr,x)

  print *,'Test func 3'
  call test_sub(test_func3,c_null_ptr,x)

 contains
!------------------------------------------------------------------------------
!------------------------------------------------------------------------------
  subroutine test_sub(func,params,x)
    use math,   only : rvfunc_rvarg
    implicit none
    procedure(rvfunc_rvarg)   :: func
    type(c_ptr),value         :: params
    real,intent(in)           :: x(:)
    real,pointer              :: ff(:)

    ff => func(x,params)
    print *, 'ff = ',ff

  end subroutine test_sub

!------------------------------------------------------------------------------

  function test_func1(x,params) result(RA)
    implicit none
    real,intent(in)                 :: x(:)
    type(c_ptr),value               :: params
    real,pointer                    :: RA(:) 
    real,target,allocatable         :: RAt(:)
    integer                         :: n
    real(c_double),pointer          :: p

    call c_f_pointer(params,p)
    n = int(p) 
    print *,' n = ',n
    allocate(RAt(n))
!    allocate(RA(n))
    RAt = x(2)
    RA => RAt

  end function test_func1
!------------------------------------------------------------------------------
  function test_func2(x,params) result(RA)
    implicit none
    real,intent(in)                 :: x(:)
    type(c_ptr),value               :: params
    real,pointer                    :: RA(:)
    real,target                     :: RAt(nf)

    RAt = x(2)
!    allocate(RA(nf))
    RA => RAt

  end function test_func2
!------------------------------------------------------------------------------
  function test_func3(x,params) result(RA)
    implicit none
    real,intent(in)                 :: x(:)
    type(c_ptr),value               :: params
    real,pointer                    :: RA(:)
    real,target                     :: RAt(10)
    
    RAt = x(2)
    RA => RAt

  end function test_func3

end program test_func_pointers

The result I get from this is the following:

 Test func 1
  n =           10
 ff =   0.000000000000000E+000   5.00000000000000        5.00000000000000
   5.00000000000000        5.00000000000000        5.00000000000000
   5.00000000000000        5.00000000000000        5.00000000000000
   5.00000000000000
 Test func 2
 ff =    5.00000000000000       6.953242939682073E-310  2.343437349384825E-317
  6.953242939696697E-310  3.448878599884572E-317  6.953242939648871E-310
  6.953242939654405E-310  6.953242939653614E-310  4.940656458412465E-323
  3.952525166729972E-323
 Test func 3
 ff =    5.00000000000000        5.00000000000000        5.00000000000000
   5.00000000000000        5.00000000000000        5.00000000000000
   5.00000000000000        5.00000000000000        5.00000000000000
   5.00000000000000

As you can see, for the first two cases the values ff points to are wrong, especially in the second case. If I uncomment associate(RA(n)) in test_func1&2, the result doesn't change much -- a few more values in test_func2 are correct, but still ff(1) = 0.0 for the test_func1 case. What is the proper way to dynamically assign the size of RA(:)?

I'm using ifort version 15.0.2. 

I would appreciate any help or clarification on this matter.

 

Grgur

0 Kudos
1 Solution
JVanB
Valued Contributor II
5,007 Views

Actually, that is the method that is supposed to work according to the standard. You just need that switch with ifort to make it conform to the standard in this respect. Contexts where [re]allocation are required on default assignment were nonconforming to the standard before f2003, where [re]allocation on assignment was introduced. There are those of us who would prefer that -assume realloc_lhs were the default for ifort rather than the current situation where -assume norealloc_lhs is, but looking at the docs for ifort 16, that change still hasn't been made. See how the default that breaks standard-conforming code caused problems for you?

 

View solution in original post

0 Kudos
12 Replies
mecej4
Honored Contributor III
5,007 Views

We need a description of the effect of

     use math,   only : rvfunc_rvarg

 

0 Kudos
gtokic
Beginner
5,007 Views

Ah, sorry, here it is

module math
  implicit none

  private
  public    :: rvfunc_rvarg

  abstract interface
    function rvfunc_rvarg(x,params)  result(res)
      use, intrinsic :: iso_c_binding
      implicit none
      real,intent(in)      :: x(:)
      type(c_ptr),value    :: params
      real,pointer         :: res(:)
    end function rvfunc_rvarg
  end interface
end module math

 

0 Kudos
gtokic
Beginner
5,007 Views

I've just noticed a typo when copying my original code. There should be "allocate(RAt(n))" inserted in test_func1 in my original code posting. I've updated the code to reflect that. (This doesn't change the results, it was a copying error)

0 Kudos
IanH
Honored Contributor III
5,007 Views

In all cases you are returning pointers to unsaved local variables (all called RAt).  As soon as the procedure completes, any pointers associated with those variables become undefined - so called "dangling references".  Your code is non-conforming.

If you are going to return a pointer (why?) you need to ensure that the object that the pointer references lasts at least until you have been able to capture the value of the pointer in the scope invoking the function.

The method of defining the size of the local array varies where the data for the local array is stored, hence the differences in behaviour.  But all three cases have the dangling reference programming error.

gtokic
Beginner
5,007 Views

I would prefer to return an allocatable array, but I was under the impression that that is not supported. Maybe it was the case in an old compiler I was using previously. 

If I add the following function under the "contains" block

  function test_func4(x,params) result(RA)
    implicit none
    real,intent(in)                 :: x(:)
    type(c_ptr),value               :: params
    real,allocatable                :: RA(:)

    allocate(RA(nf))
    RA = x(2)

  end function test_func4  

then the printing the values directly works, but assigning them to an unallocated allocatable array doesn't. I could allocate it based on a dummy run of the function, but this is prohibitive if the function evaluation is expensive. 

! This is just a snippet to be inserted in the main program above
  real,allocatable          :: result1(:)

!
! This works
  print *,'test_func4(x,c_null_ptr) = ',test_func4(x,c_null_ptr)
!
! This doesn't work  
  result1 = test_func4(x,c_null_ptr)
  print *,'result1 = ',result1
!
! This works, but prohibitive  
  allocate(result1(size(test_func4(x,c_null_ptr))))
  result1 = test_func4(x,c_null_ptr)
  print *,'result1 (after allocate) = ',result1

In this simple example, I could have done allocate(result1(nf)), but in actual use the calling routine is not aware of the return size of the array. What is the proper way of allocating/assigning the array in that case?

Thanks,

Grgur

0 Kudos
JVanB
Valued Contributor II
5,007 Views

Does the syntax that doesn't work in Quote #6 above still fail if compiled with -assume realloc_lhs ?

 

0 Kudos
gtokic
Beginner
5,007 Views

No, that works! (I just have to remove the last example because that would be allocating an allocated array.) Thanks for the tip!

Is that the preferred way of about this problem? Is there a solution that is not compiler-dependent?

 

P.S. I should probably clarify that in Quote #6 by "doesn't work" I was referring to the fact that it doesn't print anything for result1, not that there's a run-time error

0 Kudos
JVanB
Valued Contributor II
5,008 Views

Actually, that is the method that is supposed to work according to the standard. You just need that switch with ifort to make it conform to the standard in this respect. Contexts where [re]allocation are required on default assignment were nonconforming to the standard before f2003, where [re]allocation on assignment was introduced. There are those of us who would prefer that -assume realloc_lhs were the default for ifort rather than the current situation where -assume norealloc_lhs is, but looking at the docs for ifort 16, that change still hasn't been made. See how the default that breaks standard-conforming code caused problems for you?

 

0 Kudos
FortranFan
Honored Contributor III
5,007 Views

Repeat Offender wrote:

Actually, that is the method that is supposed to work according to the standard. You just need that switch with ifort to make it conform to the standard in this respect. Contexts where [re]allocation are required on default assignment were nonconforming to the standard before f2003, where [re]allocation on assignment was introduced. There are those of us who would prefer that -assume realloc_lhs were the default for ifort rather than the current situation where -assume norealloc_lhs is, but looking at the docs for ifort 16, that change still hasn't been made. See how the default that breaks standard-conforming code caused problems for you?

Look into -standard-semantics: https://software.intel.com/en-us/node/579529.  You might find it useful in gaining a host of capabilities consistent with the Fortran standard rather than having to pick individual aspects with -assume options.

 

 

0 Kudos
mecej4
Honored Contributor III
5,007 Views

R.O.'s point was, I think, that /standard-semantics should be the default. However, the warning note "May result in performance loss" that is attached to that option is probably why it is not a default option.

0 Kudos
FortranFan
Honored Contributor III
5,007 Views

mecej4 wrote:

R.O.'s point was, I think, that /standard-semantics should be the default. However, the warning note "May result in performance loss" that is attached to that option is probably why it is not a default option.

And my only point was regarding the specific compiler option recommendation by R.O. of -assume realloc-lhs in connection with his comments about standard Fortran features, that if one is considering a broader array of standard features from 2003 and 2008, the compiler option of -standard-semantics is worth knowing about.

Re: the default, I too had brought it up with Intel quite a while ago, but they have reasons to not do so - see message #9 here:

https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/520352

 

0 Kudos
gtokic
Beginner
5,007 Views

Hi everybody, 

Thanks so much for very helpful comments! Both tips about -assume realloc_lhs and -standard-semantics were very helpful to me. I'm changing my actual code to work with functions returning allocatable arrays instead of pointers, but I expect it to work without problems.

0 Kudos
Reply