From 181d2a546798ccc10ae3e61383e86e0891921ec5 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Wed, 7 Aug 2024 17:09:14 -0300 Subject: [PATCH] fix: localizing a record does not make the new local a type This allows for a pattern where we can localize a record and load and alternative implementation on top of it. Fixes #759. --- spec/call/record_method_spec.lua | 5 +++-- spec/declaration/local_spec.lua | 13 +++++++++++++ spec/stdlib/require_spec.lua | 27 +++++++++++++++++++++++++++ tl.lua | 6 +++++- tl.tl | 6 +++++- 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/spec/call/record_method_spec.lua b/spec/call/record_method_spec.lua index 0e91056c1..5e000dc01 100644 --- a/spec/call/record_method_spec.lua +++ b/spec/call/record_method_spec.lua @@ -204,8 +204,9 @@ describe("record method call", function() end m.a.add(first) ]], { - -- FIXME this warning needs to go away when we detect that "m.a" and "first" are not the same - { y = 14, msg = "invoked method as a regular function: consider using ':' instead of '.'" } + -- FIXME these warnings need to go away when we detect that the arities are correct + { y = 10, msg = "invoked method as a regular function: consider using ':' instead of '.'" }, + { y = 14, msg = "invoked method as a regular function: consider using ':' instead of '.'" }, }, {})) it("for function declared in record body with self as different type from receiver", util.check_warnings([[ diff --git a/spec/declaration/local_spec.lua b/spec/declaration/local_spec.lua index 932b11167..044d7a47a 100644 --- a/spec/declaration/local_spec.lua +++ b/spec/declaration/local_spec.lua @@ -460,6 +460,19 @@ describe("local", function() local b : A = { v = 10 } ]])) + + it("localizing a record does not make the new local a type (#759)", util.check([[ + local record k + end + + local kk: k = {} + + local k = k + + k = {} + + kk = {} + ]])) end) describe("", function() diff --git a/spec/stdlib/require_spec.lua b/spec/stdlib/require_spec.lua index 5ba6d8b89..d376c6c67 100644 --- a/spec/stdlib/require_spec.lua +++ b/spec/stdlib/require_spec.lua @@ -1249,4 +1249,31 @@ describe("require", function() { filename = "main.tl", x = 30, y = 1, msg = "require() in type declarations cannot be part of larger expressions" } }, result.syntax_errors) end) + + it("can localize a record and load and alternative implementation (#759)", function () + util.mock_io(finally, { + ["my-lua-utf8.tl"] = [[ + local record my_lua_utf8 + end + + return my_lua_utf8 + ]], + ["main.tl"] = [[ + local type Utf8 = utf8 + local utf8: Utf8 = utf8 + if not utf8 then + local ok, lutf8 = pcall(require, 'my-lua-utf8') as (boolean, Utf8) + if ok then + utf8 = lutf8 + end + end + print(utf8.charpattern) + ]], + }) + local result, err = tl.process("main.tl") + + assert.same({}, result.syntax_errors) + assert.same({}, result.type_errors) + end) + end) diff --git a/tl.lua b/tl.lua index 1cdf889a7..db6f1e6de 100644 --- a/tl.lua +++ b/tl.lua @@ -6979,7 +6979,7 @@ do local scope = self.st[i] local var = scope.vars[name] if var then - if use == "lvalue" and var.is_narrowed then + if use == "lvalue" and var.is_narrowed and var.is_narrowed ~= "localizing" then if var.narrowed_from then var.used = true return { t = var.narrowed_from, attribute = var.attribute }, i, var.attribute @@ -10595,6 +10595,10 @@ self:expand_type(node, values, elements) }) t.inferred_len = nil elseif t.typename == "nominal" then self:resolve_nominal(t) + local rt = t.resolved + if rt.typename == "typedecl" then + t.resolved = rt.def + end end return ok, t, infertype ~= nil diff --git a/tl.tl b/tl.tl index 528550a1e..b3b7a7623 100644 --- a/tl.tl +++ b/tl.tl @@ -6979,7 +6979,7 @@ do local scope = self.st[i] local var = scope.vars[name] if var then - if use == "lvalue" and var.is_narrowed then + if use == "lvalue" and var.is_narrowed and var.is_narrowed ~= "localizing" then if var.narrowed_from then var.used = true return { t = var.narrowed_from, attribute = var.attribute }, i, var.attribute @@ -10595,6 +10595,10 @@ do t.inferred_len = nil elseif t is NominalType then self:resolve_nominal(t) + local rt = t.resolved + if rt is TypeDeclType then + t.resolved = rt.def + end end return ok, t, infertype ~= nil