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

has handling of functions as arguments changed?

martymike
Novice
1,442 Views

We are moving from IVF 15 to IVF 19.

It appears that there may have been a change in how this is handled:

integer function myfunc(a)

integer a

call getvalue(a,myfunc)

end

This is a very simple example, not the real code. In this example getvalue is a subroutine taking two integer arguments, and it treats the first as input and the second as output (but does not declare them so). There is no explicit interface for getvalue.

In the past this works. We are seeing crashes now (write access violation).

I guessed that myfunc was being passed as a function pointer, and tried three changes: (1) declared an interface for getvalue specifying two integer arguments (2) declared a result variable for myfunc (3) declared a local integer, passed that to getvalue instead of myfunc, and assigned it to myfunc on return. All three changes, independently, seem to fix the problem.

I never thought about it before, but I can see now that this usage could be considered ambiguous (what if you *wanted* to pass the function?) but it still seems to represent a change.

I read through the release notes for IVF 16, 17, 18, and 19 but I did not notice this mentioned (not saying that I could not have missed it).

It is a change? Why? When was it introduced? Can you point me to any documentation about it?

Thank you.

-Marty

0 Kudos
19 Replies
andrew_4619
Honored Contributor II
1,442 Views

Did you mean to write "call getvalue( a, myfunc(a) )" which is quite different to  "call getvalue(a,myfunc)" 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,442 Views

You will have to declare an interface for getvalue that takes as arguments an integer and a function returning an integer, taking an integer argument.

module YourModule
interface
  subroutine getvalue(anInt, anIntFunction)
    integer :: anInt
    interface
      function anIntFunction(anIntToo)
        integer :: anIntFunction, anIntToo
      end function anIntFunction
    end interface
  end subroutine getvalue
end interface

contains
  function myFuncA(a)
    integer  :: myFuncA, a
    ...
  end function myFuncB
  function myFuncB(a)
    integer  :: myFuncB, a
    ...
  end function myFuncB
end module YourModule
...
subroutine YourCode
   use YourModule
   ...
   call getvalue(someInt, myFuncA)
...
   call getvalue(someOtherInt, myFuncB)

Jim Dempsey

0 Kudos
martymike
Novice
1,442 Views

To Andrew_4619: No.

To jimdempseyatthecove: When I said "what if you *wanted* to pass the function?" I wasn't really looking for an answer - that was rhetorical. Yes, I could write an interface and solve my problem. I said that that was one of the things that I tried that solved the problem. I do *not* want to pass a function pointer (but I know how).

Thank you both for taking trying to help.

Perhaps I did not make this clear - this function (the one above) is not failing. In getvalue(), when it tries to assign a value to the second argument, that is when the write access violation happens (that function is called from many other places and does not fail).

The issue is that this code has been working correctly in IVF 15, and fails only when we build it with the new compiler. Not this silly little function I wrote above. The real function takes a character argument, parses it, and does different things depending on what it finds. One of those things is to call a function like getvalue in my example (with the first argument being an integer that was parsed from the input character string).

I can make the code work, but I am guessing at the root cause of the problem. I want to understand what has changed so that I implement the best solution, and know how to look for similar issues that might be in other places in the code.

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,442 Views

What you may need to do as a quick fix (not saying his is the right thing to do) is to identify the function variable with EXTERNAL as opposed to leaving it undefined.

Some really old compilers used to assume

        SomeUndefinedVariable(someArgs)

was an external function call. In the above case a REAL(4) function taking the (implicit/explicit) arguments given.

This was non-standard behavior. 

Also note, by doing the above, you relinquish the ability of the compiler to perform argument checking. This may leave your successor support person with the headache of trying to figure out why your "working" code is now failing. Your call as to if you want to fix this now, or let someone else fix it later.

Jim Dempsey

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,442 Views

Also, you may want to experiment using the option f66 to enable old style FORTRAN-IV and FORTRAN-66 syntax.

Your unchanged code might compile as you intended. By unchanged code, I mean the original code (pre-modernization).

Jim Dempsey

0 Kudos
martymike
Novice
1,442 Views

What function? getvalue? Is already is an external function. If I add an external decl then I could pass it as an argument, but I don't want to. The compiler clearly understands it is a function as it gets called.

There's no context to declare myfunc external .Even if I could, that would be the opposite of what I want. It appears that the compiler is treating it as external already. *That's* the problem.

This is a very old application that I am working in. I'm been actively introducing newer ideas where I can (like interfaces) but there aren't very many outside of the few functions in modules.

As for "Some really old compilers used to assume

        SomeUndefinedVariable(someArgs)

was an external function call. In the above case a REAL(4) function taking the (implicit/explicit) arguments given." That's what this compiler does, and it's not old (well, without an IMPLICIT NONE; with it you get a compile-time error that SomeUndefinedVariable is not declared).

I don't wish to argue with you. I appreciate that you are trying to help (and I have seen how you are a very frequent poster who has contributed a lot a good ideas to a lot of people over the years). I feel like we are not communicating.

0 Kudos
andrew_4619
Honored Contributor II
1,442 Views

andrew_4619 wrote:
Did you mean to write "call getvalue( a, myfunc(a) )" which is quite different to  "call getvalue(a,myfunc)" 

"No" - Well then you are passing the function address and IMO if getvalue cannot see an interface you have non-conforming code so have little right to expect consistent interpretation from different compilers. If you had chosen to have the appropriate checks switched on then you would have got errors.

 

0 Kudos
mecej4
Honored Contributor III
1,442 Views

My understanding of Martymike's intention is that the local variable myfunc (which is the default result variable for the function) is to be defined in subroutine getvalue() rather than in the body of the function myfunc().

I looked at the assembler code generated by the 32-bit 14.0.6 and 19.0.3 compilers, and I see no material differences in the machine instruction sequences. In both versions, the return value myfunc is kept on the stack at (EBP-04) and that address is passed to getvalue().

        lea       eax, DWORD PTR [-4+ebp]                       ;5.6 return value myfunc
        push      eax                                           ;5.6
        push      DWORD PTR [8+ebp]                             ;5.6  arg1, "a"
        call      _GETVALUE                                     ;5.6

The address being passed is that of a data item, not a code item; this is, as far as I understand, the same as what Martymike intends to do.

I think that we need to see the code of getvalue(). Specifically, how is the second argument to that subroutine declared, used and defined in that subroutine? Either the full code or a simplified version that captures the essential details will do, and I expect to see that the code is such that no explicit interface to the subroutine is necessary. We then need to see the assembly code generated for that subroutine. If there are differences between the outputs of different compiler versions, those differences may also depend on which compiler options were used, so we need to be told which options were used when the crashing code was generated.

One thing that I am curious about: are the bodies of myfunc and getvalue both in the same Fortran source file?

0 Kudos
andrew_4619
Honored Contributor II
1,442 Views

OK thanks mecej4 (#9) but myfunc takes an argument which is missing in the getvalue call, is it that myfunc (whose definition we have not seen) has an OPTIONAL arg (in which case I think getvalue would need sight of an interface) or is there some Fortran language rule nuance at play that has escaped me all these years? Either way some clarification would be most appreciated as this is now bugging me!

0 Kudos
mecej4
Honored Contributor III
1,442 Views

Andrew, I confess that my own notions regarding reuse of the function name were vague yesterday (esp. the question: is the function name a code variable or a data variable?). However, looking at the assembly code generated by Ifort and Gfortran indicated to me that the compilers have no such doubts -- the function name, without an argument list, is just another local data variable. Furthermore, unless the function declaration contains the RECURSIVE attribute, there can be no instances of <function_name>(<argument_list>) within the body of the function.

The standard (F2008, 12.6.2.2) says:

13 4 If RESULT appears, the name of the result variable of the function is result-name and all occurrences of the
14 function name in execution-part statements in its scope refer to the function itself. If RESULT does not appear,
15 the name of the result variable is function-name and all occurrences of the function name in execution-part
16 statements in its scope are references to the result variable.

I think that in situations such as the current one, where the function name is used in more than one place in the body of the function, the use of a RESULT(result_name) clause, although optional, can be  helpful in clarifying the behavior of the code. 

Here is a toy program that illustrates the above quotation from  the standard.

program tfactorial
implicit none
integer :: n, n_fac
logical :: up
!
n = 10

up    = .true.
n_fac = factorial(n,up)
print *,'Up: ',n_fac

up    = .false.
n_fac = factorial(n,up)
print *,'Up: ',n_fac

contains
   integer function factorial(n, up)
   implicit none
   integer, intent(in) :: n
   logical, intent(in) :: up
   integer :: i

   if (up) then
      factorial = 1
      do i = 2, n
         factorial = factorial * i
      end do
   else
      factorial = n
      do i = n-1, 2, -1
         factorial = factorial * i
      end do
   endif
   return
   end function factorial

end program tfactorial

 

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,442 Views

>> is it that myfunc (whose definition we have not seen) has an OPTIONAL arg (in which case I think getvalue would need sight of an interface)

Correct. This would require the sources that CALL/use function without the optional argument to have the interface (else the placeholder for the missing argument won't be provided, e.g. as a NULL reference).

Jim Dempsey

0 Kudos
andrew_4619
Honored Contributor II
1,442 Views

mecej4 wrote:
Andrew, I confess that my own notions..............

Many thanks for taking the time. I must confess that your example really confused me as I could not see how it was relevant the the usage case of the OP. I have now re-read the OP and find that I have been totally misreading this I had rather stupidly not realised as he is using the function name from inside the function itself which is clearly an OK usage. The usage case that was causing me a problem is infact a problem only in my own mind having misread the situation! -1 to me :-(

0 Kudos
andrew_4619
Honored Contributor II
1,442 Views

 

I think the OP's example is more like the code below. ... Compiling with Intel(R) Visual Fortran Compiler 18.0.2.185 [IA-32]....0 error(s), 0 warning(s)

This gives the accepted answer of 42

program MOL
    implicit none
    integer :: a
    integer, external :: myfunc

    a = 41
    print *, 'The meaning of life is ', myfunc( a )
    
end program MOL

integer function myfunc( n )
   implicit none
   integer, intent(in) :: n

   call getvalue(n, myfunc)

end function myfunc
    
subroutine getvalue(n, m)
    implicit none
    integer :: n , m
    m = n+1
end subroutine getvalue

Does is work on Version 19?

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,442 Views

Andrew_4619,

While some compiler at some time may have provided myfunc with duality. IOW (globally) named location for return value, and named location for function call (e.g. with suffix or prefix or different address space (Harvard Architecture), line 22 in #14 would otherwise present a quandary with respect to what to do with m= where m is myfunc(a).

While within the function myfunc, the variable name myfunc is, for all intents and purposes, a DUMMY argument. Outside the function myfunc is the address of the function. (barring a case where on Harvard architecture you can have the same address in two different address spaces).

Jim Dempsey

 

0 Kudos
mecej4
Honored Contributor III
1,442 Views

andrew_4619 wrote:

... The usage case that was causing me a problem is in fact a problem only in my own mind having misread the situation...

Rather, it is the OP's choice of thread title that is misleading. He is clearly not interested in passing a function as an argument, yet the title focuses on whether the method of passing a function as an argument has changed. 

Anyhow, I think that all of us see things pretty much the same way now -- it is an elephant, as your own code example shows.

0 Kudos
martymike
Novice
1,442 Views

Andrew_4619 and jimdempseyatthe cove: thank you for continuing to take an interest in this question

mecej4: It had always seemed to me that, to use your words, "the function name, without an argument list, is just another local data variable". Therefore passing it to another function and expecting it to be set there is an OK usage. As I said in the original post, it hadn't occurred to me before to consider how this might be read differently. For clarity, at least for humans, I may choose to code such things differently in the future. But, for now, my issue continues to be simply what result should I expect. We are nearing a release date and, while I can make this code work, if this is an issue with my understanding of the language then there could be other places in the code base to worry about.

Thank you for looking at the generated code (I really should get better at that). It appears that we are arriving at the conclusion that there is nothing incorrect in the source code and the error I was seeing is not consistent with what I should expect.

I had not included the code for the getvalue function because, as I mentioned, I was able to correct the problem by modifying myfunc so I came to the conclusion the getvalue did not matter (although I did describe the interface, in particular that it declared both arguments as integers and set the second).

You asked, "One thing that I am curious about: are the bodies of myfunc and getvalue both in the same Fortran source file?" The answer is "no". In fact, they are in different dll's.

I'm on a tight schedule. I'm going to assume for now that this is a bug for now. I'm going to add an interface to getvalue, since that solves the problem without changing very much. It's also just better. We don't know why that makes a difference, so I will have to put together an actual working example that demonstrates the issue and send that in. That may take some time. The entire application is quite large, and my experience with these sorts of things is that problems sometimes don't appear in smaller examples.

fwiw the actual program that I modeled as "myfunc" is really called getBusSeqn, and I have listed it below. In the case where the crash occurred it was in the call to bssqnc (what I called getvalue in my simple example). I do not know at this time whether the same problem occurs when the code takes the path that calls nasqnc_name. I just have the one reported failure and this code has been running successfully in IVF 15 for a long time. The call to bssqnc actually goes three functions deep and relies on searching arrays in another module, so it would be a bit much to copy all that code here.

Thank you all again for your help.

integer function getBusSeqn(Key)
character(len=*) :: Key
integer ival,icode,ierr
character(len=EncodeBusNameLen) :: cval

external bssqnc, nasqnc_name

call gtinit(Key)
ival=0; getBusSeqn=0

call gtint(ival, icode, *1, *1); 1 continue
if (icode == 4) then
   cval = ExBusNam(Key)
   call nasqnc_name(cval,ival,getBusSeqn)
   if (ival == 0) then           ! Not a bus name
      call validate_bus_long_id(Key,ival,ierr)
   else
      return ! already have seq #
   endif
endif

if (ival /= 0) then
   call bssqnc(ival,getBusSeqn)
endif

end function getBusSeqn

0 Kudos
andrew_4619
Honored Contributor II
1,442 Views

martymike wrote:
I'm going to assume for now that this is a bug for now.....

It would be most helpful if there is indeed a bug if you could make a small complete reproducer along the lines of my last post maybe? I will only take a few minutes if you understand the problem.

0 Kudos
FortranFan
Honored Contributor II
1,442 Views

martymike wrote:

We are moving from IVF 15 to IVF 19.

It appears that there may have been a change in how this is handled:

..

In the past this works. We are seeing crashes now (write access violation).

..

I read through the release notes for IVF 16, 17, 18, and 19 but I did not notice this mentioned (not saying that I could not have missed it).

It is a change? Why? When was it introduced? Can you point me to any documentation about it?

@martymike,

It appears to me you have more than done your due diligence as a customer of Intel Fortran.  Did you file a support request at Intel Online Support Center (OSC)? https://supporttickets.intel.com/servicecenter?lang=en-US   If not, you should.  Using this forum to get pointers appears a perfect use of it, but for definitive answers on the kind of questions you raise above, you should look to Intel OSC.

martymike wrote:

.. the actual program ,, I have listed it below...

Have you tried turning on the compiler warnings (/warn.. ) and run-time checks /check: etc. on your code?  It might be worth a try, at least once to see if you can get any useful guidance from Intel Fortran on the crashes you mention in the original post,  

0 Kudos
jimdempseyatthecove
Honored Contributor III
1,442 Views

The usage of getBusSeqn as a variable argument within the function getBusSeqn is perfectly valid. No different than:

getBusSeqn = expression

The reason for this is getBusSeqn used within getBusSeqn is really the "dummy" argument of the return value and not the entry point to the procedure getBusSeqn (of the same name).

Usage of getBusSeqn as a variable from an external procedure by name (not by way of dummy argument within a procedure provided by a call from getBusSeqn itself) is invalid.

IOW Andrew's example in #14, the empty line 8 could NOT (legally) contain "myfunc = 43".
Whereas line 22 is valid as the dummy argument m represents the return dummy argument of getBusSeqn named as the return of function getBusSeqn.

I strongly suggest that you not use invalid syntax that doesn't crash (or may even have accidentally worked). This may cause problems that are difficult to undo later.

Jim Dempsey

0 Kudos
Reply