Search code examples
fortrangfortran

Why Fortran/gfortran assume the UNKNOWN type?


I have a grammar question about Fortran. It seems that Fortran (or gfortran?) cannot assume a returning value of a function defined in an interface block in an abstract interface. I tried to compile the following codes by gfortran (version 9.4.0).

abstract_class.f90:

module module_abstract_class
    implicit none

    type, public, abstract :: abstract_class
        contains
        procedure(method_interface), pass(self), deferred :: method
    end type

    abstract interface
        pure function method_interface(self, func, a, b) result(ret)
            import abstract_class
            class(abstract_class), intent(in) :: self
            real                 , intent(in) :: a, b
            real                              :: ret

            interface
                pure function func(a, b) result(ret)
                    real, intent(in) :: a, b
                    real             :: ret
                end function
            end interface

        end function
    end interface
end module

extend_class.f90:

module module_extend_class
    use module_abstract_class

    implicit none

    type, extends(abstract_class) :: extend_class
        contains
        procedure, public, pass(self) :: method
    end type

    contains

    pure function method(self, func, a, b) result(ret)
        class(extend_class), intent(in) :: self
        real               , intent(in) :: a, b
        real                            :: ret

        interface
            pure function func(a, b) result(ret)
                real, intent(in) :: a,b
                real             :: ret
            end function
        end interface

        ret = func(a, b)
    end function method
end module

main.f90:

program i_love_fortran
    use module_extend_class

    type(extend_class) :: e

    print *, e%method(add, 1, 2)

contains

    pure function add(a, b) result(ret)
        real, intent(in) :: a, b
        real             :: ret
        ret = a + b
    end function
end Program i_love_fortran

shell:

gfortran abstract_class.f90 extend_class.f90 main.f90

But, I could not compile this code due to the following errors.


gfortran abstract_class.f90 extend_class.f90 main.f90 extend_class.f90:8:17:

8 |         procedure, public, pass(self) :: method
  |                 1

Error: Argument mismatch for the overriding procedure ‘method’ at (1): Type mismatch in argument 'func' (UNKNOWN/REAL(4)) extend_class.f90:6:49:

6 |     type, extends(abstract_class) :: extend_class
  |                                                 1

Error: Derived-type ‘extend_class’ declared at (1) must be ABSTRACT because ‘method’ is DEFERRED and not overridden main.f90:2:8:

2 |     use module_extend_class
  |        1

Fatal Error: Cannot open module file ‘module_extend_class.mod’ for reading at (1): No such file or directory compilation terminated.


I think "func" defined in "method" should be REAL(4). Why does Fortran/gfortran assume that "func" is UNKNOWN type?


Solution

  • Your func is just another abstract interface, because id defines what the actual pure function will be like when the function called. So, you should include it just as also an abstract interface and make it public so other modules can access it. Then, you reference it as a function dummy argument using

    procedure(func_template) :: func
    

    This now works:

    module module_abstract_class
        implicit none
    
        type, public, abstract :: abstract_class
            contains
            procedure(method_interface), pass(self), deferred :: method
        end type
    
        interface
        end interface
    
        abstract interface
            pure function func_template(a, b) result(ret)
                real, intent(in) :: a, b
                real             :: ret
            end function func_template
        
            pure function method_interface(self, func, a, b) result(ret)
                import abstract_class,func_template
                class(abstract_class), intent(in) :: self
                real                 , intent(in) :: a, b
                procedure(func_template)          :: func
                real                              :: ret
    
            end function
        end interface
    end module
    
    
    module module_extend_class
        use module_abstract_class
    
        implicit none
    
        type, extends(abstract_class) :: extend_class
            contains
            procedure, public, pass(self) :: method
        end type
    
        contains
    
        pure function method(self, func, a, b) result(ret)
            class(extend_class), intent(in) :: self
            real               , intent(in) :: a, b
            procedure(func_template)        :: func
            real                            :: ret
    
            ret = func(a, b)
        end function method
    end module
    
    
    program i_love_fortran
        use module_extend_class
    
        type(extend_class) :: e
    
        print *, e%method(add, 1.0, 2.0)
    
    contains
    
        pure function add(a, b) result(ret)
            real, intent(in) :: a, b
            real             :: ret
            ret = a + b
        end function
    end Program i_love_fortran