Windows + GCC: passing internal procedure as actual argument

As of Fortran 2008, it is legal to pass an internal procedure as an actual argument. This is handy because the internal procedure can reference data in the body of the procedure or program in which it is contained. The following is a simple example showing how this mechanism can be used to pass “extra arguments” to a quadrature routine.

module integration
  implicit none

  abstract interface
    function integrand(x)
      real, intent(in) :: x
      real :: integrand
    end function integrand
  end interface

contains

  function trapz(f, a, b)
    procedure(integrand) :: f
    real, intent(in) :: a, b
    real :: trapz
    trapz = (b - a)/2 * ( f(a) + f(b) )
  end function trapz
  
  subroutine doit
    integer :: n
    do n = 0, 5
      print *, n, trapz(f, 0., 1.)
    end do
  contains
    function f(x) result(y)
      real, intent(in) :: x
      real :: y
      y = x**n
    end function f
  end subroutine doit
  
end module integration


program check
  use integration, only: doit
  implicit none
  call doit
end program check

The GCC wiki states that this has been implemented in gfortran since 2010 (Fortran2008Status - GCC Wiki). However, when I build and run the above, the program terminates unsuccessfully.

C:\Users\nsha\Projects\tfaa>fpm test --profile debug
 + gfortran -c test\check.f90  -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\tfaa\test_check.f90.o
 + gfortran  -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single  build\gfortran_2A42023B310FA28D\tfaa\test_check.f90.o build\gfortran_2A42023B310FA28D\tfaa\libtfaa.a -o build\gfortran_2A42023B310FA28D\test\check.exe
<ERROR> Execution failed for object " check.exe "
<ERROR>*cmd_run*:stopping due to failed executions
STOP 1

I am using gfortran 10.3.0 on Windows. Trusting the GCC wiki, I am led to think this is some Windows-specific issue. Can others Windows users check if the above code executes correctly? Can non-Windows users verify that it does indeed work on other platforms?

yes, in Linux OS the above program works:

Linux environment:

$ cat /etc/os-release
NAME="Ubuntu"
VERSION="20.04.3 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.3 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"

output:

$ gfortran -o internal_procedure_as_argument.x internal_procedure_as_argument.f90 && ./internal_procedure_as_argument.x

           0   1.00000000    
           1  0.500000000    
           2  0.500000000    
           3  0.500000000    
           4  0.500000000    
           5  0.500000000 


1 Like

I tried your code on Windows with gfortran12.2.0 and fpm 0.8.1 and everything works. I put your program check within a check.f90 file in the test folder, and the module within its own file in the src folder.

1 Like

I code in Windows with gcc and never had problems without explicit or abstract interfaces.

I can’t run your test now, but if you look for me in guthub, you will find "HR-WENO’ and "pbepack’ which make extensive use of that feature. Both compile perfectly with Windows + gcc.

I’m tempted to say the problem might be related with fpm, not the compiler (tried with gfortran 4.9 and it also works)… @nshaffer could you share your .toml file? which fpm version are you using?

It works for me on Windows 10 using a gfortran binary from equation.com:

>gfortran --version
GNU Fortran (GCC) 13.0.0 20221218 (experimental)

>gfortran xinternal.f90

c:\somedir
>a
           0   1.00000000    
           1  0.500000000    
           2  0.500000000    
           3  0.500000000    
           4  0.500000000    
           5  0.500000000
1 Like

Thanks all for the wide variety of tests; they are really helpful sanity checks. On both machines where I have this issue (both employer-managed), I am using an old version of @lkedward’s Windows quick-start, where gfortran comes from MinGW. I guess it is possible that the build is not configured quite right, or perhaps a Windows update broke things and I need to reinstall. Either way, this is looking like a problem with my specific combination of OS settings and/or GCC configuration.

@nshaffer , if it’s possible for you on this employer-managed devices to install Intel oneAPI HPC toolkit for Fortran, you can try out IFORT / IFX and the program response with it might help you figure out the issue.

As you will know, developers on Windows OS are prone to use DLLs (shared libraries) to encapsulate codes such as what you show in the Fortran MODULE you named as integration. And one way to manage control what can be accessed from such as user library (DLL) is via what Microsoft calls module-definition (DEF) files.

So you will notice in the console output below, with your MODULE code in a file named i.f and your caller program check in a file named p.f, Intel’s newer LLVM based processor IFX works as you would expect with the code you provide also when the MODULE code is contained in a shared lbrary:

C:\temp>type i.def
LIBRARY i
EXPORTS
   INTEGRATION_mp_DOIT         @1

C:\temp>ifx /c /standard-semantics /free i.f
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 2023.1.0 Build 20230320
Copyright (C) 1985-2023 Intel Corporation. All rights reserved.


C:\temp>link i.obj /dll /def:i.def /out:i.dll
Microsoft (R) Incremental Linker Version 14.34.31937.0
Copyright (C) Microsoft Corporation.  All rights reserved.

   Creating library i.lib and object i.exp

C:\temp>ifx /c /standard-semantics /free p.f
Intel(R) Fortran Compiler for applications running on Intel(R) 64, Version 2023.1.0 Build 20230320
Copyright (C) 1985-2023 Intel Corporation. All rights reserved.


C:\temp>link p.obj i.lib /subsystem:console /out:p.exe
Microsoft (R) Incremental Linker Version 14.34.31937.0
Copyright (C) Microsoft Corporation.  All rights reserved.


C:\temp>p.exe
 0 1.000000
 1 0.5000000
 2 0.5000000
 3 0.5000000
 4 0.5000000
 5 0.5000000

One more data point: On Windows 11, using Cygwin Gfortran 11.3.1, building with

gfortran trapz.f90  -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single

produces an executable that runs normally and produces the expected output.

I am wondering what the error message (“Execution failed”) actually means (i.e., is this a build failure, or some runtime error?) It might be useful if more info can be obtained with some option (like -verbose?)

 + gfortran -c test\check.f90  -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -J build\gfortran_2A42023B310FA28D -Ibuild\gfortran_2A42023B310FA28D -o build\gfortran_2A42023B310FA28D\tfaa\test_check.f90.o
 + gfortran  -Wall -Wextra -Wimplicit-interface -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single  build\gfortran_2A42023B310FA28D\tfaa\test_check.f90.o build\gfortran_2A42023B310FA28D\tfaa\libtfaa.a -o build\gfortran_2A42023B310FA28D\test\check.exe
<ERROR> Execution failed for object " check.exe "

Also, the version of libgfortran installed might have some issue…(eg with “-fPIC”)?
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35014

I just tested with the latest version of the quick-start Fortran environment (gcc/gfortran v12.2.0 & fpm 0.8.2) and I’m getting the same output as everyone else:

fpm test --verbose
 + gfortran -c test\main.f90   -Wall -Wextra -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -fimplicit-none -Werror=implicit-interface -ffree-form -J build\gfortran_87E2AE0597D39913 -Ibuild\gfortran_87E2AE0597D39913 -o build\gfortran_87E2AE0597D39913\test1\test_main.f90.o
 + mkdir build\gfortran_E167FD2A985B468F\test\
 + gfortran    -Wall -Wextra -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -fimplicit-none -Werror=implicit-interface build\gfortran_87E2AE0597D39913\test1\test_main.f90.o  -o build\gfortran_E167FD2A985B468F\test\test1-test.exe
 + build\gfortran_E167FD2A985B468F\test\test1-test.exe
           0   1.00000000
           1  0.500000000
           2  0.500000000
           3  0.500000000
           4  0.500000000
           5  0.500000000