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

__index cannot return a method #692

Closed
sloonz opened this issue Sep 4, 2023 · 1 comment
Closed

__index cannot return a method #692

sloonz opened this issue Sep 4, 2023 · 1 comment
Labels
bug Something isn't working

Comments

@sloonz
Copy link

sloonz commented Sep 4, 2023

Using __index to return a function ("."-style call) works, but returning a method (":"-style call does not)

local record test
    metamethod __index: function(self: test, key: string): function()
end

test.hello() -- No error
test:hello() -- invalid key 'hello' in record 'test' of type test

I’m not sure why the two are handled differently here and here. If I shunt the special : handling by adding or node.op.op == ":" in the . code (as shown bellow), this problem disappears, and the test suite still passes.

diff --git a/tl.tl b/tl.tl
index 54082ee..8c6eea0 100644
--- a/tl.tl
+++ b/tl.tl
@@ -9870,7 +9870,7 @@ tl.type_check = function(ast: Node, opts: TypeCheckOptions): Result, string
                   end
                end
                node.type = type_check_funcall(node, a, b)
-            elseif node.op.op == "." then
+            elseif node.op.op == "." or node.op.op == ":" then
                assert(node.e2.kind == "identifier")
                local bnode: Node = {
                   y = node.e2.y,
@@ -9912,20 +9912,6 @@ tl.type_check = function(ast: Node, opts: TypeCheckOptions): Result, string
                   node_error(node, "can only use 'is' on variables")
                end
                node.type = BOOLEAN
-            elseif node.op.op == ":" then
-               if lax and (is_unknown(a) or a.typename == "typevar") then
-                  if node.e1.kind == "variable" then
-                     add_unknown_dot(node.e1, node.e1.tk .. "." .. node.e2.tk)
-                  end
-                  node.type = UNKNOWN
-               else
-                  local t, e = match_record_key(a, node.e1, node.e2.conststr or node.e2.tk)
-                  if not t then
-                     node.type = INVALID
-                     return node_error(node.e2, e, a == INVALID and a or resolve_tuple(orig_a))
-                  end
-                  node.type = t
-               end
             elseif node.op.op == "not" then
                node.known = facts_not(node, node.e1.known)
                node.type = BOOLEAN
@hishamhm
Copy link
Member

Hi, thanks for the report! It took me a bit of digging the back history to try to remember why . and : have separate handlers.

Of course, in Lua, : is just syntactic sugar. In Teal, however, we have in the language semantics separate concepts for functions and record-functions/methods, because the latter are statically bound to their records.

For example, want to be able to detect that the programmer made a typo and a method name is invalid, and that means that we need, at one point of the code, to declare that the declarations of methods are done. So you can only do function my_record:mymethod in the same scope where you created your record, for instance.

Internally, Teal does keep track if a function "is a method" (that is, if it was declared with : or .; in more recent versions, we even check if a function is method-like by having a first argument self with its own type). This is to keep the door open for future enhancements to the type system, such as interfaces (see this comment: #97 (comment)).

So, it's an explicit decision to not make : just a syntactic sugar for ., like Lua.

Having said that, the problem you hit is that the current implementation of : does not support __index. That's solvable; I'll look into it! Thanks for reporting!

@hishamhm hishamhm added the bug Something isn't working label Sep 13, 2023
hishamhm added a commit that referenced this issue Dec 18, 2023
From: #692

> It took me a bit of digging the back history to try to remember why `.` and
> `:` have separate handlers. Of course, in Lua, `:` is just syntactic sugar. In
> Teal, however, we have in the language semantics separate concepts for
> functions and record-functions/methods, because the latter are statically
> bound to their records.
>
> For example, want to be able to detect that the programmer made a typo and a
> method name is invalid, and that means that we need, at one point of the code,
> to declare that the declarations of methods are done. So you can only do
> `function my_record:mymethod` in the same scope where you created your record,
> for instance.
>
> Internally, Teal does keep track if a function "is a method" (that is, if it
> was declared with `:` or `.`; in more recent versions, we even check if a
> function is method-like by having a first argument `self` with its own type).
> This is to keep the door open for future enhancements to the type system, such
> as interfaces (see this comment:
> #97 (comment)).
>
> So, it's an explicit decision to not make `:` just a syntactic sugar for `.`,
> like Lua.
hishamhm added a commit that referenced this issue Jan 6, 2024
From: #692

> It took me a bit of digging the back history to try to remember why `.` and
> `:` have separate handlers. Of course, in Lua, `:` is just syntactic sugar. In
> Teal, however, we have in the language semantics separate concepts for
> functions and record-functions/methods, because the latter are statically
> bound to their records.
>
> For example, want to be able to detect that the programmer made a typo and a
> method name is invalid, and that means that we need, at one point of the code,
> to declare that the declarations of methods are done. So you can only do
> `function my_record:mymethod` in the same scope where you created your record,
> for instance.
>
> Internally, Teal does keep track if a function "is a method" (that is, if it
> was declared with `:` or `.`; in more recent versions, we even check if a
> function is method-like by having a first argument `self` with its own type).
> This is to keep the door open for future enhancements to the type system, such
> as interfaces (see this comment:
> #97 (comment)).
>
> So, it's an explicit decision to not make `:` just a syntactic sugar for `.`,
> like Lua.
hishamhm added a commit that referenced this issue May 10, 2024
From: #692

> It took me a bit of digging the back history to try to remember why `.` and
> `:` have separate handlers. Of course, in Lua, `:` is just syntactic sugar. In
> Teal, however, we have in the language semantics separate concepts for
> functions and record-functions/methods, because the latter are statically
> bound to their records.
>
> For example, want to be able to detect that the programmer made a typo and a
> method name is invalid, and that means that we need, at one point of the code,
> to declare that the declarations of methods are done. So you can only do
> `function my_record:mymethod` in the same scope where you created your record,
> for instance.
>
> Internally, Teal does keep track if a function "is a method" (that is, if it
> was declared with `:` or `.`; in more recent versions, we even check if a
> function is method-like by having a first argument `self` with its own type).
> This is to keep the door open for future enhancements to the type system, such
> as interfaces (see this comment:
> #97 (comment)).
>
> So, it's an explicit decision to not make `:` just a syntactic sugar for `.`,
> like Lua.
hishamhm added a commit that referenced this issue Jul 23, 2024
From: #692

> It took me a bit of digging the back history to try to remember why `.` and
> `:` have separate handlers. Of course, in Lua, `:` is just syntactic sugar. In
> Teal, however, we have in the language semantics separate concepts for
> functions and record-functions/methods, because the latter are statically
> bound to their records.
>
> For example, want to be able to detect that the programmer made a typo and a
> method name is invalid, and that means that we need, at one point of the code,
> to declare that the declarations of methods are done. So you can only do
> `function my_record:mymethod` in the same scope where you created your record,
> for instance.
>
> Internally, Teal does keep track if a function "is a method" (that is, if it
> was declared with `:` or `.`; in more recent versions, we even check if a
> function is method-like by having a first argument `self` with its own type).
> This is to keep the door open for future enhancements to the type system, such
> as interfaces (see this comment:
> #97 (comment)).
>
> So, it's an explicit decision to not make `:` just a syntactic sugar for `.`,
> like Lua.
hishamhm added a commit that referenced this issue Aug 2, 2024
From: #692

> It took me a bit of digging the back history to try to remember why `.` and
> `:` have separate handlers. Of course, in Lua, `:` is just syntactic sugar. In
> Teal, however, we have in the language semantics separate concepts for
> functions and record-functions/methods, because the latter are statically
> bound to their records.
>
> For example, want to be able to detect that the programmer made a typo and a
> method name is invalid, and that means that we need, at one point of the code,
> to declare that the declarations of methods are done. So you can only do
> `function my_record:mymethod` in the same scope where you created your record,
> for instance.
>
> Internally, Teal does keep track if a function "is a method" (that is, if it
> was declared with `:` or `.`; in more recent versions, we even check if a
> function is method-like by having a first argument `self` with its own type).
> This is to keep the door open for future enhancements to the type system, such
> as interfaces (see this comment:
> #97 (comment)).
>
> So, it's an explicit decision to not make `:` just a syntactic sugar for `.`,
> like Lua.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants