Constant expressions for bind(C) name

This is an interesting issue I recently encountered. Consider this example of defining a number Fortran-C intercompatible function.

module m_api
  use, intrinsic :: iso_c_binding
  implicit none
  character(len=*), parameter :: namespace = "m_"
contains
  function info_api() result(stat) bind(C, name=namespace//"info")
    integer(c_int) :: stat
    ! ...
  end function info_api
end module m_api

Because I’m lazy (and want to avoid mistyping the namespace in one of the bind(C) declaration) I define a character constant in the module scope and concatenate it with the actual function name. This works surprisingly well with most compilers I tested (only failed in NVHPC 21.3 so far).

Having learnt about this useful feature I tried to use it for C-Fortran bindings as well, especially to avoid mixing preprocessor into the interface declarations like here:

module m_os
  use, intrinsic :: iso_c_binding
  implicit none

  interface
    function chdir(path) result(stat) &
#ifndef _WIN32
      & bind(C, name="chdir")
#else
      & bind(C, name="_chdir")
#endif
      import :: c_char, c_int
      character(kind=c_char, len=1), intent(in) :: path(*)
      integer(c_int) :: stat
    end function chdir
  end interface
end module m_os

Moving out the symbol name could greatly improve the readability here:

module m_os
  use, intrinsic :: iso_c_binding
  implicit none
#ifndef _WIN32
  character(len=*), parameter :: chdir_symbol = "chdir"
#else
  character(len=*), parameter :: chdir_symbol = "_chdir"
#endif

  interface
    function chdir(path) result(stat) bind(C, name=chdir_symbol)
      import :: c_char, c_int, chdir_symbol
      character(kind=c_char, len=1), intent(in) :: path(*)
      integer(c_int) :: stat
    end function chdir
  end interface
end module m_os

As it turns out, this is not accepted by the compiler:

❯ gfortran -c os.F90
os.F90:11:51:

   11 |     function chdir(path) result(stat) bind(C, name=chdir_symbol)
      |                                                   1
Error: Parameter ‘chdir_symbol’ at (1) has not been declared or is a variable, which does not reduce to a constant expression
os.F90:12:13:

   12 |       import :: c_char, c_int, chdir_symbol
      |             1
Error: IMPORT statement at (1) only permitted in an INTERFACE body
os.F90:13:21:

   13 |       character(kind=c_char, len=1), intent(in) :: path(*)
      |                     1
Error: Parameter ‘c_char’ at (1) has not been declared or is a variable, which does not reduce to a constant expression
os.F90:14:14:

   14 |       integer(c_int) :: stat
      |              1
Error: Parameter ‘c_int’ at (1) has not been declared or is a variable, which does not reduce to a constant expression
os.F90:15:7:

   15 |     end function chdir
      |       1
Error: Expecting END INTERFACE statement at (1)

What am I missing here? Is this a scoping issue due to the interface having a separated scope from the module and therefore cannot see the chdir_symbol parameter? Is there a way to make this work or is this usage indeed not possible?

3 Likes

A bug in gfortran based on what I see in the standard. Please file a request at GCC Bugzilla. Or better yet, look to volunteer toward GCC/gfortran bug resolution taskforce and encourage others and assemble a team globally for the same - gfortran needs that, urgently.

4 Likes

I wonder how much effort it would take to join the compiler development for GFortran. I’m certainly intrigued to contribute, but also a bit worried about taking up another huge commitment on my nights and weekend open source schedule.

1 Like

@awvwgk , if there is any one person who can do it, that will be you! Thar’s why I made the comment above. Having written this, I must admit I know little of the actual details of gfortran development. However I can suggest touching base with the names you see on this incident: Nicolas Koenig - Async I/O patch with compilation fix. Many of them are currently active with the work on “native coarrays in gfortran” and they will surely guide any and all Fortranners with the gfortran effort! [RFC] Native Coarrays (finally!)

Also, your approach with the great work you’ve been doing with this site and stdlib can be reused and extended to form a team, firm up and add to the existing documentation in the GCC world as presentable to and consumable by other new volunteers so more folks can start contributing patches for bugs and other development.

2 Likes

One of the possibly annoying prerequisities for your code to be accepted to GCC is to have a copyright signed with GNU. But a small obvious fix offered in a comment in a bug report surely does not count, it can be applied by some existing developer.

I must admit I was always discouraged by the large complicated codebase even though the developers did offer some potentially easy tasks for beginners.

As a workaround, maybe use some macros directly…? something like

module m_os
  use :: iso_c_binding
  implicit none

#ifndef _WIN32
#define prefix ""
#else
#define prefix "_"
#endif

  interface
    function chdir(path) result(stat) bind(C, name=prefix//"chdir")
      import :: c_char, c_int
      character(kind=c_char, len=1), intent(in) :: path(*)
      integer(c_int) :: stat
    end
  end interface
end module
module m_os
  use :: iso_c_binding
  implicit none

#ifndef _WIN32
#define _bind_(x) bind(C, name="_"//x)
#else
#define _bind_(x) bind(C, name=x)
#endif

  interface
    function chdir(path) result(stat) _bind_("chdir")
      import :: c_char, c_int
      character(kind=c_char, len=1), intent(in) :: path(*)
      integer(c_int) :: stat
    end
  end interface
end module
1 Like

This is now reported in 100870 – Constant expression for bind(C) name in interface body not importable for gfortran and in TPR #30170 for nvfortran.

3 Likes