Search code examples
typesfortranpolymorphism

implicit real - complex conversion in fortran


New standards of fortran are more strict about the mismatches between the calls and the procedure definition. However, there are a lot of legacy codes where, for instance, complex array is viewed as a collection of reals. This still can be properly compiled by lowering such errors to warnings. In gfortran it is achieved with the -fallow-argument-mismatch. This provides a temporary working solution, but is unsatisfactory in long-run.

program polymorphic_types_wrk
  use Maux_wrk
  implicit none
  integer, parameter       :: dp = kind(1.d0), dim = 10
  real(dp)                 :: wd(2 * dim)
  complex(dp)              :: wz(dim)

  call random_number(wd)
  call copy_real_complex_nonconformant(dim, wd, wz)
  
end program polymorphic_types_wrk

module Maux_wrk

contains
    subroutine copy_real_complex_nonconformant(dim, wd, wz)
    implicit none
    integer, parameter :: dp = kind(1.d0)
    integer, intent(in)     :: dim
    real(dp), intent(in)    :: wd(*)
    complex(dp), intent(out):: wz(*)

    call zcopy(dim, wd, 1, wz, 1)

  end subroutine copy_real_complex_nonconformant
  
end module Maux_wrk

In this example, zcopy is a standard BLAS subroutine. The goal is to copy an array of reals to an array of complexes assuming the data is contiguous. According to the gfortran documentation a standard-conformant solution is possible:

Using this option is strongly discouraged. It is possible to provide standard-conforming code which allows different types of arguments by using an explicit interface and TYPE(*).

So, I modified the code respectively, but it does not compile (Assumed-type argument wd requires an explicit interface):

program polymorphic_types
  use Maux
  implicit none
  integer, parameter       :: dp = kind(1.d0), dim = 10
  real(dp)                 :: wd(2 * dim)
  complex(dp)              :: wz(dim)

  call random_number(wd)
  call copy_real_complex(dim, wd, wz)
  
end program polymorphic_types

module Maux

contains
  subroutine copy_real_complex(dim, wd, wz)
    implicit none
    integer, parameter :: dp = kind(1.d0)
    integer, intent(in)     :: dim
    type(*), intent(in)     :: wd(..)
    complex(dp), intent(out):: wz(..)

    call zcopy(dim, wd, 1, wz, 1)

  end subroutine copy_real_complex
      
end module Maux

It puzzles me since all module subroutines are already having an explicit interface. What kind of interface is required?


Solution

  • There is no standard conforming way to associate a real actual argument with a complex dummy argument. That was just as true 45 years ago.

    What has changed in 45 years has been the tools available to developers to provide alternatives to non-conforming argument mismatches: first generics and then polymorphism and C interoperability. Compilers have also reduced their tolerance for deliberate violations and improved their diagnostics for unintentional violations.

    Assumed type (type(*)) dummy arguments are a Fortran 2018 form of polymorphism, particularly suited to C interoperability. This is not a form of polymorphism which allows a real actual argument to be associated with a complex dummy argument.

    First, to answer about the error about a missing explicit interface. The subroutine copy_real_complex in the module Maux does indeed have an explicit interface available in the main program where it is referenced (through use association).

    However, the subroutine zcopy referenced in the subroutine copy_real_complex does not have an explicit interface available in the subroutine. The compiler is complaining about this missing explicit interface.1

    You need to pass a declared complex wd to zcopy. An assumed type variable is not a declared complex variable, even if it's a dynamic complex variable (and certainly not if its dynamic type is something completely different).

    If you want to make all your argument associations compliant, then you'll need to convert (in some way) your wd to a complex entity. This answer is about the need of/complaints about an explicit interface, but you can find other questions, such as this one about how you can do that conversion. There are ways without lying to your compiler about type matching, but you may decide that -fallow-argument-mismatch is just about acceptable.

    The compiler documentation's comments about the use of type(*) allowing the writing of standard-conforming code is about how, in this case, zcopy could be written. It's not about how something which calls zcopy should be written.

    For practical examples, see how MPI's Fortran 2008 bindings take advantage of type(*) and dimension(..). These allow a single specific procedure such as MPI_SEND to take buffers of any type and rank and don't require the same characteristics in each reference.

    Finally, for the case of the question, the type mismatch makes assumptions about how default real and complex are stored. The Fortran standard gives some assurance about this storage, but much less assurance about the storage of non-default real and non-default complex.

    If you want to avoid -fallow-argument-mismatch you will need to write conforming code and that conforming code will need the actual arguments to BLAS's zcopy to be complex and not real. Consistently using complex entities can be one way, noting

    call random_number(wz%Re)
    call random_number(wz%Im)
    

    won't require any hacks. CBLAS also provides an interface to a language which is much less fussy about type matching.


    1 Although when we look at the definition of BLAS zcopy there's nothing there that requires an explicit interface when referenced, the compiler doesn't know what zcopy looks like. Recall that "explicit" and "implicit" interfaces aren't properties of the procedure itself, but are about what's known in the place referencing the procedure.

    Instead, the compiler knows that a conforming call means that zcopy has a polymorphic dummy argument: the variable wd which is an actual argument is polymorphic, and polymorphic in a way that it may be associated only with a default argument which is polymorphic. A polymorphic dummy argument requires an explicit interface.

    The reference in the subroutine copy_real_complex_nonconformant in the module Maux_wrk to zcopy does not lead to a similar conclusion requiring an explicit interface.