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
97 changes: 97 additions & 0 deletions spec/lang/code_gen/fornum_spec.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
local util = require("spec.util")

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

for i = 1, #t do
i = i + 1
print(t[i])
end
]], [[
local t = { "a", "b", "c" }

for i = 1, #t do
i = i + 1
print(t[i])
end
]], "5.3"))

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

for i = 1, #t do
i = i + 1
print(t[i])
end
]], [[
local t = { "a", "b", "c" }

for i = 1, #t do local i = i
i = i + 1
print(t[i])
end
]], "5.4"))

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

for i = 1, #t do
local j = i + 1
print(t[j])
end
]], [[
local t = { "a", "b", "c" }

for i = 1, #t do
local j = i + 1
print(t[j])
end
]], "5.4"))

it("5.4: generates control variable for a loop with a step", util.gen([[
for i = 10, 1, -2 do
i = i // 2
print(i)
end
]], [[
for i = 10, 1, -2 do local i = i
i = i // 2
print(i)
end
]], "5.4"))

it("5.4: detects an assignment made from a nested function", util.gen([[
for i = 1, 3 do
local function bump()
i = i + 1
end
bump()
print(i)
end
]], [[
for i = 1, 3 do local i = i
local function bump()
i = i + 1
end
bump()
print(i)
end
]], "5.4"))

it("5.4: only shadows the loops that are assigned to", util.gen([[
for i = 1, 3 do
for j = 1, 3 do
j = j + 1
print(i, j)
end
end
]], [[
for i = 1, 3 do
for j = 1, 3 do local j = j
j = j + 1
print(i, j)
end
end
]], "5.4"))
end)
54 changes: 36 additions & 18 deletions teal.lua
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ local parse_typeargs_if_any






local ast = {}
Expand Down Expand Up @@ -8486,7 +8487,14 @@ visit_node.cbs = {
"number"
self:add_var(node.var, node.var.tk, a_type(node.var, typename, {}))
end,
after = end_scope_and_none_type,
after = function(self, node, _children)
local var = self:find_var(node.var.tk)
if var and var.has_been_written_to then
node.fornum_modifies_control_var = true
end
self:end_scope(node)
return NONE
end,
},
["return"] = {
before = function(self, node)
Expand Down Expand Up @@ -11154,6 +11162,25 @@ local bit_operators = {
["<<"] = "lshift",
}


local function shadow_control_var(node, control_var)
if #node.body == 0 then
return
end
local stmt = node_at(node, {
kind = "local_declaration",
vars = node_at(node, {
kind = "variable_list",
node_at(node, { kind = "variable", is_lvalue = true, tk = control_var }),
}),
exps = node_at(node, {
kind = "expression_list",
node_at(node, { kind = "variable", tk = control_var }),
}),
})
table.insert(node.body, 1, stmt)
end

local function adjust_code(ast, needs_compat, gen_compat, gen_target)
local visit = false

Expand Down Expand Up @@ -11283,24 +11310,15 @@ local function adjust_code(ast, needs_compat, gen_compat, gen_target)
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 loc_at = node
local stmt = node_at(loc_at, {
kind = "local_declaration",
vars = node_at(loc_at, {
kind = "variable_list",
node_at(loc_at, { kind = "variable", is_lvalue = true, tk = control_var }),
}),
exps = node_at(loc_at, {
kind = "expression_list",
node_at(loc_at, { kind = "variable", tk = control_var }),
}),
})
table.insert(node.body, 1, stmt)
shadow_control_var(node, node.vars[1].tk)
end
end,
}
visit_node.cbs["fornum"] = {
after = function(_, node, _children)
if node.fornum_modifies_control_var then
shadow_control_var(node, node.var.tk)
end
end,
}
Expand Down
1 change: 1 addition & 0 deletions teal/ast.lua
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ local parse_typeargs_if_any






local ast = {}
Expand Down
1 change: 1 addition & 0 deletions teal/ast.tl
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ local record Node
from: Node
to: Node
step: Node
fornum_modifies_control_var: boolean

-- forin
vars: Node
Expand Down
9 changes: 8 additions & 1 deletion teal/check/visitors.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1217,7 +1217,14 @@ visit_node.cbs = {
"number"
self:add_var(node.var, node.var.tk, a_type(node.var, typename, {}))
end,
after = end_scope_and_none_type,
after = function(self, node, _children)
local var = self:find_var(node.var.tk)
if var and var.has_been_written_to then
node.fornum_modifies_control_var = true
end
self:end_scope(node)
return NONE
end,
},
["return"] = {
before = function(self, node)
Expand Down
9 changes: 8 additions & 1 deletion teal/check/visitors.tl
Original file line number Diff line number Diff line change
Expand Up @@ -1217,7 +1217,14 @@ visit_node.cbs = {
or "number"
self:add_var(node.var, node.var.tk, a_type(node.var, typename, {}))
end,
after = end_scope_and_none_type,
after = function(self: Context, node: Node, _children: {Type}): Type
local var = self:find_var(node.var.tk)
if var and var.has_been_written_to then
node.fornum_modifies_control_var = true
end
self:end_scope(node)
return NONE
end,
},
["return"] = {
before = function(self: Context, node: Node)
Expand Down
44 changes: 27 additions & 17 deletions teal/gen/lua_compat.lua
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,25 @@ local bit_operators = {
["<<"] = "lshift",
}


local function shadow_control_var(node, control_var)
if #node.body == 0 then
return
end
local stmt = node_at(node, {
kind = "local_declaration",
vars = node_at(node, {
kind = "variable_list",
node_at(node, { kind = "variable", is_lvalue = true, tk = control_var }),
}),
exps = node_at(node, {
kind = "expression_list",
node_at(node, { kind = "variable", tk = control_var }),
}),
})
table.insert(node.body, 1, stmt)
end

local function adjust_code(ast, needs_compat, gen_compat, gen_target)
local visit = false

Expand Down Expand Up @@ -246,24 +265,15 @@ local function adjust_code(ast, needs_compat, gen_compat, gen_target)
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 loc_at = node
local stmt = node_at(loc_at, {
kind = "local_declaration",
vars = node_at(loc_at, {
kind = "variable_list",
node_at(loc_at, { kind = "variable", is_lvalue = true, tk = control_var }),
}),
exps = node_at(loc_at, {
kind = "expression_list",
node_at(loc_at, { kind = "variable", tk = control_var }),
}),
})
table.insert(node.body, 1, stmt)
shadow_control_var(node, node.vars[1].tk)
end
end,
}
visit_node.cbs["fornum"] = {
after = function(_, node, _children)
if node.fornum_modifies_control_var then
shadow_control_var(node, node.var.tk)
end
end,
}
Expand Down
44 changes: 27 additions & 17 deletions teal/gen/lua_compat.tl
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,25 @@ local bit_operators: {string:string} = {
["<<"] = "lshift",
}

-- shadow var function
local function shadow_control_var(node: Node, control_var: string)
if #node.body == 0 then
return
end
local stmt = node_at(node, {
kind = "local_declaration",
vars = node_at(node, {
kind = "variable_list",
node_at(node, { kind = "variable", is_lvalue = true, tk = control_var }),
}),
exps = node_at(node, {
kind = "expression_list",
node_at(node, { kind = "variable", tk = control_var }),
}),
})
table.insert(node.body, 1, stmt)
end

local function adjust_code(ast: Node, needs_compat: {string:boolean}, gen_compat: GenCompat, gen_target: string)
local visit = false

Expand Down Expand Up @@ -246,24 +265,15 @@ local function adjust_code(ast: Node, needs_compat: {string:boolean}, gen_compat
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 loc_at = node
local stmt = node_at(loc_at, {
kind = "local_declaration",
vars = node_at(loc_at, {
kind = "variable_list",
node_at(loc_at, { kind = "variable", is_lvalue = true, tk = control_var }),
}),
exps = node_at(loc_at, {
kind = "expression_list",
node_at(loc_at, { kind = "variable", tk = control_var }),
}),
})
table.insert(node.body, 1, stmt)
shadow_control_var(node, node.vars[1].tk)
end
end,
}
visit_node.cbs["fornum"] = {
after = function(_: nil, node: Node, _children: {nil}): nil
if node.fornum_modifies_control_var then
shadow_control_var(node, node.var.tk)
end
end,
}
Expand Down
Loading
Loading