Skip to content

add static type checking for function call#1

Open
ishwar00 wants to merge 15 commits intoSamyak2:masterfrom
ishwar00:master
Open

add static type checking for function call#1
ishwar00 wants to merge 15 commits intoSamyak2:masterfrom
ishwar00:master

Conversation

@ishwar00
Copy link
Copy Markdown

To get the type of arguments, i have used pretty much same approach as you used for type inference. I have added space in the beginning of some comments, just because LSP was yelling at me to add. Error messages need some work.

Comment thread syntree.py Outdated
@ishwar00
Copy link
Copy Markdown
Author

I also made an attemp to add marker in error message for each argument that did not match declared type. It had limitations, because i believe there is not enough info to locate(line_number, column_number) arguments in current AST. Currently error message marks callee name.

@Samyak2
Copy link
Copy Markdown
Owner

Samyak2 commented Oct 23, 2022

I have tried printing the the declared function's parameter along with the error in 852e1fa. But it looks like it's pointing to the wrong parameter at times.

For a simple example like this:

package main

func a(b int, c float64) {
	return b + c
}

func main() {
	a(10, "string")
}

It does work:

TYPE ERROR: Arguments Type Mismatch Declaration
"string" has type string
         8:      a(10, "string")
                 ^
but function wanted float64
         3:     func a(b int, c float64) {
                              ^

But seems to be incorrect for the test cases you have added.

Comment thread syntree.py Outdated
@Samyak2
Copy link
Copy Markdown
Owner

Samyak2 commented Oct 24, 2022

For arguments though, we'll also need to keep track of lineno and colno of literals, like we do for identifiers.

@Samyak2
Copy link
Copy Markdown
Owner

Samyak2 commented Oct 24, 2022

That said, this is really nice already. I don't mind merging it at this stage.

@ishwar00
Copy link
Copy Markdown
Author

package main

type integer int
type anotherInteger integer

func main() {
}

try running above code, you will get

SYNTAX ERROR:
at line 4, column 21
         4:     type anotherInteger integer
                                    ^^^

it's because of this rule in grammer. when it should be this. This rule only parses certain type names.

@ishwar00
Copy link
Copy Markdown
Author

For arguments though, we'll also need to keep track of lineno and colno of literals, like we do for identifiers.

yes!, i have that in mind. I'll do it, once type declarations are fixed.

@ishwar00
Copy link
Copy Markdown
Author

That said, this is really nice already. I don't mind merging it at this stage.

Good to hear that!, but still static type checking gets wrong for new type declarations.

@ishwar00
Copy link
Copy Markdown
Author

I have been thinking about resolving that syntax error.

A variable declaration creates one or more variables, binds corresponding identifiers to them, and gives each a type and an initial value.

A variable is a storage location for holding a value. The set of permissible values is determined by the variable's type.

A type declaration binds an identifier, the type name, to a type

one represents type name other represents a set of values, in a certain scope. So i think we should add types in SymbolTable instead of TypeTable. But this might look mingling of two identifiers representing different things. But since we cannot declare a identifier twice in the same block, it should not be a problem. So a identifier in the given scope either represents type name or value.

Let me know what you think.

@Samyak2
Copy link
Copy Markdown
Owner

Samyak2 commented Oct 30, 2022

Makes sense to me. Although, I wonder how errors will look like when you pass a type name instead of a value - we can get to that once type names are working.

Also, I see this comment under the grammar rule you linked:

TODO: QualifiedIdent here gives R/R conflict

I'm guessing this wasn't implemented because of time constraints. Fixing conflicts in a grammar can be annoying xD

@ishwar00
Copy link
Copy Markdown
Author

Actually i did not understand why we would get reduce/reduce conflict. But when i changed
TypeName : BasicType to TypeName : IDENTIFIER i did get one shift/reduce conflict though, i think this

 """ParameterDecl : Type
    | ELLIPSIS Type
    | IdentifierList Type
    | IdentifierList ELLIPSIS Type
 """

rule is the cause. Type and IdentifierList are producing shift/reduce conflict. But i think it's not a problem, since parser prefers to shift, it naturally solves it.

If we remove Type from above rule, parser doesn't warn about any conflict. Implying Type and IdentifierList are conflicting(i think).

 """ParameterDecl : ELLIPSIS Type
    | IdentifierList Type
    | IdentifierList ELLIPSIS Type
 """

P.S: After looking at comments it seems that, decision to store types in TypeTable was made at the last moment, to simply things a bit, i assume it was time contraint.

@ishwar00
Copy link
Copy Markdown
Author

ishwar00 commented Nov 5, 2022

please ignore this.
I was busy with other stuff, mostly i will be free to work on this PR now onwards.

parser warns about reduce/reduce conflicts, after changing production TypeName : BasicType to TypeName : IDENTIFIER.

WARNING: 2 reduce/reduce conflicts
WARNING: reduce/reduce conflict in state 116 resolved using rule (OperandName -> IDENTIFIER)
WARNING: rejected rule (TypeName -> IDENTIFIER) in state 116
WARNING: reduce/reduce conflict in state 217 resolved using rule (OperandName -> IDENTIFIER)
WARNING: rejected rule (TypeName -> IDENTIFIER) in state 217

the above conflicting productions are TypeName and OperandName :

""" TypeName : IDENTIFIER
"""

""" OperandName : IDENTIFIER %prec '='
                | QualifiedIdent
"""

So where are the conflicts? they must be right below between OperandName and Literal, because OperandName occurs only in Operand production.

"""Operand : OperandName   // could be reduced to IDENTIFIER
           | Literal
           | '(' Expression ')'
"""

"""Literal : BasicLit       // ignore
           | FunctionLit    // ignore
           | CompositeLit         
"""

"""CompositeLit : LiteralType LiteralValue
"""

"""LiteralType : ArrayType
               | '[' '.' '.' '.' ']' ElementType
               | TypeName     // could be reduced to IDENTIFIER
               | SliceType
"""

"""LiteralValue : '{' ElementList '}'
"""

I don't quite get it, how is this a reduce/reduce conflict. because CompositeLit cannot be reduced to an IDENTIFIER, can it?

@ishwar00
Copy link
Copy Markdown
Author

ishwar00 commented Nov 6, 2022

Hey, could you please explain me, why update_info was called here?

# just for reference
class Function(Node):
    """Node to store function declaration"""

    def __init__(self, name, signature, lineno: int, body=None):
        super().__init__("FUNCTION",
                         children=[signature, body],
                         data=(name, lineno))
        self.data: tuple

        self.fn_name = name
        self.lineno = lineno
        self.signature = signature
        self.body = body

        if name is not None:
            symtab.update_info(name[1],
                               lineno,
                               0,
                               type_="FUNCTION",
                               const=True,
                               value=self)

@Samyak2
Copy link
Copy Markdown
Owner

Samyak2 commented Nov 6, 2022

I think it's because of this:

https://github.com/ishwar00/gopy/blob/7e6fd1e4f7477f43fa2f547054499ad5035dc364/go_parser.py#L165-L167

The function is added to the symbol table as soon as we see a function name. Anything after that - parameters, return type, body, etc. - is declared in that function's scope.

Edit: so when the function declaration is completely parsed, it is updated in the symtab.

@ishwar00
Copy link
Copy Markdown
Author

ishwar00 commented Nov 6, 2022

I think it's because of this:

https://github.com/ishwar00/gopy/blob/7e6fd1e4f7477f43fa2f547054499ad5035dc364/go_parser.py#L165-L167

The function is added to the symbol table as soon as we see a function name. Anything after that - parameters, return type, body, etc. - is declared in that function's scope.

Edit: so when the function declaration is completely parsed, it is updated in the symtab.

Oh, i see. I did not trace FunctionDecl production at all, my bad.

About the reduce/reduce conflicts, as of now we can get away with it, by removing TypeName from LiteralType, and we don't see them as long as we don't support StructType.

"""LiteralType : ArrayType
               | '[' '.' '.' '.' ']' ElementType
               | SliceType
"""

But getting away with it like this, won't be so satifying.

@ishwar00
Copy link
Copy Markdown
Author

ishwar00 commented Nov 9, 2022

Things are still broken, still fixing, but things are changed enough for suggestions.

@ishwar00
Copy link
Copy Markdown
Author

It's almost done, except shift/reduce conflict.

@ishwar00 ishwar00 marked this pull request as ready for review November 14, 2022 08:29
@ishwar00
Copy link
Copy Markdown
Author

Just to update: I believe it is not yet ready to merge, it needs few more changes, especially code generation part, i am not confident enough. Also static type checking gets wrong for recursive calls, since signature gets updated after parsing body of a function.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants