Skip to content

Commit ed9ff68

Browse files
committed
fix: check validity of goto target
Fixes #1121.
1 parent 4b97e8d commit ed9ff68

6 files changed

Lines changed: 92 additions & 13 deletions

File tree

spec/lang/statement/goto_spec.lua

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,38 @@ describe("goto", function()
107107
end
108108
]]))
109109

110+
it("rejects a goto into the scope of local in same block (regression test for #1121)", util.check_type_error([[
111+
goto finish
112+
local _foo = 0
113+
:: finish ::
114+
local _bar = 0
115+
]], {
116+
{ y = 1, msg = "goto jumps into scope of a local variable" }
117+
}))
118+
119+
it("can jump upwards (regression test for #1121)", util.check([[
120+
local _foo = 0
121+
:: finish ::
122+
goto finish
123+
]]))
124+
125+
it("can jump over out-of-scope locals (regression test for #1121)", util.check([[
126+
do
127+
goto finish
128+
local _foo = 0
129+
end
130+
:: finish ::
131+
local _bar = 0
132+
]]))
133+
134+
it("rejects a goto into the scope of local in outer block (regression test for #1121)", util.check_type_error([[
135+
do
136+
goto finish
137+
end
138+
local _foo = 0
139+
:: finish ::
140+
local _bar = 0
141+
]], {
142+
{ y = 2, msg = "goto jumps into scope of a local variable" }
143+
}))
110144
end)

teal/ast.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ local parse_typeargs_if_any
232232

233233

234234

235+
235236

236237

237238
local ast = {}

teal/ast.tl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ local record Node
189189

190190
-- goto
191191
label: string
192+
n_scope_vars: integer
192193

193194
-- label
194195
used_label: boolean

teal/check/visitors.lua

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,16 @@ local function assert_is_a(ctx, w, t1, t2, ectx, name)
783783
return ok
784784
end
785785

786+
local function count_scope_vars(self)
787+
local n = 0
788+
for i = 1, #self.st do
789+
for _, _ in pairs(self.st[i].vars) do
790+
n = n + 1
791+
end
792+
end
793+
return n
794+
end
795+
786796
visit_node.cbs = {
787797
["statements"] = {
788798
before = function(self, node)
@@ -1052,14 +1062,17 @@ visit_node.cbs = {
10521062
end
10531063
end
10541064

1055-
10561065
local scope = self.st[#self.st]
10571066
if scope.pending_labels and scope.pending_labels[label_id] then
1067+
local n_scope_vars = count_scope_vars(self)
1068+
for _, goto_node in ipairs(scope.pending_labels[label_id]) do
1069+
if n_scope_vars > goto_node.n_scope_vars then
1070+
self.errs:add(goto_node, "goto jumps into scope of a local variable")
1071+
end
1072+
end
10581073
node.used_label = true
10591074
scope.pending_labels[label_id] = nil
1060-
10611075
end
1062-
10631076
end,
10641077
after = function()
10651078
return NONE
@@ -1084,6 +1097,7 @@ visit_node.cbs = {
10841097
scope.pending_labels = scope.pending_labels or {}
10851098
scope.pending_labels[label_id] = scope.pending_labels[label_id] or {}
10861099
table.insert(scope.pending_labels[label_id], node)
1100+
node.n_scope_vars = count_scope_vars(self)
10871101
end
10881102

10891103
return NONE

teal/check/visitors.tl

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,16 @@ local function assert_is_a(ctx: Context, w: Where, t1: Type, t2: Type, ectx?: No
783783
return ok
784784
end
785785

786+
local function count_scope_vars(self: Context): integer
787+
local n = 0
788+
for i = 1, #self.st do
789+
for _, _ in pairs(self.st[i].vars) do
790+
n = n + 1
791+
end
792+
end
793+
return n
794+
end
795+
786796
visit_node.cbs = {
787797
["statements"] = {
788798
before = function(self: Context, node: Node)
@@ -1052,14 +1062,17 @@ visit_node.cbs = {
10521062
end
10531063
end
10541064

1055-
--for i = #self.st, 1, -1 do
1056-
local scope = self.st[#self.st]
1057-
if scope.pending_labels and scope.pending_labels[label_id] then
1058-
node.used_label = true
1059-
scope.pending_labels[label_id] = nil
1060-
--break
1065+
local scope = self.st[#self.st]
1066+
if scope.pending_labels and scope.pending_labels[label_id] then
1067+
local n_scope_vars = count_scope_vars(self)
1068+
for _, goto_node in ipairs(scope.pending_labels[label_id]) do
1069+
if n_scope_vars > goto_node.n_scope_vars then
1070+
self.errs:add(goto_node, "goto jumps into scope of a local variable")
1071+
end
10611072
end
1062-
--end
1073+
node.used_label = true
1074+
scope.pending_labels[label_id] = nil
1075+
end
10631076
end,
10641077
after = function(): Type
10651078
return NONE
@@ -1084,6 +1097,7 @@ visit_node.cbs = {
10841097
scope.pending_labels = scope.pending_labels or {}
10851098
scope.pending_labels[label_id] = scope.pending_labels[label_id] or {}
10861099
table.insert(scope.pending_labels[label_id], node)
1100+
node.n_scope_vars = count_scope_vars(self)
10871101
end
10881102

10891103
return NONE

tl.lua

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ local parse_typeargs_if_any
494494

495495

496496

497+
497498

498499

499500
local ast = {}
@@ -8310,6 +8311,16 @@ local function assert_is_a(ctx, w, t1, t2, ectx, name)
83108311
return ok
83118312
end
83128313

8314+
local function count_scope_vars(self)
8315+
local n = 0
8316+
for i = 1, #self.st do
8317+
for _, _ in pairs(self.st[i].vars) do
8318+
n = n + 1
8319+
end
8320+
end
8321+
return n
8322+
end
8323+
83138324
visit_node.cbs = {
83148325
["statements"] = {
83158326
before = function(self, node)
@@ -8579,14 +8590,17 @@ visit_node.cbs = {
85798590
end
85808591
end
85818592

8582-
85838593
local scope = self.st[#self.st]
85848594
if scope.pending_labels and scope.pending_labels[label_id] then
8595+
local n_scope_vars = count_scope_vars(self)
8596+
for _, goto_node in ipairs(scope.pending_labels[label_id]) do
8597+
if n_scope_vars > goto_node.n_scope_vars then
8598+
self.errs:add(goto_node, "goto jumps into scope of a local variable")
8599+
end
8600+
end
85858601
node.used_label = true
85868602
scope.pending_labels[label_id] = nil
8587-
85888603
end
8589-
85908604
end,
85918605
after = function()
85928606
return NONE
@@ -8611,6 +8625,7 @@ visit_node.cbs = {
86118625
scope.pending_labels = scope.pending_labels or {}
86128626
scope.pending_labels[label_id] = scope.pending_labels[label_id] or {}
86138627
table.insert(scope.pending_labels[label_id], node)
8628+
node.n_scope_vars = count_scope_vars(self)
86148629
end
86158630

86168631
return NONE

0 commit comments

Comments
 (0)