I maintain a Fortran library with a C interface, so that it can be called from Python, etc.
The library compiles with gfortran, but not with flang.
The code below is a minimal example of the approach I use to build the C-API. The tricky part is the handling of the callbacks.
gfortran apparently considers that the C-callback interface matches the Fortran-callback and does the “right” thing. flang, on the other hand, is strict and deems the interfaces incompatible because the Fortran interface does not have the bind(c) attribute.
I can “solve” the problem by adding bind(c) to func_t. However, then flang forces me to add bind(c) to the pure fortran callbacks, which is not very fortranic.
How can I address this issue in a proper manner without constraining the Fortran usage to the specific requirements of the C-API?
module lib
use iso_c_binding, only: c_int, c_float, c_funptr, c_f_procpointer, c_funloc
implicit none
abstract interface
subroutine func_t(n, x, res)
! Interface for Fortran callback
integer, intent(in) :: n
real, intent(in) :: x(n)
real, intent(out) :: res(n)
end subroutine func_t
subroutine func_c_t(n, x, res) bind(c)
! Interface for C callback
! Identical to func_t but with C types and bind(c)
import :: c_int, c_float
integer(c_int), intent(in) :: n
real(c_float), intent(in) :: x(n)
real(c_float), intent(out) :: res(n)
end subroutine func_c_t
end interface
contains
subroutine norm_func(f, n, x, fnorm)
! Main Fortran algorithm using Fortran callback
! Here just a trivial example computing the 2-norm of f(x)
procedure(func_t) :: f
integer, intent(in) :: n
real, intent(in) :: x(n)
real, intent(out) :: fnorm
real :: res(n)
call f(n, x, res)
fnorm = norm2(res)
end subroutine norm_func
subroutine norm_func_c(f, n, x, fnorm) bind(c)
! C-wapper to main algorithm using a C callback
procedure(func_c_t) :: f
integer(c_int), intent(in) :: n
real(c_float), intent(in) :: x(n)
real(c_float), intent(out) :: fnorm
! gfortran works fine with or without this explicit conversion
! flang complains regardless
type(c_funptr) :: fp
procedure(func_t), pointer :: fptr
fp = c_funloc(f)
call c_f_procpointer(fp, fptr)
call norm_func(f, n, x, fnorm)
end subroutine norm_func_c
end module lib
program test
use lib
implicit none
integer, parameter :: n = 3
real :: x(n), fnorm
integer :: i
x = 3.0
call norm_func(myfunc, n, x, fnorm)
print *, fnorm
contains
subroutine myfunc(n, x, res)
integer, intent(in) :: n
real, intent(in) :: x(n)
real, intent(out) :: res(n)
integer :: i
res = x ** 2
end subroutine myfunc
end program test