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

Generics, wrong sum #1823

Closed
certik opened this issue Jun 15, 2023 · 9 comments
Closed

Generics, wrong sum #1823

certik opened this issue Jun 15, 2023 · 9 comments
Assignees
Labels
generics Fortran 202Y Generics

Comments

@certik
Copy link
Contributor

certik commented Jun 15, 2023

This:

module template_add_m
    implicit none
    private
    public :: add_t

    requirement R(T)
        type :: T; end type
    end requirement

    template add_t(T)
        requires R(T)
        private
        public :: add_generic
    contains
        function add_generic(x, y, z) result(s)
            type(T), intent(in) :: x, y, z
            type(T) :: s
            print*, "x, y, z, x+y+z =", x, y, z, x+y+z
            s = x + y + z
            print *, s
        end function
    end template

        function add_integer2(x, y, z) result(s)
            integer, intent(in) :: x, y, z
            integer :: s
            print*, "x, y, z, x+y+z =", x, y, z, x+y+z
            s = x + y + z
            print *, s
        end function

contains


    subroutine test_template()
        real :: a
        integer :: n

        instantiate add_t(real), only: add_real => add_generic
        a = add_real(5.1, 7.2, 10.0)
        print*, "The result is ", a

        instantiate add_t(integer), only: add_integer => add_generic
        n = add_integer(5, 9, 10)
        s = add_integer2(5, 9, 10)
        print*, "The result is ", n, s
    end subroutine
end module

program template_add
use template_add_m
implicit none

call test_template()

end program template_add

Gives:

x, y, z, x+y+z = 5.09999990e+00 7.19999981e+00 1.00000000e+01 1.00000000e+01
1.00000000e+01
The result is  1.00000000e+01
x, y, z, x+y+z = 5 9 10 10
10
x, y, z, x+y+z = 5 9 10 24
24
The result is  10 2.40000000e+01
@Shaikh-Ubaid
Copy link
Member

The code shared above in the description of this issue does not seem to compile for me with LFortran.

$ cat examples/expr2.f90 
module template_add_m
    implicit none
    private
    public :: add_t

    requirement R(T)
        type :: T; end type
    end requirement

    template add_t(T)
        requires R(T)
        private
        public :: add_generic
    contains
        function add_generic(x, y, z) result(s)
            type(T), intent(in) :: x, y, z
            type(T) :: s
            print*, "x, y, z, x+y+z =", x, y, z, x+y+z
            s = x + y + z
            print *, s
        end function
    end template

        function add_integer2(x, y, z) result(s)
            integer, intent(in) :: x, y, z
            integer :: s
            print*, "x, y, z, x+y+z =", x, y, z, x+y+z
            s = x + y + z
            print *, s
        end function

contains


    subroutine test_template()
        real :: a
        integer :: n

        instantiate add_t(real), only: add_real => add_generic
        a = add_real(5.1, 7.2, 10.0)
        print*, "The result is ", a

        instantiate add_t(integer), only: add_integer => add_generic
        n = add_integer(5, 9, 10)
        s = add_integer2(5, 9, 10)
        print*, "The result is ", n, s
    end subroutine
end module

program template_add
use template_add_m
implicit none

call test_template()

end program template_add
$ lfortran examples/expr2.f90 
syntax error: Token 'function' is unexpected here
  --> examples/expr2.f90:24:9
   |
24 |         function add_integer2(x, y, z) result(s)
   |         ^^^^^^^^ 


Note: if any of the above error or warning messages are not clear or are lacking
context please report it to us (we consider that a bug that must be fixed).

@Thirumalai-Shaktivel
Copy link
Member

Thirumalai-Shaktivel commented Jun 15, 2023

Here is the code that works:

module template_add_m
    implicit none
    private
    public :: add_t

    requirement R(T)
        type :: T; end type
    end requirement

    template add_t(T)
        requires R(T)
        private
        public :: add_generic
    contains
        function add_generic(x, y, z) result(s)
            type(T), intent(in) :: x, y, z
            type(T) :: s
            print*, "x, y, z, x+y+z =", x, y, z, x+y+z
            s = x + y + z
            print *, s
        end function
    end template


contains

    function add_integer2(x, y, z) result(s)
        integer, intent(in) :: x, y, z
        integer :: s
        print*, "x, y, z, x+y+z =", x, y, z, x+y+z
        s = x + y + z
        print *, s
    end function

    subroutine test_template()
        real :: a
        integer :: n, s

        instantiate add_t(real), only: add_real => add_generic
        a = add_real(5.1, 7.2, 10.0)
        print*, "The result is ", a

        instantiate add_t(integer), only: add_integer => add_generic
        n = add_integer(5, 9, 10)
        s = add_integer2(5, 9, 10)
        print*, "The result is ", n, s
    end subroutine
end module

program template_add
use template_add_m
implicit none

call test_template()

end program template_add
$ lfortran examples/expr2.f90
x, y, z, x+y+z = 5.09999990e+00 7.19999981e+00 1.00000000e+01 1.00000000e+01
1.00000000e+01
The result is  1.00000000e+01
x, y, z, x+y+z = 5 9 10 10
10
x, y, z, x+y+z = 5 9 10 24
24
The result is  10 24

@Shaikh-Ubaid
Copy link
Member

Yup, I figured that. The s in test_template() seems to be declared as real then it matches the output of ondrej.

module template_add_m
    implicit none
    private
    public :: add_t

    requirement R(T)
        type :: T; end type
    end requirement

    template add_t(T)
        requires R(T)
        private
        public :: add_generic
    contains
        function add_generic(x, y, z) result(s)
            type(T), intent(in) :: x, y, z
            type(T) :: s
            print*, "x, y, z, x+y+z =", x, y, z, x+y+z
            s = x + y + z
            print *, s
        end function
    end template

        

contains

    function add_integer2(x, y, z) result(s)
        integer, intent(in) :: x, y, z
        integer :: s
        print*, "x, y, z, x+y+z =", x, y, z, x+y+z
        s = x + y + z
        print *, s
    end function

    subroutine test_template()
        real :: a
        integer :: n
        real :: s

        instantiate add_t(real), only: add_real => add_generic
        a = add_real(5.1, 7.2, 10.0)
        print*, "The result is ", a

        instantiate add_t(integer), only: add_integer => add_generic
        n = add_integer(5, 9, 10)
        s = add_integer2(5, 9, 10)
        print*, "The result is ", n, s
    end subroutine
end module

program template_add
use template_add_m
implicit none

call test_template()

end program template_add

@certik certik added the generics Fortran 202Y Generics label Jun 15, 2023
@certik
Copy link
Contributor Author

certik commented Jun 15, 2023

@ansharlubis do you have time to investigate what is going on here?

@ansharlubis
Copy link
Contributor

@certik I've resolved this in #1831. I removed TemplateBinOp in a previous PR, because I thought we won't have binary operators in template functions considering that an operator may not be defined for a given type.

@certik
Copy link
Contributor Author

certik commented Jun 17, 2023

@ansharlubis thanks. I think we don't want to have TemplateBinOp, because as you said, it might not be defined for all types. But I think we want to support "+". I think AST->ASR can verify that "+" is supported by the template type, and if so, implicitly create the proper function "plus" to represent "+".

@ansharlubis
Copy link
Contributor

@certik let me consider it step-by-step. If we encounter a+b in a template function, the compiler triggers the creation of a function plus so that a+b becomes plus(a,b). However, this plus function eventually would have a binary operation a+b inside of it, which still has to be categorized as some form of BinOp.

In the end something like TemplateBinOp is needed so that we know during function instantiation to replace the type in this BinOp. Unless, maybe we abstract all of BinOp into something like BinOp(expr left, expr right, op binop, type type)?

@certik
Copy link
Contributor Author

certik commented Jun 20, 2023

I see. Well, here are the operations to consider: BitNot, UnaryMinus, Compare, BinOp, Not, Len, Item, Section, Concat, Ord, Chr, etc. I think all the operations in expr must now be representable using templates.

We used to have abstract BinOp, but we found it much easier to separate into individual types. Say string concat operation:

z = x // y

If x and y are templates, how should this be represented?

@ansharlubis
Copy link
Contributor

Handled this in #1831.

ansharlubis added a commit that referenced this issue Jul 21, 2023
* Restored TemplateBinOp

* Updated tests

* Renamed tests

* Forgot to update reference tests

* Got the interface working in requirement

* Converted binop in template into the corresponding functions during template construction, add various checks for requirement's parameters

* Removed TemplateBinOp

* Modified the template example with + operator

---------

Co-authored-by: Ondřej Čertík <ondrej@certik.us>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
generics Fortran 202Y Generics
Projects
None yet
Development

No branches or pull requests

4 participants