Support for Gfortran-style arbitrary-typed parameters

There is a common pattern in gfortran for passing “arbitrary” value to C code via

   type (*), dimension (*) :: a

declaration. Unfortunately this does not seem to work for flang:

$ cat repro.f90
module foo_mod
  interface foo
    procedure :: foo_int
  end interface
contains
  function foo_int (a) bind (C, name = "foo_int")
    logical :: foo_int
    !GCC$ ATTRIBUTES NO_ARG_CHECK :: a
    type (*), dimension (*) :: a
    ! This does not work either:
    ! type (*), dimension (..) :: a
  end function
end module foo_mod

module main_mod
  use foo_mod
contains
  function bar(P)
    logical :: bar
    INTEGER :: P(:, :)
    bar = foo(P)
  end function
end module main_mod

$ gfortran tmp.f90 -fsyntax-only
$ flang tmp.f90 -fsyntax-only
./tmp.f90:22:11: error: No specific procedure of generic 'foo' matches the actual arguments
      bar = foo(P)
            ^^^^^^

Is there any way to achieve the similar functionality with flang? If not, what would be the best way to implement it in flang’s source code?

What error are you getting here ?

I did not get errors when running flang semantics on the assumed rank version of your example.

@jeanPerier you are right (and i was wrong) - the assumed rank indeed works. So is this the preferred approach in this case?

Another opinion would be good here because I am not familiar with such usages.

I would tend to say that this is the standard way to do it, assuming the C function foo_int expects a CFI_cdesc_t * (Fortran standard mandates in 18.3.6 point 2. (5) that assumed-rank must be passed as pointer to a CFI descriptor).

type (*), dimension (*) :: a must be passed as void* to the data according to same Fortran standard section.

So this means that the two options do not lead to the same thing when calling foo(P).

  • With type (*), dimension (*) :: a, first of all, you need a compiler directive to silence the rank mismatch (flang does not handle such directive, hence the fact you are still getting the semantics error). Then, what happens is that the data of P will be copied in a contiguous block of memory (because P may not be contiguous, but the dummy argument of foo must be contiguous), and a pointer to that block of memory will be passed to foo_int. After the call, the contiguous block of memory will be copied back into P (to cover cases where foo_int modified the argument).

  • With type (*), dimension (..) :: a, you do not need compiler specific directives because I think that usage is legal. However, this time you a CFI_cdesc_t * descriptor will be made to described P . It will describe its base address, rank, type, shape and strides in a standard way. This descriptor will be passed to foo_int without making a temporary contiguous copy of the data.

I cannot really say which usage is right. The second usage is the standard one, but this means that the C code must deal with the descriptor and non contiguous data (which it easily can using the ISO_Fortran_binding.h provided with the Fortran compiler).

Please note that the Fortran standard did standardized the field names of such CFI_cdesc_t descriptor, and it provided enum names to match the types, but it did not mandates an exact C structure for the descriptor, and did not assign of the type enum names to integer values. This means that C code using a CFI_cdesc_t is portable from a source code point of view (it can be recompiled with the ISO_Fortran_binding.h of different Fortran compilers), but not portable once compiled (a foo_int C binary object might not work with Fortran code calling it that is compiled with a different Fortran compiler that the one whose ISO_Fortran_binding.h was used to compile foo_int).

2 Likes

Jean, thanks a lot for the in-depth explanation. In general I’m looking for an interface for precompiled C library so I guess I need to use the assumed-size syntax and somehow teach Flang to ignore the rank mismatch. I will look into this when time permits, for now I can live with the assumed-rank approach.

ignore_tkr is probably useful here. It is supported by pgfortran/nvfortran and classic flang based compilers. From the flang documentation it seems it is supported by the parser, not sure whether codegen support is there. Is that what you are saying @jeanPerier?

Thanks for pointing to ignor_tkr @kiranchandramohan, it seems like the right equivalent of the !GCC$ ATTRIBUTES NO_ARG_CHECK here. I just checked and flang supports parsing it, but it is actually not yet used in semantics (as far as I can tell), which means it does not silence the semantics error yet.

I cannot say for sure, but I would expect codegen will have little or nothing to do in order to support it. It will just happily cast the actual argument addresses to the dummy argument address if needed.

Note that semantics of ignore_tkr does not seem to be very portable: I couldn’t find it in https://www.nag.com/market/training/fortran-workshop/f2018_standard.pdf and gfortran does not seem to support it:

$ cat repro.f90
module foo_mod
  interface foo
    module procedure :: foo_int
  end interface
contains
  integer function foo_int(A)
    implicit none
    !dir$ ignore_tkr(tkr) A
    type(*), dimension(*) :: A
    foo_int = 1
  end function
end module foo_mod

module main_mod
  USE foo_mod
contains
  INTEGER FUNCTION ACC_DPOTRF(A, B, C)
    IMPLICIT NONE
    REAL :: A(:, :)
    REAL :: B(:, :)
    REAL :: C(100, *)
    ACC_DPOTRF = foo(A) + foo(B) + foo(C)
  END FUNCTION
end module main_mod
$ gfortran -c repro.f90
   22 |     ACC_DPOTRF = foo(A) + foo(B) + foo(C)
      |                                      1
Error: There is no specific function for the generic ‘foo’ at (1)