Passing functions, which have derived types as arguments, as arguments in Fortran

Viewed 326

I have a subroutine which receives some data and another function as arguments. The function can take as argument this data. The subroutine is just there to call this function with the given data. Now let us assume the data can be of type foo. I would assume that I can also pass data of the from foo derived type bar. Apparently Fortran disagrees.

I have tried to compile the code below with gfortran 9. It fails with the error message:

   47 |         call funA(x, y, baz)
      |                        1
Error: Interface mismatch in dummy procedure ‘baz’ at (1): Type mismatch in argument 'y' (CLASS(foo)/CLASS(bar))

Various other attempts of replacing foo by bar yield similar resuts. Using pgfortran 19.4 yields the same error.

module A
    implicit none
    private

    type, abstract, public :: foo
    end type foo

    public :: funA

contains

    subroutine funA(x, y, baz)
        integer, intent(in)    :: x
        class(foo), intent(in) :: y
        interface
            function baz(x, y) result(z)
                import foo
                integer,    intent(in) :: x
                class(foo), intent(in) :: y
                integer                :: z
            end function baz
        end interface

        write(*,*) baz(x, y)
    end subroutine funA

end module

module B
    use A
    implicit none
    private

    type, extends(foo), public :: bar
        integer :: ii
    end type bar

    public :: fiz

contains

    function baz(x, y) result(z)
        integer,    intent(in) :: x
        class(bar), intent(in) :: y
        integer :: z

        z = x + y%ii
    end function baz

    subroutine fiz(x, y)
        integer,    intent(in) :: x
        class(bar), intent(in) :: y

        call funA(x, y, baz) ! <---- This causes errors.
    end subroutine fiz
end module

program meh
    use B
    implicit none
    integer   :: x
    type(bar) :: y
    x = 1
    y%ii = 2
    call fiz(x, y)
end program meh

My expectation would have been that since y is of type bar, it is also of type foo and thus I can pass it to funA. The function baz must know that y is of type bar, otherwise it cannot access ii. However the rest of the code doesn't really need to know this, does it?

Where is the error in my reasoning? How can I get the above code to compile and run?

1 Answers

The error pointed to here is in the characteristics of the procedure argument. The procedure dummy argument baz of funA has characteristics which are not matched by the procedure actual argument baz of module B. The dummy argument has second argument (y) of declared type foo whereas the procedure baz passed to it has corresponding argument of declared type bar.

It is not allowed to have this mismatch in characteristics in dummy and actual procedure arguments.

To fix this problem it is necessary to make the dummy arguments y of the dummy baz and module procedure baz to be of the same declared type (probably foo). Of course, you'll then need to work around having y in baz (of module B) having declared type foo: such a y doesn't have a component ii. A quick hack would be to use a select type construct, but there may well be other approaches if a different flow can be designed.


As you can see, often discussions around code like this are helped if various arguments and the like have distinct names.

Related