Bug 59560 - Resolution generic procedure of derived types fail
Summary: Resolution generic procedure of derived types fail
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: fortran (show other bugs)
Version: 4.8.2
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-12-19 13:29 UTC by klaas_giesbertz
Modified: 2014-01-08 12:06 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2013-12-19 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description klaas_giesbertz 2013-12-19 13:29:09 UTC
I want to achieve some more complicated structure in fortran2008 and I am not sure how to do it according to the standard. So I am even not sure if it is a bug, or simply not possible with fortran2008.

I want to make a base class, preferably an abstract one, which promises a subroutine which operates on TWO arguments of this base class. The derived class should implement this and there is some other class using this procedure which only knows about the base class. This was actually possible with gcc 4.7 by declaring both arguments of the subroutine as class. As an example, consider the following program (sorry, quite lengthy due to the several classes):

module BaseModule
  implicit none
  private

  type, public, abstract :: BaseClass
  contains
    procedure(FuncAbstr), deferred :: Func
  end type

  abstract interface
    subroutine FuncAbstr(self, other)
      import
      class(BaseClass), intent(inout) :: self
      class(BaseClass), intent(in)    :: other
    end subroutine
  end interface
end module

module UseBaseModule
  use BaseModule
  implicit none
  private

  type, public :: UseBaseClass
    class(BaseClass), pointer :: base => null()
  contains
    procedure :: Init
    procedure :: CallFunc
  end type

contains

  subroutine Init(self, base)
    class(UseBaseClass),       intent(inout) :: self
    class(BaseClass), pointer, intent(in)    :: base

    self%base => base
  end subroutine

  subroutine CallFunc(self)
    class(UseBaseClass), intent(inout) :: self
    class(BaseClass), allocatable :: newBase

    allocate(newBase, mold=self%base)

    call newBase%Func(self%base)
  end subroutine
end module

module DerivedModule
  use BaseModule
  implicit none
  private

  type, public, extends(BaseClass) :: DerivedClass
    real :: x
  contains
    procedure :: Func
  end type

contains

  subroutine Func(self, other)
    class(DerivedClass), intent(inout) :: self
    class(DerivedClass), intent(in)    :: other

    self%x = other%x
    write(*,*) 'Derived Func called'
  end subroutine

end module

program Test
  use BaseModule
  use UseBaseModule
  use DerivedModule
  implicit none

  class(BaseClass), allocatable :: derived
  type(UseBaseClass) :: useBase

  allocate(DerivedClass :: derived)

  call useBase%Init(derived)
  call useBase%CallFunc()
end program


This code compiles and runs correctly with gcc4.7.3, but gcc4.8.2 gives the following compile error:

Test1.f08:58.13:

procedure :: Func
             1
Error: Argument mismatch for the overriding procedure 'func' at (1): Type/rank mismatch in argument 'other'

and some more which are not relevant.

I actually do not even know if this code is supposed to compile, since it is not clear to me if such kind of overloading is allowed by the fortran standard.

One way around this problem might be to give up the possibility to use an abstract type and to use a generic interface with an explicit type for the 2nd argument of the subroutine. The type is now required to facilite the resolution of the generic subroutine. However, this generic subroutine is not correctly resolved. As an example consider the following code (again quite lengthy, sorry):

module BaseModule
  implicit none
  private

  type, public :: BaseClass
  contains
    procedure :: BaseFunc
    generic   :: Func => BaseFunc
  end type

contains

  subroutine BaseFunc(self, other)
    class(BaseClass), intent(inout) :: self
    type(BaseClass),  intent(in)    :: other

    write(*,*) 'Base Func called'
  end subroutine

end module

module DerivedModule
  use BaseModule
  implicit none
  private

  type, public, extends(BaseClass) :: DerivedClass
    real :: x
  contains
    procedure :: DerivedFunc
    generic   :: Func => DerivedFunc !Extend generic Func
  end type

contains

  subroutine DerivedFunc(self, other)
    class(DerivedClass), intent(inout) :: self
    type(DerivedClass),  intent(in)    :: other

    self%x = other%x
    write(*,*) 'Derived Func called'
  end subroutine

end module

module UseBaseModule
  use BaseModule
  implicit none
  private

  type, public :: UseBaseClass
    class(BaseClass), pointer :: base => null()
  contains
    procedure :: Init
    procedure :: CallFunc
  end type

contains

  subroutine Init(self, base)
    class(UseBaseClass),      intent(inout) :: self
    class(BaseClass), target, intent(in)    :: base

    self%base => base
  end subroutine

  subroutine CallFunc(self)
    class(UseBaseClass), intent(in) :: self
    class(BaseClass), allocatable :: newBase

    allocate(newBase, mold=self%base)

    call newBase%Func(self%base)
  end subroutine
end module

program Test
  use DerivedModule
  use UseBaseModule
  implicit none

  type(DerivedClass) :: derived
  type(UseBaseClass) :: useBase

  call useBase%Init(derived)
  call useBase%CallFunc()
end program

This code compiles both with gcc4.7.3 and gcc4.8.2 and gives in both cases the incorrect output:
Base Func called
It should have called the DerivedFunc instead. Building a double block of select types around it solves the problem, but the UseBase needs to know about the Derived as well to do this, which is not desirable.

Hope someone can help me out.
Klaas Giesbertz
Comment 1 klaas_giesbertz 2013-12-19 13:35:31 UTC
Sorry, the 2nd argument of Init of UseBase in the 1st test should have been target instead of pointer. In that case 'program Test' becomes the same as in the 2nd test.
Comment 2 janus 2013-12-19 16:44:01 UTC
(In reply to klaas_giesbertz from comment #0)
> This code compiles and runs correctly with gcc4.7.3, but gcc4.8.2 gives the
> following compile error:
> 
> Test1.f08:58.13:
> 
> procedure :: Func
>              1
> Error: Argument mismatch for the overriding procedure 'func' at (1):
> Type/rank mismatch in argument 'other'

For your first example, I get this error with all version from 4.6 to trunk (with slight variations in the wording). Are you sure the code you posted is what you compiled with 4.7?


> I actually do not even know if this code is supposed to compile, since it is
> not clear to me if such kind of overloading is allowed by the fortran
> standard.

I think the error is correct. The Fortran standard does not allow this.
Comment 3 janus 2013-12-19 16:57:56 UTC
(In reply to klaas_giesbertz from comment #0)
> This code compiles both with gcc4.7.3 and gcc4.8.2 and gives in both cases
> the incorrect output:
> Base Func called
> It should have called the DerivedFunc instead.

The second example is a bit tricky.

Note that the resolution of your generic 'Func' happens at compile time and only based on the declared type -- the dynamic type of the objects does not matter at all.

I would say the behavior here is ok as well.
Comment 4 klaas_giesbertz 2013-12-19 17:12:00 UTC
In reply to Comment2&3 (Janus):

I have copied it back from my post and it still compiles with my gfortran4.7.3. Could it have something to do with my build of gcc? I used macports to install it on my machine, so I am not fully aware of the dependencies. Would be strange though. Anyway, it is wrong fortran, so it should not work.

A 'select type' could make the distinction, so 'select type' works dynamically, but the generic works statically. Confusing. So you would say that this kind of functionality is out of reach for fortran2008? Do you have any idea for a different solution? I know this is not the place to ask, but I would be grateful for some help.
Comment 5 janus 2013-12-19 17:32:14 UTC
(In reply to klaas_giesbertz from comment #4)
> I have copied it back from my post and it still compiles with my
> gfortran4.7.3. Could it have something to do with my build of gcc?

Hopefully not, but it might have to do with the exact version. I just tried it with the most recent branch build:

gcc version 4.7.4 20131219 (prerelease) [gcc-4_7-branch revision 206127] (GCC)

(which yields the same result as the 4.7.3 version provided by Ubuntu, namely rejecting the test case).

What does "gfortran -v" show in your case?
Comment 6 Dominique d'Humieres 2013-12-19 17:44:15 UTC
The test in comment 0 compiles with 4.7.3 and gives at run time

 Derived Func called

And I confirm the reported behavior for the rest of the posts.
Comment 7 janus 2013-12-19 18:10:17 UTC
(In reply to Dominique d'Humieres from comment #6)
> The test in comment 0 compiles with 4.7.3 and gives at run time

Is this with the proper 4.7.3 release or some 4.7 branch build? Can you give the excact version (gfortran -v)? As mentioned in comment 5, it is being rejected with a current 4.7 branch build for me. Some commit on the 4.7 branch might have affected the behavior?
Comment 8 janus 2013-12-19 18:13:49 UTC
(In reply to janus from comment #7)
> Some commit on the 4.7
> branch might have affected the behavior?

After a quick look into the ChangeLog, I already have a suspicion:


2013-06-01  Janus Weil  <janus@gcc.gnu.org>
	    Tobias Burnus  <burnus@net-b.de>

	PR fortran/57217
	* interface.c (check_dummy_characteristics): Symmetrize type check.
Comment 9 Dominique d'Humieres 2013-12-19 18:26:22 UTC
When I say 4.7.3 it means the 4.7.3 release, otherwise I give the revision number or the date if the former is not available.
Comment 10 janus 2013-12-19 18:29:34 UTC
(In reply to Dominique d'Humieres from comment #9)
> When I say 4.7.3 it means the 4.7.3 release, otherwise I give the revision
> number or the date if the former is not available.

Ok, that matches my suspicion (as that commit was after the 4.7.3 release).
Comment 11 klaas_giesbertz 2014-01-08 11:04:02 UTC
Finally figured out how to make it work. I guess this is what the class(*) is useful for. Using class(*) I can simply overload Func and its resolution becomes dynamic. I consider the class(*) quite an overkill, but it works.

So the Base and Derived Modules now become

module BaseModule
  implicit none
  private

  type, public :: BaseClass
  contains
    procedure :: Func
  end type

contains

  subroutine Func(self, other)
    class(BaseClass), intent(inout) :: self
    class(*),         intent(in)    :: other

    write(*,*) 'Base Func called'
  end subroutine

end module

module DerivedModule
  use BaseModule
  implicit none
  private

  type, public, extends(BaseClass) :: DerivedClass
    real :: x
  contains
    procedure :: Func
  end type

contains

  subroutine Func(self, other)
    class(DerivedClass), intent(inout) :: self
    class(*),            intent(in)    :: other

    write(*,*) 'Derived Func called'

    select type(bla => other)
      class is (DerivedClass)
        write(*,*) 'DerivedClass found'
      class default
        write(*,*) 'Bad luck'
    end select
  end subroutine

end module
Comment 12 janus 2014-01-08 11:14:25 UTC
(In reply to klaas_giesbertz from comment #11)
> Finally figured out how to make it work. I guess this is what the class(*)
> is useful for. Using class(*) I can simply overload Func and its resolution
> becomes dynamic. I consider the class(*) quite an overkill, but it works.

I don't think you need unlimited polymorphism, i.e. class(*), for what you're trying to do. It should be enough to declare the 'other' argument as 'class(BaseClass)' in both cases.
Comment 13 klaas_giesbertz 2014-01-08 11:42:24 UTC
You are right! Thanks a lot.
Comment 14 janus 2014-01-08 12:06:39 UTC
In any case I will close this PR now, since I don't see any problems with gfortran's behavior on the test cases reported here.