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

Ifort bug: Loosing polymorphic information for overriden operators

Bálint_A_
Beginner
284 Views

Dear Intel developer,

The ifort 16 compiler (16.0.0 20150815) seems to generate non-standard conforming code, when a polymorphic type with an overriden operator is stored as a pointer within a derived type. The self containing example below demonstrates the issue for the comparison operator: the code calls the comparison operator of the basetype, instead of the extended one. Similar error happens if the assignment operator is overriden, so I guess, the problem is operator independent.

Best regards,

Bállint

module typedefs
  implicit none

  type :: Base
  contains
    procedure :: isEqualTo => Base_isEqualTo
    generic :: operator(==) => isEqualTo
  end type Base

  type, extends(Base) :: Extended
    integer :: data
  contains
    procedure :: isEqualTo => Extended_isEqualTo
  end type Extended

contains

  function Base_isEqualTo(this, other) result(isEqual)
    class(Base), intent(in) :: this
    class(Base), intent(in) :: other
    logical :: isEqual

    select type (other)
    type is (Base)
      isEqual = .true.
    class default
      isEqual = .false.
    end select
    print *, "Base_isEqualTo:", isEqual

  end function Base_isEqualTo


  function Extended_isEqualTo(this, other) result(isEqual)
    class(Extended), intent(in) :: this
    class(Base), intent(in) :: other
    logical :: isEqual

    select type (other)
    type is (Extended)
      isEqual = (this%data == other%data)
    class default
      isEqual = .false.
    end select
    print *, "Extended_isEqualTo:", isEqual

  end function Extended_isEqualTo

end module typedefs


program test
  use typedefs
  implicit none

  type :: BasePtr
    class(Base), pointer :: ptr
  end type BasePtr
  
  type(Extended) :: myExt1, myExt2
  logical :: isEqual
  type(BasePtr), allocatable :: array(:)

  myExt1%data = 1
  myExt2%data = 1
  allocate(array(2))
  allocate(array(1)%ptr, source=myExt1)
  allocate(array(2)%ptr, source=myExt2)
  ! This should invoke Extended_isEqualTo, but it calls Base_isEqualTo instead
  isEqual = (array(1)%ptr == array(2)%ptr)
  print *, "Main:", isEqual

end program test

 

0 Kudos
4 Replies
FortranFan
Honored Contributor II
284 Views

I'm not convinced it is an issue with the compiler.  Prima facie, Intel Fortran appears standard-conforming even though it doesn't work as you want it to whereas gfortran does.  I vaguely recall discussions along these lines on this forum and elsewhere; I'm unable to track down any details at present.  But perhaps you can follow up with the standard and point to relevant sections that indicate the behavior should be as you indicate.  Given the polymorphic pointer component, I think the standard requires you to do something along the following lines given how you have defined the Base and Extended types and in that case, Intel compiler will work as you would like it to:

program test
   use typedefs
   implicit none

   type :: BasePtr
      class(Base), pointer :: ptr
   end type BasePtr

   type(Extended) :: myExt1, myExt2
   logical :: isEqual
   type(BasePtr), allocatable :: array(:)

   myExt1%data = 1
   myExt2%data = 1
   allocate(array(2))
   allocate(array(1)%ptr, source=myExt1)
   allocate(array(2)%ptr, source=myExt2)

   select type ( ptr => array(1)%ptr )
      type is ( Extended )
         isEqual = (ptr == array(2)%ptr)
      class default
         isEqual = (array(1)%ptr == array(2)%ptr)
   end select

   print *, "Main:", isEqual

end program test

Upon execution with Intel Fortran,

 Extended_isEqualTo: T
 Main: T
Press any key to continue . . .

Of course, the above change is lame but I think that is what it would take with polymorphic components of the 'class(Base)', a penalty incurred with runtime polymorphism.  But I may be wrong about this, would like to learn better.

0 Kudos
Steven_L_Intel1
Employee
284 Views

I'm looking at this. I think it should work. More later.

0 Kudos
Bálint_A_
Beginner
284 Views

I am quite bad in reading the words of the standard, so I can not point out which part of it is violated by the code above. Thanks for the working example. Actually, although it may work fine, I really need run-time polymorphism, as I am designing collections with elements of arbitrary types.

Also, note, that if I replace the call

isEqual = (array(1)%ptr == array(2)%ptr)

with

isEqual = array(1)%ptr%isEqualTo(array(2)%ptr)

(without any select type statement around) the code works as expected. So, ifort knows how to do proper run-time polymorphism, but forgets it, if it is invoked via an overridden operator.

0 Kudos
Steven_L_Intel1
Employee
284 Views

I think this is the same general issue as https://software.intel.com/en-us/forums/intel-fortran-compiler-for-linux-and-mac-os-x/topic/623372

Here is what the standard says regarding defined operators:

5 A function defines the binary operation x1 op x2 if
 (1) the function is specified with a FUNCTION (12.6.2.2) or ENTRY (12.6.2.6) statement that specifies
 two dummy arguments, d1 and d2,
 (2) either
 (a) a generic interface (12.4.3.2) provides the function with a generic-spec of OPERATOR (op),
 or
 (b) there is a generic binding (4.5.5) in the declared type of x1 or x2 with a generic-spec of
 OPERATOR (op) and there is a corresponding binding to the function in the dynamic type
 of x1 or x2, respectively,
 (3) the types of d1 and d2 are compatible with the dynamic types of x1 and x2, respectively,
 (4) the type parameters, if any, of d1 and d2 match the corresponding type parameters of x1 and x2,
 respectively, and
 (5) either
 (a) the ranks of x1 and x2 match those of d1 and d2 or
 (b) the function is elemental, x1 and x2 are conformable, and there is no other function that defines
 the operation.

In this case, there is a generic-spec of OPERATOR(op) and a corresponding binding in the dynamic type of array()%ptr. I added this to DPD200409351.

0 Kudos
Reply