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

Expression result ignored for procedure argument

jwmwalrus
New Contributor I
751 Views

Hi.

The following code

module mod1
    implicit none

    abstract interface
        subroutine i_proc()
        end subroutine
    end interface

contains
    function gen_proc(str) result(p)
        procedure(i_proc), pointer :: p
        character(*), intent(in) :: str

        p => cp
    contains
        subroutine cp()
            print*,'str=',trim(str)
        end subroutine
    end function

    subroutine match(p)
        procedure(i_proc) :: p
        call p()
    end subroutine
end module mod1

use mod1

implicit none

call match(gen_proc('Hello'))

end

Fails to compile with

$ ifx -V return-proc.f90 
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 2024.1.0 Build 20240308
Copyright (C) 1985-2024 Intel Corporation. All rights reserved.

 Intel(R) Fortran 24.0-1472.3
return-proc.f90(31): error #6636: When a dummy argument is a subroutine, the corresponding actual argument must also be a subroutine.
call match(gen_proc('Hello'))
-----------^
compilation aborted for return-proc.f90 (code 1)

I'm not sure if my interpretation is correct, but it seems like the expression's result is not being considered, and the compiler is assuming that I'm trying to pass a function as actual argument.

When compiling the code with gfortran 13, I get the expected outcome:

$ gfortran -std=f2018 return-proc.f90 && ./a.out 
/usr/bin/ld: warning: /tmp/cc2N2xku.o: requires executable stack (because the .note.GNU-stack section is executable)
 str=Hello

(The warning issued by the linker is just a side-effect of the way GCC implements nested functions)

 

0 Kudos
1 Solution
Steve_Lionel
Honored Contributor III
626 Views

The copy of nagfor I have (7.2-7203) doesn't display an ICE, but it doesn't generate an object much less an exe.  However, I agree with Andrew that this is not legal code. The "thunk" created by the pointer assignment p=>cp becomes undefined on return from gen_proc.  Whether it "works" or not is unpredictable.

View solution in original post

0 Kudos
11 Replies
Ron_Green
Moderator
709 Views

nagfor gets an internal compiler error

nagfor -v return-proc.f90 
NAG Fortran Compiler Release 7.1(Hanzomon) Build 7125
return-proc.f90:
Segmentation violation
Internal error - please report this bug

I'll try our ifx mainline branch. 

0 Kudos
Ron_Green
Moderator
709 Views

the latest ifx nightly build also gives the error you describe.  I will have to check the syntax to see if this code is conformant.  It is suspicious when something crashes the NAG compiler. 

0 Kudos
jwmwalrus
New Contributor I
686 Views

The code is simply implementing a closure.

From what I understood  in this old post from Steve Lionel, the context from gen_proc execution should be kept around (which is probably what GCC refers to when performing the stack jump).

In Steve Lionel's example, the contained procedure is passed as an argument to another procedure directly, but imho same (context preservation) rules should apply here... unless procedure pointers to contained procedures are not allowed at all as function results in Fortran.

0 Kudos
Andrew_Smith
Valued Contributor I
708 Views

Even if it did compile and run. I don't think it should work and is probably not legal code. I used a temporary pointer to hold the result of gen_proc, than passed that to match. This compiles and runs but crashes.

After the execution returns from gen_proc, its argument str is removed from the stack and is gone. Then when you call the pointer to it's internal procedure cp, it attempts to access str and protection violates.

0 Kudos
Andrew_Smith
Valued Contributor I
697 Views

This page from IBM Fortran says its not legal to call an internal procedure from outside. Procedure pointers (Fortran 2003) - IBM Documentation

 

0 Kudos
jwmwalrus
New Contributor I
680 Views

Oh. Interesting.

Thanks for the pointer.

0 Kudos
FortranFan
Honored Contributor II
657 Views

@Andrew_Smith wrote:

This page from IBM Fortran says its not legal to call an internal procedure from outside. Procedure pointers (Fortran 2003) - IBM Documentation


Starting Fortran 2008 revision (current standard is 2023) around 14 years ago, this restriction around internal procedures has been lifted in a way.

Meaning, starting with 2008 standard publication, "An internal procedure can be used as an actual argument
or procedure pointer target."  This effectively provides a mechanism for the internal procedure to be invoked in a scope that is external to the host.

The standard can be referred to for further details; the book "Modern Fortran Explained" provides some commentary about this.

 

 

0 Kudos
Steve_Lionel
Honored Contributor III
627 Views

The copy of nagfor I have (7.2-7203) doesn't display an ICE, but it doesn't generate an object much less an exe.  However, I agree with Andrew that this is not legal code. The "thunk" created by the pointer assignment p=>cp becomes undefined on return from gen_proc.  Whether it "works" or not is unpredictable.

0 Kudos
jwmwalrus
New Contributor I
613 Views

@Steve_Lionel wrote:

The copy of nagfor I have (7.2-7203) doesn't display an ICE, but it doesn't generate an object much less an exe.  However, I agree with Andrew that this is not legal code. The "thunk" created by the pointer assignment p=>cp becomes undefined on return from gen_proc.  Whether it "works" or not is unpredictable.


In (a draft of) the Fortran 2023 standard, around the function-stmt definition, I see the following

If the function result is a pointer, on return the pointer association status of the function result shall not be undefined.

And there's also the following note:

The function result is similar to any other entity (variable or procedure pointer) local to a function subprogram. Its existence begins when execution of the function is initiated and ends when execution of the function is terminated. However, because the final value of this entity is used subsequently in the evaluation of the expression that invoked the function, an implementation might defer releasing the storage occupied by that entity until after its value has been used in expression evaluation.

Which seems to be exactly what gfortran is doing (i.e., using the result in the immediate expression).

But if you say the code is not legal, then I believe you.

0 Kudos
FortranFan
Honored Contributor II
529 Views

@jwmwalrus ,

For whatever it's worth, my understanding is your code in the original post does not conform per the standard (c.f. proxy document 23-007r1.pdf).  But it is not so per the stated reasons in this thread, whether with a thunk (no such thing is recognized by the standard) or an internal subprogram or any such argument.  Rather the non-conformance is simply due to a reference to any undefined object ('str`) on line 17 on the code in your original post.

Consider a slight variant of your code in the original post where the object reference is replaced with something valid, two options - a) a module entity thus with default SAVE attribute or a "static" (per common CS parlance) object `SAVE` in the host.  I reckon such code conforms starting with Fortran 2008 standard revision.  And any processor (compiler) unable to complete the instructions with such a program shall be non-conforming with Fortran 2008 and subsequent revisions (2018 and 2023(:

module m
   abstract interface
      subroutine Iproc()
      end subroutine
   end interface
   character(len=:), allocatable, save :: s !<-- option a ; comment out for option b
contains
   function gen_proc(str) result(p)
      procedure(Iproc), pointer :: p
      character(*), intent(in) :: str
      !character(len=:), allocatable, save :: s !<-- uncomment this line for option b
      s = str
      p => cp
   contains
      subroutine cp()
         print*,'str=',trim(s)
      end subroutine
   end function
   subroutine match( p )
      procedure(Iproc) :: p
      call p()
   end subroutine
end module
   use m
   call match( gen_proc('Hello') )
end

Intel Support team may want to submit above case with Intel Fortran team as a bug incident for resolution.

In the meantime, a further variant of above works with Intel Fortran processor and that's good news.

module m
   abstract interface
      subroutine Iproc()
      end subroutine
   end interface
   character(len=:), allocatable, save :: s !<-- option a ; comment out for option b
contains
   function gen_proc(str) result(p)
      procedure(Iproc), pointer :: p
      character(*), intent(in) :: str
      !character(len=:), allocatable, save :: s !<-- uncomment this line for option b
      s = str
      p => cp
   contains
      subroutine cp()
         print*,'str=',trim(s)
      end subroutine
   end function
   subroutine match( p )
      procedure(Iproc) :: p
      call p()
   end subroutine
end module
   use m
   procedure(Iproc), pointer :: proc
   proc => gen_proc('Hello')
   call match( proc )
end
C:\temp>ifx /standard-semantics /free p.f
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 2024.1.0 Build 20240308
Copyright (C) 1985-2024 Intel Corporation. All rights reserved.

Microsoft (R) Incremental Linker Version 14.36.32537.0
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:p.exe
-subsystem:console
p.obj

C:\temp>p.exe
 str=Hello
0 Kudos
Steve_Lionel
Honored Contributor III
527 Views

"An internal procedure cannot be invoked using a procedure pointer from either Fortran or C after the host
instance completes execution, because the pointer is then undefined. While the host instance is active, however,
if an internal procedure was passed as an actual argument or is the target of a procedure pointer, it could be
invoked from outside of the host subprogram." (F2023 15.5.1)

Malcolm Cohen (NAG) agrees the code is invalid and is fixing nagfor to give a run-time error in this case.

The text you cite refers to the function return value (procedure pointer in this case) itself, not the pointer's target. For example, a function returning an allocatable array keeps the result around until consumed.

Reply