lcompilers / lpython

Python compiler
https://lpython.org/
Other
1.37k stars 156 forks source link

Handle class constructor #2730

Open Thirumalai-Shaktivel opened 2 weeks ago

Thirumalai-Shaktivel commented 2 weeks ago
from lpython import i32

class test:
    y: i32 = 2
    def __init__(self, x: i32 = 1):
        self.x: i32 = x

t1: test = test()
t2: test = test(3)
print (t1.x)
print (t1.y)
print (t2.x)
print (t2.y)

I think we can skip the __init__() It has to be represented as a StructTypeConstructor, the class_constructor will take care of the rest.

Thirumalai-Shaktivel commented 2 weeks ago

Assign: @tanay-man

Thirumalai-Shaktivel commented 2 weeks ago

Let's do this.

from lpython import i32, TypeVar

test = TypeVar("test")
class test:
    x: i32 = 0
    y: i32 = 0
    def __init__(self: test, x: i32, y: i32):
        self.x: i32 = x
        self.y: i32 = y

# StrucTypeConstrutor(3, 3)

t2: test = test(3, 3)
Thirumalai-Shaktivel commented 2 weeks ago

Another example:

from lpython import i32, TypeVar

test = TypeVar("test")
class test:
    kind: str = "abc"
    def __init__(self: test, name: str):
        self.name = name

t1: test = test("1")
t2: test = test("2")

print(t1.kind)
print(t2.kind)

t2.kind = "d"
print(t1.kind)
print(t2.kind)

print(t1.name)
print(t2.name)
certik commented 1 week ago

Instead of:

from lpython import i32, TypeVar

test = TypeVar("test")
class test:
    x: i32 = 0
    y: i32 = 0
    def __init__(self: test, x: i32, y: i32):
        self.x: i32 = x
        self.y: i32 = y

# StrucTypeConstrutor(3, 3)

t2: test = test(3, 3)

I recommend doing this:

from lpython import i32, TypeVar

test = TypeVar("test")
class test:
    def __init__(self: test, x: i32, y: i32):
        self.x: i32 = x
        self.y: i32 = y

# StrucTypeConstrutor(3, 3)

t2: test = test(3, 3)

The first are class variables, the second are instance variables. Let's focus on instance variables first.

tanay-man commented 6 days ago

The TypeVar is causing conflicts in the symbol table as both the Struct and TypeVar have the same names.

certik commented 6 days ago

I think we should have just one "test" in the symbol table, which represents the class definition, it will be of type Struct.

And the corresponding StructType will be the type of the first argument self.

tanay-man commented 6 days ago

I agree the problem is since TypeVar has the same name, we cannot add Struct (class definition) to the symbol table.

certik commented 6 days ago

What does LPython currently put into the symbol table for test = TypeVar("test")?

It seems it should just keep note of this variable internally in AST->ASR, but not expose it in ASR. The test variable will be only put into the symbol table once the class test is processed. The test = TypeVar("test") line is just a crutch that we use to make it work with CPython.

tanay-man commented 6 days ago

What does LPython currently put into the symbol table for test = TypeVar("test")?

ASR before adding the Struct node :

(TranslationUnit
    (SymbolTable
        1
        {
            __main__:
                (Module
                    (SymbolTable
                        2
                        {
                            test:
                                (Variable
                                    2
                                    test
                                    []
                                    Local
                                    ()
                                    ()
                                    Default
                                    (TypeParameter
                                        test
                                    )
                                    ()
                                    Source
                                    Public
                                    Required
                                    .false.
                                )
                        })
                    __main__
                    []
                    .false.
                    .false.
                ),
            main_program:
                (Program
                    (SymbolTable
                        5
                        {

                        })
                    main_program
                    []
                    []
                )
        })
    []
)
certik commented 5 days ago

I see, the test = TypeVar("test") is a Variable of type TypeParameter which is used for templates. Then in CPython this test gets shadowed. We could shadow it the same way. But why not use the simpler:

class test:
    def __init__(self: "test", x: i32, y: i32):
        self.x: i32 = x
        self.y: i32 = y
Thirumalai-Shaktivel commented 5 days ago

self would of type: TypeParameter or Class or Struct??

tanay-man commented 5 days ago

Can we treat self as a special argument? Whenever a function is declared inside a class, it can either have no arguments (if it's a static method) or at least one argument. We can assume the first argument to be a pointer to the object, and therefore, of the type of the class. Can we exempt the first argument from the type annotation requirement? We can treat self as this in lfortran.

Thirumalai-Shaktivel commented 5 days ago

@tanay-man is suggesting the following example:

class test:
  def __init__(self, x: i32, y: i32):
    ...