Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions spec/lang/call/record_method_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,9 @@ describe("record method call", function()

]],
{
{ y = 9, msg = "argument 1: got integer, expected Foo" },
{ y = 11, msg = "argument 1: got integer, expected Foo" },
{ y = 15, msg = "argument 1: got integer, expected Foo" },
{ y = 9, msg = "argument 1: got integer, expected record Foo" },
{ y = 11, msg = "argument 1: got integer, expected record Foo" },
{ y = 15, msg = "argument 1: got integer, expected record Foo" },
}))

it("for function declared in record body with self as different type from receiver", util.check_type_error([[
Expand Down
38 changes: 38 additions & 0 deletions spec/lang/metamethods/call_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,42 @@ describe("metamethod __call", function()
Foo(1)
]]))
end)

describe("method returning self", function()
it("resolves self to the proper type, not the caller, with __call", util.check([[
local record Foo
end

local interface ICallable
metamethod __call: function(self, ...: any): self
end

function Foo:bar(_a: ICallable)
end

local x: ICallable
local foo: Foo = {}

local res = x(1)
foo:bar(res)
]]))

it("resolves self to the proper type, not the caller, with method", util.check([[
local record Foo
end

local record Selfish
me: function(self): self
end

function Foo:bar(_a: Selfish)
end

local x: Selfish
local foo: Foo = {}

local myself = x:me()
foo:bar(myself)
]]))
end)
end)
2 changes: 1 addition & 1 deletion spec/lang/stdlib/pcall_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,6 @@ describe("pcall", function()
print(err)
end
]], {
{ msg = "argument 2: got integer, expected Text" }
{ msg = "argument 2: got integer, expected record Text" }
}))
end)
2 changes: 1 addition & 1 deletion spec/lang/stdlib/xpcall_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ describe("xpcall", function()
local myText: Text = {}
xpcall(myText.print, msgh, 12)
]], {
{ msg = "argument 3: got integer, expected Text" }
{ msg = "argument 3: got integer, expected record Text" }
}))

end)
17 changes: 16 additions & 1 deletion teal/check/context.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1122,9 +1122,16 @@ function Context:resolve_for_call(func, args, is_method)
return self:resolve_for_call(func.def, args, is_method)

elseif func.fields and func.meta_fields and func.meta_fields["__call"] then
local interface_type = func
table.insert(args.tuple, 1, func)
func = func.meta_fields["__call"]
func = self:to_structural(func)

func = types.map(interface_type, func, {
["self"] = function(iface, _typ)
return iface, true
end,
})
is_method = true
end

Expand Down Expand Up @@ -1673,7 +1680,15 @@ function Context:match_record_key(t, rec, key)
assert(t.fields, "record has no fields!?")

if t.fields[key] then
return t.fields[key]
local ft = t.fields[key]
if ft.typename == "function" then
ft = types.map(nil, ft, {
["self"] = function(_, _typ)
return t, true
end,
})
end
return ft
end

local str = a_type(rec, "string", {})
Expand Down
17 changes: 16 additions & 1 deletion teal/check/context.tl
Original file line number Diff line number Diff line change
Expand Up @@ -1122,9 +1122,16 @@ function Context:resolve_for_call(func: Type, args: TupleType, is_method: boolea
return self:resolve_for_call(func.def, args, is_method)
-- resolve if metatable
elseif func is RecordLikeType and func.meta_fields and func.meta_fields["__call"] then
local interface_type = func -- save the interface/record type for self resolution
table.insert(args.tuple, 1, func)
func = func.meta_fields["__call"]
func = self:to_structural(func)
-- resolve 'self' in return type to the interface type, not the caller
func = types.map(interface_type, func, {
["self"] = function(iface: RecordLikeType, _typ: SelfType): Type, boolean
return iface, true
end,
})
is_method = true
end

Expand Down Expand Up @@ -1673,7 +1680,15 @@ function Context:match_record_key(t: Type, rec: Node, key: string): Type, string
assert(t.fields, "record has no fields!?")

if t.fields[key] then
return t.fields[key]
local ft = t.fields[key]
if ft is FunctionType then
ft = types.map(nil, ft, {
["self"] = function(_: nil, _typ: SelfType): Type, boolean
return t, true
end,
})
end
return ft
end

local str = a_type(rec, "string", {})
Expand Down
2 changes: 1 addition & 1 deletion teal/precompiled/default_env.lua

Large diffs are not rendered by default.

19 changes: 17 additions & 2 deletions tl.lua

Large diffs are not rendered by default.

Loading