Inheritance, Overriding and Deferred Procedure Problems

Inheritance, Overriding and Deferred Procedure Problems

Hi everybody,Recently I have been facing terrible problems with FORTRAN classes. I don't know if I am implementing it in the wrong way of if simply the features are not implemented yet. As my code is quite big, I will post here a small example in which I have the problem that I consider to be the core of my other problems.

module m_first

implicit none

private

public :: c_first

type, abstract :: c_first
   integer :: a, b
   contains
      procedure(proc_abstract), deferred :: proc
      procedure, pass(self) :: print_info
end type c_first

abstract interface
   function proc_abstract(self) result(int_out)
      import :: c_first
      class(c_first), intent(inout) :: self
      integer :: int_out
   end function proc_abstract
end interface

 contains
 
subroutine print_info(self)
   class(c_first), intent(in) :: self
   
   print *,'==== from c_first: ', self%a

end subroutine print_info

end module m_first
[fxfortran]module m_second

 use m_first
 
 implicit none
 
 private 
 
 public :: c_second
 
 type, abstract, extends(c_first) :: c_second
   real :: c
   contains
      procedure(scream_abstract), deferred :: scream
 end type c_second

 
 abstract interface
   subroutine scream_abstract(self)
      import :: c_second
      class(c_second), intent(inout) :: self
   end subroutine scream_abstract
 end interface
 
end module m_second[/fxfortran]
module m_thirdA

 use m_second
 
 implicit none
 
 private
 
 public ::  c_thirdA
 
 type, extends(c_second) :: c_thirdA
   real :: d
   contains
      procedure, pass(self) :: scream
      procedure, pass(self) :: proc
      procedure, pass(self) :: print_info
 end type c_thirdA

 contains
 
 function proc(self) result(int_out)
   class(c_thirdA), intent(inout) :: self
   integer :: int_out
   int_out=1
   self%d = float(42/666)
 end function proc
 
 
 subroutine scream(self)
   class(c_thirdA), intent(inout) :: self
   print *, 'AAAAAAAAAAAAAH ',self%a+self%b,' - c_thirdA'
 end subroutine scream
 
 
 subroutine print_info(self)
   class(c_thirdA), intent(in) :: self
   print *,'I am c_thirdA. Proof? My d is ', self%d
 end subroutine print_info
 
end module m_thirdA 
module m_thirdB

 use m_second
 
 implicit none
 
 private
 
 public ::  c_thirdB
 
 type, extends(c_second) :: c_thirdB
   character*512 :: d
   contains
      procedure, pass(self) :: scream
      procedure, pass(self) :: proc
      procedure, pass(self) :: print_info
 end type c_thirdB

 contains
 
 function proc(self) result(int_out)
   class(c_thirdB), intent(inout) :: self
   integer :: int_out
   int_out=-1
   self%d = 'BAZINGA'
 end function proc
 
 
 subroutine scream(self)
   class(c_thirdB), intent(inout) :: self
   print *, 'AAAAAAAAAAAAAH ',self%a+self%b,' - c_thirdB'
 end subroutine scream
 
 
 subroutine print_info(self)
   class(c_thirdB), intent(in) :: self
   print *,'I am c_thirdB. Proof? My d is ', self%d
 end subroutine print_info
 
end module m_thirdB 
program main_multinh

use m_thirdA
use m_thirdB
use m_second

implicit none
 
 class(c_second), pointer :: obj
 integer :: err


 ! This should be c_thirdA
 call choose_class(obj,1)
 err = obj%proc()
 call obj%scream()
 call obj%print_info()
 
 ! This should be c_thirdB
 call choose_class(obj,-1)
 err = obj%proc()
 call obj%scream()
 call obj%print_info()
 
 
 contains 
 
 subroutine choose_class(obj_second,switch)
   class(c_second), intent(inout), pointer :: obj_second
   integer, intent(in) :: switch
   
   type(c_thirdA) :: obj_thirdA
   type(c_thirdB) :: obj_thirdB

   obj_thirdA%a=1
   obj_thirdA%b=2
   obj_thirdB%a=4
   obj_thirdB%b=8
   
   if (switch>0) then
      allocate(obj_second, source=obj_thirdA)
   else
      allocate(obj_second, source=obj_thirdB)
   end if
   
 end subroutine 
 
end program main_multinh

So, when compiling this very code I get the following error:

 perjanus  Area51 $  make multinh
ifort -I. -c m_first.f90
ifort -I. -c m_second.f90
ifort -I. -c m_thirdA.f90
ifort -I. -c m_thirdB.f90
ifort -I. -c main_multinh.f90
ifort  -I. m_first.o m_second.o m_thirdA.o m_thirdB.o main_multinh.o -o multinh.exe 
main_multinh.o: In function `MAIN__':
main_multinh.f90:(.text+0xf3): undefined reference to `proc_abstract_'
main_multinh.f90:(.text+0x1fc): undefined reference to `proc_abstract_'
make: *** [multinh] Error 1
 perjanus  Area51 $ 

Apparentely the compiler has problems finding the abstract interface for proc_abstract which is defined in m_first, and is private. Well, I've tried making it public, it didn't help so I got lost again. Is this expected? What is the CORRECT way to avoid this? Apparentely the compiler has problems to deal with DEFERRED procedures which were DEFERRED more than once in child classes.Nevertheless, I've found a not-so-elegant workaround. I've added the proc_abstract to m_second.f90's abstract interface, changing only the line class(c_first) by class(c_second), and then re-declared "procedure(proc_abstract), deferred :: proc" in c_second class "contains" section. Now it compiled fine, but didn't work fine.When I run the executable, I get:

 perjanus  Area51 $  ./multinh.exe 
 AAAAAAAAAAAAAH            3  - c_thirdA
 ==== from c_first:            1
 AAAAAAAAAAAAAH           12  - c_thirdB
 ==== from c_first:            4
 perjanus  Area51 $  

Simply the print_info procedure is not being overriden. This is a small example, in my original code things are a little more complex (we have 4 or 5 inheritance levels sometimes).Am I doing anything wrong? Is there any concept that I am missing? Is there any feature that is needed for this to work yet is not implemented yet? Is that not supposed to work at all?Thanks in advance.

19 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Forgot to mention that I'm using intel fortran compiler 12.0.4.

With 12.1.0.233 the code compiles fine and produces the output

AAAAAAAAAAAAAH 3 - c_thirdA
I am c_thirdA. Proof? My d is 0.0000000E+00
AAAAAAAAAAAAAH 12 - c_thirdB
I am c_thirdB. Proof? My d is
BAZINGA

Regards
Reinhold

That is indeed interesting.I have tried to compile with 12.1 right after posting this here and received

ifort  -I. m_first.o m_second.o m_thirdA.o m_thirdB.o main_multinh.o -o multinh.exe 
main_multinh.o:(.rodata+0x88): undefined reference to `proc_abstract_'
main_multinh.o:(.rodata+0x108): undefined reference to `proc_abstract_'
make: *** [multinh] Error 1

That is, however, if I include proc_abstract in c_second and re-defer it. If I remove this, it compiles fine! Just making it clear, the undefined reference error happens in 12.0.4 when the deferred proc_abstract is not in c_second, whereas the same error happens in 12.1 if the deferred proc_abstract IS IN c_second.Is there any known bug related to this? My big code compiles with 12.0.4 (when I re-declare all deferred procedures) just presenting some weird behavior eventually; for instance, supose I have a (polymorphic) object beloning to another class (c_other, let's say) which has a pointer to a c_second. It may happen that I am trying to call obj_other%obj_second%proc() and I receive an error at compilation time regarding "undefined reference to proc_abstract". I can workaround it by definingclass(c_second), pointer :: tmp_objtmp_obj =>obj_other%obj_secondcall tmp_obj%proc()Interesting enough, with 12.1, I cannot compile, regardless if I'm using such workaround or not, and regardless if I am re-declaring deferred procedures.

There was a bug in 12.0.5 where the compiler would inappropriately put out an external reference to an abstract interface. In 12.1 we fixed most occurrences of this, and, like Reinhold, I can't get the error to occur in 12.1 (I tried Update 6 and Update 7 to Composer XE 2011) for your sources, though I see exactly the error you report using 12.0.5.

Please show us the output adding -V to display the compiler version.

Steve - Intel Developer Support

Hi Steve, thank you for your answer. The code I've posted previously compiles with no error in 12.1. However, my big code wouldn't. I've tried to make a structure of classes which resembles the one from my big code, the files follow attached with a simple makefile (this is still a simplified version, since my other code relies moslty on pointers like ptr_secondB which you can find in c_secondA).Attempting to compile gives

 perjanus  Area51 $  make
ifort -I. -c m_first.f90
ifort -I. -c m_secondB.f90
ifort -I. -c m_secondA.f90
ifort -I. -c m_thirdA.f90
ifort -I. -c m_thirdB.f90
ifort -I. -c m_forthA.f90
ifort -I. -c m_forthB.f90
ifort -I. -c main_multinh.f90
ifort  -I. m_first.o m_secondB.o m_secondA.o m_thirdA.o m_thirdB.o m_forthA.o m_forthB.o main_multinh.o -o multinh.exe 
main_multinh.o:(.rodata+0x1a8): undefined reference to `func_seconda1_abstract_'
make: *** [multinh] Error 1
 perjanus  Area51 $  ifort -V
Intel Fortran Intel 64 Compiler XE for applications running on Intel 64, Version 12.1.0.233 Build 20110811
Copyright (C) 1985-2011 Intel Corporation.  All rights reserved.
FOR NON-COMMERCIAL USE ONLY

 perjanus  Area51 $  

Can you see anything I've missed?

Attachments: 

AttachmentSize
Downloadapplication/octet-stream multinh.tar_.gz1.9 KB

I get no errors building this with Update 7. You're using Update 6, so please try Update 7 and see what happens.

Steve - Intel Developer Support

Thank you. Done again and the small sample works whereas the big one still doesn't compile with the very same error. I will keep building the example until it stumbles on the same stone as the big one. I'll post when I get it.

Got it. I've simplified the code as much as I could. Follows attached with a simple makefile. Just to make sure, this is the version of the compiler I'm using:

 perjanus  Area51 $  ifort -V
Intel Fortran Intel 64 Compiler XE for applications running on Intel 64, Version 12.1.1.256 Build 20111011
Copyright (C) 1985-2011 Intel Corporation.  All rights reserved.
FOR NON-COMMERCIAL USE ONLY

 perjanus  Area51 $  

Attachments: 

AttachmentSize
Downloadapplication/octet-stream TestClass.tar_.gz3.6 KB

Was anyone able to reproduce the error?

 perjanus  Area51 $  make
cd Class;make
make[1]: Entering directory `/Lago/Projetos/Area51/Class'
ifort -I. -c m_class.f90
ar cr libclass.a m_class.o
make[1]: Leaving directory `/Lago/Projetos/Area51/Class'
cd Operator;make
make[1]: Entering directory `/Lago/Projetos/Area51/Operator'
ifort -I. -I../mod -c m_operator.f90
ifort -I. -I../mod -c m_opextension.f90
ifort -I. -I../mod -c m_opimplt_a.f90
ifort -I. -I../mod -c m_opimplt_b.f90
ar cr liboperator.a m_operator.o m_opextension.o m_opimplt_a.o m_opimplt_b.o
make[1]: Leaving directory `/Lago/Projetos/Area51/Operator'
cd TestClass;make
make[1]: Entering directory `/Lago/Projetos/Area51/TestClass'
ifort -I../mod/ -module ../mod -c main.f90
ifort  -I../mod/ -module ../mod main.o -L../lib/ -loperator -lclass -o TestClass.exe
main.o:(.rodata+0x8): undefined reference to `initialize_abstract_'
main.o:(.rodata+0x68): undefined reference to `initialize_abstract_'
main.o:(.rodata+0x148): undefined reference to `initialize_abstract_'
make[1]: *** [all] Error 1
make[1]: Leaving directory `/Lago/Projetos/Area51/TestClass'
make: *** [all] Error 2
 perjanus  Area51 $  

I could reproduce it in Update 7 but not update 8, which is available now. Please try it.

Steve - Intel Developer Support

Hi Steve Lionel,First of all, sorry for taking so long to reply. Second, I found it a little confusing because I am trying to use Intel Parallel Studio XE and I have just realised that the lastest version of IPS doesn't necessarly contains the lastest version of ifc and/or icc. Anyways, I have both installed.So I am using Update 9 now. It compiles fine, but it gives segmentation fault upon running.

 perjanus  TestClass $  ifort -V
Intel Fortran Intel 64 Compiler XE for applications running on Intel 64, Version 12.1.3.293 Build 20120212
Copyright (C) 1985-2012 Intel Corporation.  All rights reserved.
FOR NON-COMMERCIAL USE ONLY

 perjanus  TestClass $  ./TestClass.exe 
 Which one you choose?: 
1
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source             
TestClass.exe      0000000000403157  Unknown               Unknown  Unknown
TestClass.exe      0000000000402C4C  Unknown               Unknown  Unknown
libc.so.6          00007F80BBBC122D  Unknown               Unknown  Unknown
TestClass.exe      0000000000402B49  Unknown               Unknown  Unknown
 perjanus  TestClass $  ./TestClass.exe 
 Which one you choose?: 
2
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source             
TestClass.exe      0000000000403157  Unknown               Unknown  Unknown
TestClass.exe      0000000000402C4C  Unknown               Unknown  Unknown
libc.so.6          00007F30ADA4D22D  Unknown               Unknown  Unknown
TestClass.exe      0000000000402B49  Unknown               Unknown  Unknown
 perjanus  TestClass $  ./TestClass.exe 
 Which one you choose?: 
0000000000000000000000000000000000
forrtl: severe (174): SIGSEGV, segmentation fault occurred
Image              PC                Routine            Line        Source             
TestClass.exe      0000000000403157  Unknown               Unknown  Unknown
TestClass.exe      0000000000402C4C  Unknown               Unknown  Unknown
libc.so.6          00007F240351822D  Unknown               Unknown  Unknown
TestClass.exe      0000000000402B49  Unknown               Unknown  Unknown
 perjanus  TestClass $  

EDIT#1: Using Update 8 also compiles fine but gives segmentation fault while running.

I had not tried running it before. Let me take another look...

Steve - Intel Developer Support

I can reproduce the problem. The ALLOCATE (SOURCE=) isn't doing everything right to set up the type-bound procedures. If you assign to obj_operator separately, it works. But to do that you have to do the assignment inside a SELECT TYPE construct.

I will report this to the developers and let you know of any progress.

Steve - Intel Developer Support

Okay, thank you very much!I've tried to have defined "type(C_opimplt_A), pointer :: obj_opimplt_A"and then use it to receive the object fromnew_opimplt_a function, and then finally pass it as obj_operator =>obj_opimplt_A. That works fine, but is a little "inelegant" if you have many possible different types.Also, something that I thought that should work just fine is to change the output ofnew_opimplt_a to "TYPE(C_OPImplt_A), POINTER :: self" to attemp to set it just as "obj_operator =>new_opimplt_a(...)" but it also gives a segmentation fault. Apparentely I NEED an intermediateobj_opimplt_A for doing this.

Best Reply

Issue ID is DPD200179385

Steve - Intel Developer Support

This problem has been fixed for a release later this year.

Steve - Intel Developer Support

Thank you for the update. Any estimative for the release date?

Third quarter.

Steve - Intel Developer Support

Leave a Comment

Please sign in to add a comment. Not a member? Join today