- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

Link Copied

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

"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.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

Thanks for the help.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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
```

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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)

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

Thanks for the clarification.

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

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

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content

Bug ID for the ICE is CMPLRLLVM-48013

- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page