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

Fortran Compiler Bug: overloading binary and unary operator(-) and use with extended types

SebastianM
Novice
1,697 Views

I came across a strange bug with all Intel-Fortran compilers I tested (Intel 18/19, Intel oneAPI 2023) when overloading the "-" operator for binary AND unary operations (same is also true for "+").

Here is a minimal example for a vector type:

 

module mo_vec
  type :: vector
    real :: x, y
  contains
    procedure :: diff, neg
    generic :: operator(-) => diff, neg
  end type vector

  interface vector
    procedure init_vec
  end interface vector

  type, extends(vector) :: vector_parameter
  end type vector_parameter

  type(vector_parameter), parameter :: vec_x = vector_parameter(1., 0.)
  type(vector_parameter), parameter :: vec_y = vector_parameter(0., 1.)

contains

  pure type(vector) function init_vec(x, y, factor)
    real, intent(in) :: x, y
    real, intent(in), optional :: factor
    real :: factor_
    factor_ = 1.0
    if (present(factor)) factor_= factor
    init_vec%x = factor_ * x
    init_vec%y = factor_ * y
  end function init_vec

  pure type(vector) function diff(this, that)
    class(vector), intent(in) :: this, that
    diff = vector(x=this%x-that%x, y=this%y-that%y)
  end function diff

  pure type(vector) function neg(this)
    class(vector), intent(in) :: this
    neg = vector(x=-this%x, y=-this%y)
  end function neg
end module mo_vec

program vec_test
  use mo_vec
  print*, vec_x - vec_y
end program vec_test

 

First: I had to create an extended type vector_parameter in order to be able to create parameter variables of my vector type. Otherwise Intel-Fortran also throws an error, that I can't use the vector interface (which I need to include the "factor" argument) in a constant expression:

 

main1.f90(19): error #9066: A generic function reference is not permitted in a constant expression.   [INIT_VEC]
  type(vector), parameter :: vec_x = vector(1., 0.)

 

Compiling this example I get:

 

main1.f90(59): error #6355: This binary operation is invalid for this data type.   [VEC_X]
  print*, vec_x - vec_y
----------^
main1.f90(59): error #6355: This binary operation is invalid for this data type.   [VEC_Y]
  print*, vec_x - vec_y
------------------^
main1.f90(59): error #6549: An arithmetic or LOGICAL type is required in this context.
  print*, vec_x - vec_y
----------------^
compilation aborted for main1.f90 (code 1)

 

When I comment out the neg part of the "-" operator:

 

generic :: operator(-) => diff!, neg

 

it works.

I could get it to work, with adding routines to vector_parameter:

 

  type, extends(vector) :: vector_parameter
  contains
    procedure :: diffp, negp
    generic :: operator(-) => diffp, negp
  end type vector_parameter
!...
  pure type(vector) function diffp(this, that)
    class(vector_parameter), intent(in) :: this, that
    diffp = vector(x=this%x-that%x, y=this%y-that%y)
  end function diffp

  pure type(vector) function negp(this)
    class(vector_parameter), intent(in) :: this
    negp = vector(x=-this%x, y=-this%y)
  end function negp

 

 But then, I would also need add routines to subtract vector from vector_parameter and vice versa and it now wont compile with other compilers anymore.

The strange thing is, if I now comment out neg and negp:

 

    generic :: operator(-) => diff!, neg
!...
    generic :: operator(-) => diffp!, negp

 

I get the following error (like with gfortran or NAG before):

 

main1.f90(44): error #5286: Ambiguous generic interface OPERATOR(-): previously declared specific procedure DIFF is not distinguishable from this declaration. [DIFFP]
  pure type(vector) function diffp(this, that)

 

 So I guess, this is a compiler bug.

Opinions?

Cheers, Sebastian

17 Replies
Steve_Lionel
Honored Contributor III
1,628 Views

This may not be a compiler bug, as suggested by commenters in Intel Fortran Compiler Bug: overloading binary and unary operator(-) and use with extended types - Fortran Discourse (fortran-lang.discourse.group)  I have not studied the issue myself.

0 Kudos
SebastianM
Novice
1,580 Views

I still think there is something wrong as @IanH also suggests.

The reason for using an extended type for constant expressions is also discussed here:
https://community.intel.com/t5/Intel-Fortran-Compiler/Custom-constructor-initializing-constant-error-6259-with-IFORT/m-p/1166969/highlight/true#M144683

0 Kudos
IanH
Honored Contributor II
1,572 Views

There's no apparent need (with what you have shown) for the extended type. 

Ditch the extension.  Remove the OPTIONAL argument from the third argument to init_vec and associated logic to set the default value for the factor when the third argument is not present.  Calls to init_vec with two arguments are just duplicating what effectively happens with the structure constructor.

Structure constructors with constant expressions for the component specifications can be used in constant expressions, user provided functions can not.

SebastianM
Novice
1,567 Views

That is of course true, but I just created an artificial example that is short enough to discuss about. In my application I need the optional argument(s).

 

One hacky option I may use is to add a flag "constant" to the vector to make the native interface distinguishable from my custom one, so I can use it for parameters:

  type :: vector
    logical :: constant = .false.
    real :: x, y
  contains
    procedure :: diff, neg
    generic :: operator(-) => diff, neg
  end type vector
!...
  type(vector), parameter :: vec_x = vector(.true., 1., 0.)

Then I don't need the extended type for parameters and I don't run into the described bug.

0 Kudos
IanH
Honored Contributor II
1,542 Views

Be mindful that the cure is not worse that the disease. 

If a construction-like function is unintentionally hiding the desired structure constructor, then the simple solution is to use an identifier other than the type name to access the construction-like function.  You can call it `vector_with_factor` or similar, potentially gaining code clarity.

mic_esp96
New Contributor I
1,539 Views

"If a construction-like function is unintentionally hiding the desired structure constructor, then the simple solution is to use an identifier other than the type name to access the construction-like function. You can call it `vector_with_factor` or similar, potentially gaining code clarity."

 

This is exactly what has been suggested in the Fortran Discourse thread as well.

 

There might be some unclear reasons why the OP wants that optional argument at all costs, but I agree that from what he has provided, that is not actually needed.

0 Kudos
SebastianM
Novice
1,535 Views

That is true. Will keep this in mind. But the described bug still stands.

Thanks for the help.

0 Kudos
IanH
Honored Contributor II
1,601 Views

Compiler bug. The compiler appears to get confused resolving the generic binding to a specific binding.

mic_esp96
New Contributor I
1,556 Views

Very likely it is.

 

This example produces an ICE with both ifort and ifx:

 

 

 

module mo_vec
   type :: vector
      real :: x, y
   contains
      generic :: operator(-) => diff, neg
      procedure :: diff, neg
      generic :: operator(+) => plus
      procedure :: plus
   end type vector
 
   interface vector
      procedure init_vec
   end interface vector
 
   type, extends(vector) :: vector_parameter
   end type vector_parameter
 
   type(vector_parameter), parameter :: vec_x = vector_parameter(1., 0.)
   type(vector_parameter), parameter :: vec_y = vector_parameter(0., 1.)
 
 contains
 
   pure type(vector) function init_vec(x, y, factor)
      real, intent(in) :: x, y
      real, intent(in), optional :: factor
      real :: factor_
      factor_ = 1.0
      if (present(factor)) factor_= factor
      init_vec%x = factor_ * x
      init_vec%y = factor_ * y
   end function init_vec
 
   pure type(vector) function diff(this, that)
      class(vector), intent(in) :: this, that
      diff = vector(x=this%x-that%x, y=this%y-that%y)
   end function diff

   pure type(vector) function plus(this, that)
      class(vector), intent(in) :: this, that
      plus = vector(x=this%x+that%x, y=this%y+that%y)
   end function 

   pure type(vector) function neg(this)
      class(vector), intent(in) :: this
      neg = vector(x=-this%x, y=-this%y)
   end function neg
 end module mo_vec
 
 program vec_test
   use mo_vec
   print *, vec_x + -vec_y
 end program vec_test

 

 

0 Kudos
IanH
Honored Contributor II
1,529 Views

Should just be a syntax error.  There's a compiler extension for adjacent add-operands that may be complicating this case.

0 Kudos
mic_esp96
New Contributor I
1,519 Views

@IanH  what should it be a syntax error ?

0 Kudos
Steve_Lionel
Honored Contributor III
1,423 Views

In: print *, vec_x + -vec_y 

the + may not be followed directly by -.  To be conforming, it should be:

print *, vec_x + (-vec_y)

I discuss this in Doctor Fortran in "Order! Order!" - Doctor Fortran (stevelionel.com)

mic_esp96
New Contributor I
1,405 Views
Oh, I see, my bad.
Thanks for the clarification.
0 Kudos
Ron_Green
Moderator
1,387 Views

I'll get a bug report opened for the ICE.  And along with fixing the ICE, let's get an error message like Gfortran.

0 Kudos
SebastianM
Novice
1,386 Views

Thanks for all your help. To remove the discussion about the interface, here is the boiled down version of the problem with the extended type:

module mo_vec
  type :: typ1
    real :: x
  contains
    procedure :: diff, neg
    generic :: operator(-) => diff, neg
  end type typ1
  ! extended type
  type, extends(typ1) :: typ2
  end type typ2
contains
  pure type(typ1) function diff(this, that)
    class(typ1), intent(in) :: this, that
    diff = typ1(this%x-that%x)
  end function diff
  pure type(typ1) function neg(this)
    class(typ1), intent(in) :: this
    neg = typ1(-this%x)
  end function neg
end module mo_vec

program vec_test
  use mo_vec, only: typ2
  type(typ2) :: var1 = typ2(1.), var2 = typ2(2.)
  print *, -var2     ! error #5633: **Internal compiler error
  print *, var1-var2 ! error #6355: This binary operation is invalid for this data type.
end program vec_test

Both errors shouldn't come.
Cheers, Sebastian

0 Kudos
FortranFan
Honored Contributor II
1,280 Views

@SebastianM ,

Which version of the Intel oneAPI with IFORT and IFX generates the ICE with your "boiled down" code?

0 Kudos
Ron_Green
Moderator
1,363 Views

Bug ID for the ICE is CMPLRLLVM-48013


0 Kudos
Reply