Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flang] Incorrect diagnostic on user defined operator of two different types using the same module procedure #124766

Closed
DanielCChen opened this issue Jan 28, 2025 · 6 comments
Labels
flang:frontend question A question, not bug report. Check out https://llvm.org/docs/GettingInvolved.html instead!

Comments

@DanielCChen
Copy link
Contributor

Consider the following code:

module m

   type base
      integer :: i = -999
      contains
         procedure, pass(b) :: g => int_base1_base
         generic :: operator(+) => g
   end type

   type base1
      integer :: j  = -999
      contains
         procedure, pass :: f => int_base1_base
         generic :: operator(+) => f
   end type

   contains

   integer function int_base1_base ( a, b )
      class(base1), intent(in) :: a
      class(base), intent(in) :: b
   end function

end module

Flang currently issues an error as:

error: Semantic errors in t.f
./t.f:7:21: error: Generic 'OPERATOR(+)' may not have specific procedures 'base%g' and 'base1%f' as their interfaces are not distinguishable
           generic :: operator(+) => g
                      ^^^^^^^^^^^
./t.f:19:21: Procedure 'g' of type 'base' is bound to 'int_base1_base'
     integer function int_base1_base ( a, b )
                      ^^^^^^^^^^^^^^
./t.f:19:21: Procedure 'f' of type 'base1' is bound to 'int_base1_base'
     integer function int_base1_base

The code seems conforming to me. There doesn't seem any ambiguity when the user defined (+) is executed.
Both ifort and XLF compiled it successfully.

@llvmbot
Copy link
Member

llvmbot commented Jan 28, 2025

@llvm/issue-subscribers-flang-frontend

Author: Daniel Chen (DanielCChen)

Consider the following code: ``` module m

type base
integer :: i = -999
contains
procedure, pass(b) :: g => int_base1_base
generic :: operator(+) => g
end type

type base1
integer :: j = -999
contains
procedure, pass :: f => int_base1_base
generic :: operator(+) => f
end type

contains

integer function int_base1_base ( a, b )
class(base1), intent(in) :: a
class(base), intent(in) :: b
end function

end module


Flang currently issues an error as:

error: Semantic errors in t.f
./t.f:7:21: error: Generic 'OPERATOR(+)' may not have specific procedures 'base%g' and 'base1%f' as their interfaces are not distinguishable
generic :: operator(+) => g
^^^^^^^^^^^
./t.f:19:21: Procedure 'g' of type 'base' is bound to 'int_base1_base'
integer function int_base1_base ( a, b )
^^^^^^^^^^^^^^
./t.f:19:21: Procedure 'f' of type 'base1' is bound to 'int_base1_base'
integer function int_base1_base


The code seems conforming to me. There doesn't seem any ambiguity when the user defined (+) is executed.  
Both ifort and XLF compiled it successfully. 
</details>

@DanielCChen DanielCChen changed the title [Flang] Incorrect diagnostic on user defined operator of two different types uses the same module procedure [Flang] Incorrect diagnostic on user defined operator of two different types using the same module procedure Jan 28, 2025
@klausler
Copy link
Contributor

It would open the door for ambiguity when the binding g is overridden by an extension type.

module m2
  use m
  type, extends(base) :: t2
   contains
    procedure, pass(b) :: g => override
  end type
 contains
  integer function override(a,b)
    class(base1), intent(in) :: a
    class(t2), intent(in) :: b
  end
  subroutine test(a,b)
    class(base1), intent(in) :: a
    class(base), intent(in) :: b
    print *, a + b ! does this call int_base1_base or override?
  end
end

@DanielCChen
Copy link
Contributor Author

It would open the door for ambiguity when the binding g is overridden by an extension type.

module m2
  use m
  type, extends(base) :: t2
   contains
    procedure, pass(b) :: g => override
  end type
 contains
  integer function override(a,b)
    class(base1), intent(in) :: a
    class(t2), intent(in) :: b
  end
  subroutine test(a,b)
    class(base1), intent(in) :: a
    class(base), intent(in) :: b
    print *, a + b ! does this call int_base1_base or override?
  end
end

I think if the dynamic type of b is of t2, override is called. If it is of type base, int_base1_base is called.
The overriding type-bound procedure is depending on the dynamic type of its arguments.

@klausler
Copy link
Contributor

It would open the door for ambiguity when the binding g is overridden by an extension type.

module m2
  use m
  type, extends(base) :: t2
   contains
    procedure, pass(b) :: g => override
  end type
 contains
  integer function override(a,b)
    class(base1), intent(in) :: a
    class(t2), intent(in) :: b
  end
  subroutine test(a,b)
    class(base1), intent(in) :: a
    class(base), intent(in) :: b
    print *, a + b ! does this call int_base1_base or override?
  end
end

I think if the dynamic type of b is of t2, override is called. If it is of type base, int_base1_base is called. The overriding type-bound procedure is depending on the dynamic type of its arguments.

But the binding f of type base1 would also be a match. So the resolution is ambiguous if the error is not caught in the definition of type base1.

@DanielCChen
Copy link
Contributor Author

DanielCChen commented Jan 28, 2025

Right, although it would resolve to the same procedure anyway. But I see your point. Let me try to clarify it because I don't see any wording in the standard disallow it.

@DanielCChen
Copy link
Contributor Author

It is indeed invalid code as confirmed with the standard committee.

It is all very simple and consistent: “named generic: set of named procedures” and “type-bound generic: set of type-bound procedures”. In the type-bound procedure case, it does not matter what the implementing procedures are, it is the type-bound procedures that must satisfy the generic rules. That is because the type-bound procedures can be overridden individually, so generic resolution absolutely needs non-ambiguous resolution: whether one of the types uses the same actual procedure to implement two type-bound procedures is simply irrelevant and unhelpful to consider.
 
When the generic type-bound procedure is an operator, the resulting generic set will consist of all procedures, type-bound or otherwise, with that operator symbol in a scope. The ambiguity rules in 15.4.3.4.5 work equally on pairs of named procedures, pairs of type-bound procedures, and pairs consisting of one named procedure and one type-bound procedure.

@DanielCChen DanielCChen added the question A question, not bug report. Check out https://llvm.org/docs/GettingInvolved.html instead! label Jan 29, 2025
@EugeneZelenko EugeneZelenko marked this as a duplicate of #124772 Jan 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:frontend question A question, not bug report. Check out https://llvm.org/docs/GettingInvolved.html instead!
Projects
None yet
Development

No branches or pull requests

3 participants