Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
Test:
strategy:
matrix:
lua-version: ["5.4", "5.3", "5.2", "5.1", "luajit"]
lua-version: ["5.5", "5.4", "5.3", "5.2", "5.1", "luajit"]
os: ["ubuntu-latest"]
include:
- os: "macos-latest"
Expand Down
67 changes: 67 additions & 0 deletions spec/lang/code_gen/forin_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
local util = require("spec.util")

describe("forin", function()
it("5.3: doesn't generate control variable that is local to the iteration", util.gen([[
local mypairs: function({string:string}): (function(string): (string, string))

local t: {string:string} = { k1 = "a", k2 = "b", k3 = "c" }

for k, v in mypairs(t) do
k = k .. "!"
print(k)
k = "yes"
print(k)
end
]], [[
local mypairs

local t = { k1 = "a", k2 = "b", k3 = "c" }

for k, v in mypairs(t) do
k = k .. "!"
print(k)
k = "yes"
print(k)
end
]], "5.3"))

it("5.4: generates control variable that is local to the iteration", util.gen([[
local t: {string:string} = { k1 = "a", k2 = "b", k3 = "c" }

for k, v in pairs(t) do
k = k .. "!"
print(k)
k = "yes"
print(k)
end
]], [[
local t = { k1 = "a", k2 = "b", k3 = "c" }

for k, v in pairs(t) do local k = k
k = k .. "!"
print(k)
k = "yes"
print(k)
end
]], "5.4"))

it("5.4: does not generate control variable if not assigned to", util.gen([[
local t: {string:string} = { k1 = "a", k2 = "b", k3 = "c" }

for k, v in pairs(t) do
local k2 = k .. "!"
print(k2)
k2 = "yes"
print(k2)
end
]], [[
local t = { k1 = "a", k2 = "b", k3 = "c" }

for k, v in pairs(t) do
local k2 = k .. "!"
print(k2)
k2 = "yes"
print(k2)
end
]], "5.4"))
end)
6 changes: 3 additions & 3 deletions teal/check/relations.lua
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ local function subtype_record(ck, a, b)

if a.is_userdata ~= b.is_userdata then
return false, { errors.new(a.is_userdata and "userdata is not a record" or
"record is not a userdata"), }
"record is not a userdata"), }
end

local errs = {}
Expand Down Expand Up @@ -667,8 +667,8 @@ relations.subtype_relations = {
for i = 1, math.min(#a.types, #b.types) do
if not ck:is_a(a.types[i], b.types[i]) then
return false, { types.error("in tuple entry " ..
tostring(i) .. ": got %s, expected %s",
a.types[i], b.types[i]), }
tostring(i) .. ": got %s, expected %s",
a.types[i], b.types[i]), }
end
end
if #a.types > #b.types then
Expand Down
14 changes: 11 additions & 3 deletions teal/check/visitors.lua
Original file line number Diff line number Diff line change
Expand Up @@ -630,9 +630,9 @@ local function infer_table_literal(self, node, children)
if is_array and is_map then
self.errs:add(node, "cannot determine type of table literal")
t = a_type(node, "map", { keys =
self:expand_type(node, keys, a_type(node, "integer", {})), values =
self:expand_type(node, keys, a_type(node, "integer", {})), values =

self:expand_type(node, values, elements) })
self:expand_type(node, values, elements) })
elseif is_record and is_array then
t = a_type(node, "record", {
fields = fields,
Expand Down Expand Up @@ -1138,7 +1138,15 @@ visit_node.cbs = {
end
end
end,
after = end_scope_and_none_type,
after = function(self, node, _children)
local control_var = node.vars[1]
local var = self:find_var(control_var.tk)
if var and var.has_been_written_to then
node.forin_modifies_control_var = true
end
self:end_scope(node)
return NONE
end,
},
["fornum"] = {
before_statements = function(self, node, children)
Expand Down
10 changes: 9 additions & 1 deletion teal/check/visitors.tl
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,15 @@ visit_node.cbs = {
end
end
end,
after = end_scope_and_none_type,
after = function(self: Context, node: Node, _children: {Type}): Type
local control_var = node.vars[1]
local var = self:find_var(control_var.tk)
if var and var.has_been_written_to then
node.forin_modifies_control_var = true
end
self:end_scope(node)
return NONE
end
},
["fornum"] = {
before_statements = function(self: Context, node: Node, children: {Type})
Expand Down
33 changes: 29 additions & 4 deletions teal/gen/lua_compat.lua
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,14 @@ local bit_operators = {
}

local function adjust_code(ast, needs_compat, gen_compat, gen_target)
if gen_target == "5.4" then
return
end
local visit = false

local visit_node = {
cbs = {},
}

if gen_compat ~= "off" then
if (gen_target == "5.1" or gen_target == "5.3") and gen_compat ~= "off" then
visit = true
visit_node.cbs["op"] = {
after = function(_, node, _children)
if node.op.op == "is" then
Expand Down Expand Up @@ -174,6 +173,32 @@ local function adjust_code(ast, needs_compat, gen_compat, gen_target)
}
end

if gen_target == "5.4" then
visit = true
visit_node.cbs["forin"] = {
after = function(_, node, _children)
if #node.body == 0 then
return
end
if node.forin_modifies_control_var then
local control_var = node.vars[1].tk
local localization = parser.parse("local " .. control_var .. " = " .. control_var, "@<internal>.lua")
localization[1].y = node.y
localization[1].vars.y = node.y
localization[1].vars[1].y = node.y
localization[1].exps.y = node.y
localization[1].exps[1].y = node.y
table.insert(node.body, 1, localization[1])
node.body.y = node.y
end
end,
}
end

if not visit then
return
end

traverse_nodes(nil, ast, visit_node, {})

add_compat_entries(ast, needs_compat, gen_compat)
Expand Down
33 changes: 29 additions & 4 deletions teal/gen/lua_compat.tl
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,14 @@ local bit_operators: {string:string} = {
}

local function adjust_code(ast: Node, needs_compat: {string:boolean}, gen_compat: GenCompat, gen_target: string)
if gen_target == "5.4" then
return
end
local visit = false

local visit_node: Visitor<nil, NodeKind, Node, nil> = {
cbs = {}
}

if gen_compat ~= "off" then
if (gen_target == "5.1" or gen_target == "5.3") and gen_compat ~= "off" then
visit = true
visit_node.cbs["op"] = {
after = function(_: nil, node: Node, _children: {nil}): nil
if node.op.op == "is" then
Expand Down Expand Up @@ -174,6 +173,32 @@ local function adjust_code(ast: Node, needs_compat: {string:boolean}, gen_compat
}
end

if gen_target == "5.4" then
visit = true
visit_node.cbs["forin"] = {
after = function(_: nil, node: Node, _children: {nil}): nil
if #node.body == 0 then
return
end
if node.forin_modifies_control_var then
local control_var = node.vars[1].tk
local localization = parser.parse("local " .. control_var .. " = " .. control_var, "@<internal>.lua") as Node
localization[1].y = node.y
localization[1].vars.y = node.y
localization[1].vars[1].y = node.y
localization[1].exps.y = node.y
localization[1].exps[1].y = node.y
table.insert(node.body, 1, localization[1])
node.body.y = node.y
end
end,
}
end

if not visit then
return
end

traverse_nodes(nil, ast, visit_node, {})

add_compat_entries(ast, needs_compat, gen_compat)
Expand Down
Loading
Loading