Skip to content

Commit 993b392

Browse files
syntax: Disallow using @goto to skip finally blocks
Add syntax errors when `@goto` is used to jump out of try, catch, or else blocks when a finally block is present. Fixes #60972 Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 0494804 commit 993b392

File tree

6 files changed

+93
-10
lines changed

6 files changed

+93
-10
lines changed

JuliaLowering/src/desugaring.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2120,6 +2120,7 @@ function expand_try(ctx, ex)
21202120

21212121
if !isnothing(finally_)
21222122
# TODO: check unmatched symbolic gotos in try.
2123+
# TODO: Disallow @goto from try/catch/else blocks when there's a finally clause
21232124
end
21242125

21252126
try_body = @ast ctx try_ [K"scope_block"(scope_type=:neutral) try_]

JuliaLowering/test/exceptions.jl

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,4 +352,59 @@ begin
352352
end
353353
""") == (1,2)
354354

355+
@testset "goto from try/catch/else with finally" begin
356+
# Test that @goto from try block with finally is prevented
357+
@test_throws JuliaLowering.LoweringError r"goto from a try/finally block is not permitted" JuliaLowering.include_string(test_mod, """
358+
function f()
359+
try
360+
@goto skip
361+
finally
362+
println("cleanup")
363+
end
364+
@label skip
365+
end
366+
""") broken=true
367+
368+
# Test that @goto from catch block with finally is prevented
369+
@test_throws JuliaLowering.LoweringError r"goto from a catch/finally block is not permitted" JuliaLowering.include_string(test_mod, """
370+
function f()
371+
try
372+
error()
373+
catch
374+
@goto skip
375+
finally
376+
println("cleanup")
377+
end
378+
@label skip
379+
end
380+
""") broken=true
381+
382+
# Test that @goto from else block with finally is prevented
383+
@test_throws JuliaLowering.LoweringError r"goto from an else/finally block is not permitted" JuliaLowering.include_string(test_mod, """
384+
function f()
385+
try
386+
catch
387+
else
388+
@goto skip
389+
finally
390+
println("cleanup")
391+
end
392+
@label skip
393+
end
394+
""") broken=true
395+
396+
# Test that @goto within try/catch/else is allowed when there's no finally
397+
@test JuliaLowering.include_string(test_mod, """
398+
function f()
399+
try
400+
@goto skip
401+
catch
402+
end
403+
@label skip
404+
return 42
405+
end
406+
f()
407+
""") == 42
408+
end
409+
355410
end

base/essentials.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,9 @@ end
991991
992992
`@label` and `@goto` cannot create jumps to different top-level statements. Attempts cause an
993993
error. To still use `@goto`, enclose the `@label` and `@goto` in a block.
994+
995+
Also, `@goto` is not allowed for jumping out of a `try`, `catch`, or `else` block when a `finally`
996+
block is present.
994997
"""
995998
macro goto(name::Symbol)
996999
return esc(Expr(:symbolicgoto, name))

src/julia-syntax.scm

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1433,6 +1433,12 @@
14331433
(cond ((and (length> e 4) (not (equal? (caddddr e) '(false))))
14341434
(if (has-unmatched-symbolic-goto? tryb)
14351435
(error "goto from a try/finally block is not permitted"))
1436+
(if (and (not (equal? catchb '(false)))
1437+
(has-unmatched-symbolic-goto? catchb))
1438+
(error "goto from a catch/finally block is not permitted"))
1439+
(if (and (length> e 5)
1440+
(has-unmatched-symbolic-goto? (cons 'block (cdddddr e))))
1441+
(error "goto from an else/finally block is not permitted"))
14361442
(let ((finalb (caddddr e)))
14371443
(expand-forms
14381444
`(tryfinally

test/exceptions.jl

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -131,16 +131,6 @@ end
131131
@goto outofcatch
132132
end
133133
@label outofcatch
134-
try
135-
error("A")
136-
catch
137-
@test length(current_exceptions()) == 1
138-
@goto outofcatch2
139-
finally
140-
@test length(current_exceptions()) == 0
141-
end
142-
@label outofcatch2
143-
@test length(current_exceptions()) == 0
144134

145135
# Exiting from a try block in various ways should not affect the exception
146136
# stack state.

test/goto.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,34 @@ end
9999
end
100100
end)
101101

102+
@test Expr(:error, "goto from a catch/finally block is not permitted around $(@__FILE__):$(3 + @__LINE__)") ==
103+
Meta.lower(@__MODULE__, quote
104+
function goto_test6_catch()
105+
try
106+
error()
107+
catch
108+
@goto a
109+
finally
110+
end
111+
@label a
112+
return
113+
end
114+
end)
115+
116+
@test Expr(:error, "goto from an else/finally block is not permitted around $(@__FILE__):$(3 + @__LINE__)") ==
117+
Meta.lower(@__MODULE__, quote
118+
function goto_test6_else()
119+
try
120+
catch
121+
else
122+
@goto a
123+
finally
124+
end
125+
@label a
126+
return
127+
end
128+
end)
129+
102130

103131
function goto_test6()
104132
@goto a

0 commit comments

Comments
 (0)