Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

CLASS argument mismatch

John4
Valued Contributor I
1,347 Views

Hi,

In the core below, I get an error stating that the characteristics of the first argument of the actual procedure do not match those of the dummy procedure.

module mod1
    type, abstract :: t1
        integer :: dummy1 = 0
    contains
        procedure :: execute
    end type

    abstract interface
        subroutine i_t1(this)
            import
            class(t1), intent(INOUT) :: this
        end subroutine
    end interface

contains
    subroutine execute(this, proc)
        class(t1), intent(INOUT) :: this
        procedure(i_t1) :: proc

        call proc(this)

    end subroutine
end module mod1

module mod2
    use mod1

    type, abstract, extends(t1) :: t2
        integer :: dummy2 = 0
    contains
        procedure :: handler2
    end type

contains
    subroutine handler2(this)
        class(t2), intent(INOUT) :: this
    end subroutine
end module mod2

module mod3
    use mod2

    type, extends(t2) :: t3
        integer :: dummy3 = 0
    contains
        procedure :: handler3
        procedure :: do_something
    end type

contains
    subroutine handler3(this)
        class(t3), intent(INOUT) :: this
    end subroutine

    subroutine do_something(this)
        class(t3), intent(INOUT) :: this

        call this%execute(handler2)
        call this%execute(handler3)
    end subroutine
end module mod3

Even if t2 is not an abstract type, there seems to be no way of passing handler2 or handler3 to the type-bound procedure (I already tried the f95 way as well).

Is it a bug in the implementation? Or is the compiler right at complaining here?





 

0 Kudos
9 Replies
kostas85
Beginner
1,347 Views

Hello John

How about changing the handlers 2 as :

    subroutine handler2(this)
        class(t1), intent(INOUT) :: this
    end subroutine

    subroutine handler3(this)
        class(t1), intent(INOUT) :: this
    end subroutine

and using  nopass:

        procedure, nopass :: handler2

        procedure, nopass :: handler3

This should compile I think

 

0 Kudos
John4
Valued Contributor I
1,347 Views

I'm not really looking for workarounds to make it just compile, and that particular workaround doesn't work for me ---since, in the actual code, handler2 and handler3 would have to invoke type-bound procedures from t2 and t3, respectively.

It just seems like weird behavior to me, and I would like to be certain that I'm not doing something wrong.

The following code fails to compile as well, so the issue is not related to the ABSTRACT attribute of t1:

module mod1

    type :: t1
        integer :: dummy1 = 0
    contains
        procedure :: execute
    end type

    abstract interface
        subroutine i_t1(this)
            import
            class(t1), intent(INOUT) :: this
        end subroutine
    end interface

contains
    subroutine execute(this, proc)
        class(t1), intent(INOUT) :: this
        procedure(i_t1) :: proc

        call proc(this)

    end subroutine

end module mod1

module mod2
    use mod1

    type, extends(t1) :: t2
        integer :: dummy2 = 0
    contains
        procedure :: do_something
    end type

contains
    subroutine handler2(this)
        class(t2), intent(INOUT) :: this
    end subroutine

    subroutine do_something(this)
        class(t2), intent(INOUT) :: this
        procedure(i_t1), pointer :: pp

        pp => handler2
       call this%execute(pp)
    end subroutine

end module mod2



 

0 Kudos
FortranFan
Honored Contributor III
1,347 Views

John wrote:

...

Is it a bug in the implementation? Or is the compiler right at complaining here?

I think the compiler is right; you can comb through the Fortran standards documentation and see if you find something that supports your case, but I'll be surprised if you find any other compiler that accepts this.

Separately, from a polymorphism and OO design point of view, your code doesn't make much sense to me and I can't identify the design pattern and a practical value of such an approach.  Why do you need a construct such as [fortran]call this%execute(handler2)[/fortran] when you can do [fortran]call this%handler2()[/fortran] directly and accrue all the benefits of class design, inheritance, and polymorphism in this simpler way?

0 Kudos
Steven_L_Intel1
Employee
1,347 Views

This is not a bug. Unlike with direct calls, procedure pointers must have the SAME interface, not just compatible interfaces.

0 Kudos
John4
Valued Contributor I
1,347 Views

FortranFan wrote:

I think the compiler is right; you can comb through the Fortran standards documentation and see if you find something that supports your case, but I'll be surprised if you find any other compiler that accepts this.

Separately, from a polymorphism and OO design point of view, your code doesn't make much sense to me and I can't identify the design pattern and a practical value of such an approach.  Why do you need a construct such as

call this%execute(handler2)

when you can do

call this%handler2()

directly and accrue all the benefits of class design, inheritance, and polymorphism in this simpler way?

 

Most of the code in this forum doesn't have to make sense.  It only has to prove a point (in this case, the point is showing the error thrown by the compiler).  It's actually the second time you fail to realize that.

In the actual code the base abstract class implements a comparison of methods for doing certain things, and each handler implements a particular method.  The this%execute() procedure would actually be this%addSomeMethodOfComputingThings(), and just stores a pointer to the procedure passed as argument, to be called later; and it stores many of those. Another procedure in the base class calls them one after another, compares and outputs the results ---and for some reason that makes sense to me.

The actual code compiled and ran just fine with a previous version of ifort (even with some of the methods being in separate libraries, loaded through dlfcn and C_F_PROCPOINTER), but with the latest version it throws an error.


 

0 Kudos
John4
Valued Contributor I
1,347 Views

Steve Lionel (Intel) wrote:

This is not a bug. Unlike with direct calls, procedure pointers must have the SAME interface, not just compatible interfaces.

Steve, in the original code I posted, there are no procedure pointers.  It's just procedures being passed as arguments.  The argument of each of those procedures being passed, satisfies the required interface (since it's of the same class), but the compiler sees differently.

0 Kudos
Steven_L_Intel1
Employee
1,347 Views

Right - we fixed a bug. The standard says:

"If proc-pointer-object has an explicit interface, its characteristics shall be the same as proc-target except that proc-target may be pure even if proc-pointer-object is not pure and proc-target may be an elemental intrinsic procedure even if proc-pointer-object is not elemental." [F2008, p160, lines 6-8]

0 Kudos
John4
Valued Contributor I
1,347 Views

Steve,

The text you cite mentions "explicit interface", and that made me think of a somewhat riskier approach: Is the following code valid? Or does it just work because of a yet-to-be-fixed bug?

module mod1

    type :: availableMethods
        procedure(i_compute), pointer, nopass :: proc => NULL()
    end type

    type :: compute
        type(availableMethods) :: methods(2)
    contains
        procedure :: addMethod
        procedure :: execute
    end type

    abstract interface
        subroutine i_compute(this)
            import
            class(compute), intent(INOUT) :: this
        end subroutine
    end interface

contains
    subroutine addMethod(this, proc)
        class(compute), intent(INOUT) :: this
        procedure() :: proc

        integer :: i, idx

        idx = 0
        do i = 1, SIZE(this%methods)
            if (.NOT. ASSOCIATED(this%methods(i)%proc)) then
                idx = i
                exit
            endif
        enddo

        if (idx == 0) then
            write (*,'(A)') 'Cannot add more'
            return
        endif

        this%methods(idx)%proc => proc
    end subroutine

    subroutine execute(this)
        class(compute), intent(INOUT) :: this

        integer :: i

        do i = 1, SIZE(this%methods)
            if (.NOT. ASSOCIATED(this%methods(i)%proc)) cycle
            call this%methods(i)%proc(this)
        enddo
    end subroutine
end module mod1

module mod2
    use mod1

    type, extends(compute) :: implement
        integer :: dummy = 0
    contains
        procedure :: do_something
    end type

contains
    subroutine handler(this)
        class(implement), intent(INOUT) :: this

        call this%do_something()
    end subroutine

    subroutine do_something(this)
        class(implement), intent(INOUT) :: this

        write (*, '(A)') 'Doing something'
    end subroutine
end module mod2

use mod2

type(implement) :: a

call a%addMethod(handler)

call a%execute()

end




 

0 Kudos
Steven_L_Intel1
Employee
1,347 Views

I quoted the wrong part of the standard. You're passing a procedure dummy argument. But the same issue arises - the characteristics of the arguments must be the same and yours are not.

0 Kudos
Reply