Skip to content

Commit

Permalink
RA22-015: Fix call decl/type resolution
Browse files Browse the repository at this point in the history
This fixes several bugs in the call decl/type resolution mechanism

* Generally make `called_decl` only return a called decl when there is
  one for the specific callexpr. Before, for nested call expressions
  like ̀ a () () ()` it would return the sub call-expression called decl.

* Make `expr_context_free_type` compute the type even when there is no
  called declaration
  • Loading branch information
raph-amiard committed Sep 30, 2021
1 parent 39925cd commit da65c32
Show file tree
Hide file tree
Showing 6 changed files with 275 additions and 11 deletions.
69 changes: 64 additions & 5 deletions contrib/lkt/language/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1932,11 +1932,22 @@ class FunDecl(UserValDecl):

call_scope = Property(Entity.children_env)

@langkit_property()
def is_property():
"""
Returns whether this function decl defines a property or not.
A property is a function that has the @property annotation and has no
parameters. As a result of this annotation, it will be callable without
parens.
"""
return Entity.full_decl.has_annotation('property')

@langkit_property()
def get_type(no_inference=(T.Bool, False)):
ignore(no_inference)
return If(
Entity.full_decl.has_annotation('property'),
Entity.is_property,

Entity.return_type.designated_type,

Expand Down Expand Up @@ -2509,20 +2520,50 @@ def called_object_type():
@langkit_property(public=True)
def called_decl():
"""
Return the declaration that is called by this call expression.
Return the declaration that is called by this call expression, if there
is one that can be statically determined.
"""
refd_decl = Var(Entity.name.referenced_decl.then(
lambda sr: sr.result_ref

# We use this to resolve formals rather than just the type of the
# ``name`` of the callexpr, for two language features:
#
# * Generic functions: We need to statically resolve them in order to
# allow inference in the implicit instantiation.
#
# * Named parameters: Since function types don't contain parameter
# names, if we want to allow named parameters we need to statically
# resolve the target of the call when possible.

refd_decl = Var(If(

# If the name of this callexpr is a callexpr itself, there cannot
# be a statically known referenced decl, since whatever is
# referenced is the result.
Not(Entity.name.is_a(CallExpr)),

Entity.name.referenced_decl.then(
lambda sr: sr.result_ref
).then(lambda decl: If(
decl.cast(T.FunDecl)._.is_property, No(T.Decl.entity), decl
)),

No(T.Decl.entity)
))

# Implement special resolution for calls to objects via __call__
# Implement special resolution for calls to objects via __call__.
# NOTE: We do this resolution here because in case where the object is
# statically resolvable, the __call__ method might have keyword
# arguments, see comment above.
called_decl = Var(
refd_decl.then(lambda refd_decl: refd_decl.match(

# Don't try to look for __call__ on function decls
lambda _=FunDecl: refd_decl,

lambda v=BaseValDecl:
v._.get_type._.get_fun('__call__')
.then(lambda a: a, default_val=refd_decl),

lambda _: refd_decl,
))
)
Expand All @@ -2543,12 +2584,26 @@ def called_decl():
@langkit_property()
def expr_context_free_type():
return Entity.called_decl.then(
# First case, we have a statically resolvable called decl: Use it
# to determine the context free type of this expression.
# TODO: it's not clear we actually need this branch: we could
# determine the type from the type of name in all cases?
lambda rd: rd.match(
lambda fd=T.FunDecl: fd.return_type.designated_type,
lambda td=T.TypeDecl: td,
lambda _:
PropertyError(T.TypeDecl.entity, "should not happen"),
)
)._or(
# Second case, we don't have a statically resolvable called decl:
# just rely on the type of the ``name``.
Entity.name.expr_context_free_type.then(
lambda t: t.match(
lambda ft=FunctionType: ft.return_type.as_entity,
lambda t=TypeDecl:
t.get_fun('__call__')._.return_type.designated_type,
)
)
)

@langkit_property(return_type=T.FormalParam.array)
Expand All @@ -2558,6 +2613,7 @@ def formals():
called object.
"""
return Entity.called_decl.then(
# Case where the called decl is known statically
lambda cd: cd.match(
lambda fd=T.FunDecl: fd.args.map(
lambda p: p.cast_or_raise(T.ComponentDecl).to_formal_param,
Expand All @@ -2567,6 +2623,9 @@ def formals():
lambda _:
PropertyError(T.FormalParam.array, "Should not happen")
),

# Case where there is no statically known declaration, so we base
# the resolution of formals on the callable type of the name.
default_val=Entity.called_object_type.match(
lambda ft=T.FunctionType: ft.args.map(
lambda a: FormalParam.new(
Expand Down
19 changes: 19 additions & 0 deletions testsuite/tests/contrib/lkt_semantic/advanced_calls/test.lkt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class MyCallable {
fun __call__(a: Int): Bool
}

class RecursiveCallable {
fun __call__(a: Int): RecursiveCallable
}

class A {
@property
fun pouet(): (Int) -> Bool

fun callable_array(): Array[MyCallable]

fun test(): Bool = self.pouet(12)
fun test2(): Bool = MyCallable()(12)
fun test3(): Bool = self.callable_array()(12)(12)
fun test4(): RecursiveCallable = RecursiveCallable()(12)(12)(12)(12)(12)(12)(12)(12)(12)(12)(12)(12)(12)(12)
}
191 changes: 191 additions & 0 deletions testsuite/tests/contrib/lkt_semantic/advanced_calls/test.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
Resolving test.lkt
==================
Id <RefId "Int" test.lkt:2:21-2:24>
references <StructDecl prelude: "Int">

Id <RefId "Bool" test.lkt:2:27-2:31>
references <EnumTypeDecl prelude: "Bool">

Id <RefId "Int" test.lkt:6:21-6:24>
references <StructDecl prelude: "Int">

Id <RefId "RecursiveCallable" test.lkt:6:27-6:44>
references <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Id <RefId "Int" test.lkt:11:19-11:22>
references <StructDecl prelude: "Int">

Id <RefId "Bool" test.lkt:11:27-11:31>
references <EnumTypeDecl prelude: "Bool">

Id <RefId "Array" test.lkt:13:27-13:32>
references <GenericDecl prelude: "Array">

Id <RefId "MyCallable" test.lkt:13:33-13:43>
references <ClassDecl "MyCallable" test.lkt:1:1-3:2>

Id <RefId "Bool" test.lkt:15:17-15:21>
references <EnumTypeDecl prelude: "Bool">

Id <RefId "self" test.lkt:15:24-15:28>
references <SelfDecl "self" test.lkt:9:1-19:2>

Expr <RefId "self" test.lkt:15:24-15:28>
has type <ClassDecl "A" test.lkt:9:1-19:2>

Id <RefId "pouet" test.lkt:15:29-15:34>
references <FunDecl "pouet" test.lkt:11:5-11:31>

Expr <RefId "pouet" test.lkt:15:29-15:34>
has type <FunctionType prelude: "(Int) -> Bool">

Expr <DotExpr test.lkt:15:24-15:34>
has type <FunctionType prelude: "(Int) -> Bool">

Expr <NumLit test.lkt:15:35-15:37>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:15:24-15:38>
has type <EnumTypeDecl prelude: "Bool">

Id <RefId "Bool" test.lkt:16:18-16:22>
references <EnumTypeDecl prelude: "Bool">

Id <RefId "MyCallable" test.lkt:16:25-16:35>
references <ClassDecl "MyCallable" test.lkt:1:1-3:2>

Expr <CallExpr test.lkt:16:25-16:37>
has type <ClassDecl "MyCallable" test.lkt:1:1-3:2>

Expr <NumLit test.lkt:16:38-16:40>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:16:25-16:41>
has type <EnumTypeDecl prelude: "Bool">

Id <RefId "Bool" test.lkt:17:18-17:22>
references <EnumTypeDecl prelude: "Bool">

Id <RefId "self" test.lkt:17:25-17:29>
references <SelfDecl "self" test.lkt:9:1-19:2>

Expr <RefId "self" test.lkt:17:25-17:29>
has type <ClassDecl "A" test.lkt:9:1-19:2>

Id <RefId "callable_array" test.lkt:17:30-17:44>
references <FunDecl "callable_array" test.lkt:13:5-13:44>

Expr <RefId "callable_array" test.lkt:17:30-17:44>
has type <FunctionType prelude: "() -> Array[MyCallable]">

Expr <DotExpr test.lkt:17:25-17:44>
has type <FunctionType prelude: "() -> Array[MyCallable]">

Expr <CallExpr test.lkt:17:25-17:46>
has type <InstantiatedGenericType prelude: "Array[MyCallable]">

Expr <NumLit test.lkt:17:47-17:49>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:17:25-17:50>
has type <ClassDecl "MyCallable" test.lkt:1:1-3:2>

Expr <NumLit test.lkt:17:51-17:53>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:17:25-17:54>
has type <EnumTypeDecl prelude: "Bool">

Id <RefId "RecursiveCallable" test.lkt:18:18-18:35>
references <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Id <RefId "RecursiveCallable" test.lkt:18:38-18:55>
references <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <CallExpr test.lkt:18:38-18:57>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:58-18:60>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:61>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:62-18:64>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:65>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:66-18:68>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:69>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:70-18:72>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:73>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:74-18:76>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:77>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:78-18:80>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:81>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:82-18:84>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:85>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:86-18:88>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:89>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:90-18:92>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:93>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:94-18:96>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:97>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:98-18:100>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:101>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:102-18:104>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:105>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:106-18:108>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:109>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Expr <NumLit test.lkt:18:110-18:112>
has type <StructDecl prelude: "Int">

Expr <CallExpr test.lkt:18:38-18:113>
has type <ClassDecl "RecursiveCallable" test.lkt:5:1-7:2>

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
driver: lkt
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,3 @@ test.lkt:13:66: error: Cannot find entity `self` in this scope
12 | @invalid fun test_astlist_indexing(a: ASTList[String]): String = self(12)
| ^^^^

Expr <CallExpr test.lkt:13:66-13:74>
has type <StructDecl prelude: "String">

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,3 @@ test.lkt:4:26: error: Cannot find entity `plus` in this scope
3 | @invalid val b : Int = a.plus(2)
| ^^^^

Expr <CallExpr test.lkt:4:24-4:33>
has type <StructDecl prelude: "Int">

0 comments on commit da65c32

Please sign in to comment.