Skip to content

Commit 6a750cd

Browse files
syntax: Disallow using @goto to skip finally blocks (#60979)
1 parent a0083b4 commit 6a750cd

File tree

18 files changed

+188
-27
lines changed

18 files changed

+188
-27
lines changed

JuliaLowering/src/binding_analysis.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ function du_visit!(ctx, state::DefUseState, e)
183183
du_kill!(state)
184184
return false
185185

186-
elseif k in KSet"break symbolicgoto"
186+
elseif k in KSet"break symbolicgoto oldsymbolicgoto"
187187
# this kill!() is not required for soundness since these are branch points
188188
# not merge points, but it's here for parity with flisp
189189
du_kill!(state)

JuliaLowering/src/compat.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,7 @@ function est_to_dst(st::SyntaxTree)
539539
[K"inert"(st[1]) ex]
540540
]
541541
[K"symbolicgoto" lab] -> setattr!(mkleaf(st), :name_val, lab.name_val)
542+
[K"oldsymbolicgoto" lab] -> setattr!(mkleaf(st), :name_val, lab.name_val)
542543
[K"symboliclabel" lab] -> setattr!(mkleaf(st), :name_val, lab.name_val)
543544
[K"symbolicblock" id body] -> if all(==('_'), id.name_val)
544545
@ast g st [K"symbolicblock" id=>K"Placeholder" rec(body)]

JuliaLowering/src/desugaring.jl

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

21192119
if !isnothing(finally_)
21202120
# TODO: check unmatched symbolic gotos in try.
2121+
# TODO: Disallow @goto from try/catch/else blocks when there's a finally clause
21212122
end
21222123

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

JuliaLowering/src/kinds.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ function _register_kinds()
4141
"symboliclabel"
4242
# Goto named label
4343
"symbolicgoto"
44+
# Goto named label (old syntax version, no try/finally check)
45+
"oldsymbolicgoto"
4446
# Labeled block for `@label name expr` (block break)
4547
"symbolicblock"
4648
# Internal initializer for struct types, for inner constructors/functions

JuliaLowering/src/linear_ir.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -774,7 +774,7 @@ function compile(ctx::LinearIRContext, ex, needs_value, in_tail_pos)
774774
elseif needs_value
775775
throw(LoweringError(ex, "misplaced label in value position"))
776776
end
777-
elseif k == K"symbolicgoto"
777+
elseif k == K"symbolicgoto" || k == K"oldsymbolicgoto"
778778
push!(ctx.symbolic_jump_origins, JumpOrigin(ex, length(ctx.code)+1, ctx))
779779
emit(ctx, newleaf(ctx, ex, K"TOMBSTONE")) # ? pop_exception
780780
emit(ctx, newleaf(ctx, ex, K"TOMBSTONE")) # ? leave

JuliaLowering/src/utils.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ function _value_string(ex)
1818
k == K"static_parameter" ? "static_parameter" :
1919
k == K"symboliclabel" ? "label:$(ex.name_val)" :
2020
k == K"symbolicgoto" ? "goto:$(ex.name_val)" :
21+
k == K"oldsymbolicgoto" ? "goto:$(ex.name_val)" :
2122
k == K"SourceLocation" ?
2223
"SourceLocation:$(JuliaSyntax.filename(ex)):$(join(source_location(ex), ':'))" :
2324
k == K"Value" && ex.value isa SourceRef ?

JuliaLowering/src/validation.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ vst1(vcx::Validation1Context, st::SyntaxTree)::ValidationResult = @stm st begin
259259
all(vst1, vcx, [argt, lb, ub, bool]) & vst1_lam(vcx, lam)
260260
[K"symboliclabel" lab] -> vst1_ident(vcx, lab; lhs=true)
261261
[K"symbolicgoto" lab] -> vst1_ident(vcx, lab; lhs=true)
262+
[K"oldsymbolicgoto" lab] -> vst1_ident(vcx, lab; lhs=true)
262263
[K"symbolicblock" lab body] ->
263264
vst1_ident(vcx, lab; lhs=true) & vst1(with(vcx; in_symblock=true), body)
264265
[K"gc_preserve" x ids...] -> vst1(vcx, x) & all(vst1_ident, vcx, ids)
@@ -1119,6 +1120,7 @@ function _assert_syntaxtree(st::SyntaxTree, parents::Vector{NodeId}, vr)
11191120
[K"label"] -> (:id,)
11201121
[K"symboliclabel"] -> (:name_val,)
11211122
[K"symbolicgoto"] -> (:name_val,)
1123+
[K"oldsymbolicgoto"] -> (:name_val,)
11221124
[K"Value"] -> (:value,)
11231125
[K"slot"] -> (:var_id,)
11241126
[K"static_parameter"] -> (:var_id,)
@@ -1176,7 +1178,7 @@ vst2(vcx::Validation2Context, st::SyntaxTree) = @stm st begin
11761178
Identifier BindingId Placeholder
11771179
Bool Char Float Float32 BinInt OctInt HexInt Integer
11781180
SourceLocation String Symbol Value core top
1179-
latestworld latestworld_if_toplevel symbolicgoto symboliclabel TOMBSTONE
1181+
latestworld latestworld_if_toplevel symbolicgoto oldsymbolicgoto symboliclabel TOMBSTONE
11801182
""" ? pass() : @fail(st, "unrecognized leaf kind")
11811183

11821184
[K"call" [K"static_eval" cg] xs...] -> get(cg, :name_val, nothing) == "cglobal" ?

JuliaLowering/test/exceptions.jl

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,60 @@ begin
347347
)
348348
end
349349
""") == (1,2)
350+
351+
@testset "goto from try/catch/else with finally" begin
352+
# Test that @goto from try block with finally is prevented
353+
@test_throws JuliaLowering.LoweringError r"goto from a try/finally block is not permitted" JuliaLowering.include_string(test_mod, """
354+
function f()
355+
try
356+
@goto skip
357+
finally
358+
println("cleanup")
359+
end
360+
@label skip
361+
end
362+
""") broken=true
363+
364+
# Test that @goto from catch block with finally is prevented
365+
@test_throws JuliaLowering.LoweringError r"goto from a catch/finally block is not permitted" JuliaLowering.include_string(test_mod, """
366+
function f()
367+
try
368+
error()
369+
catch
370+
@goto skip
371+
finally
372+
println("cleanup")
373+
end
374+
@label skip
375+
end
376+
""") broken=true
377+
378+
# Test that @goto from else block with finally is prevented
379+
@test_throws JuliaLowering.LoweringError r"goto from an else/finally block is not permitted" JuliaLowering.include_string(test_mod, """
380+
function f()
381+
try
382+
catch
383+
else
384+
@goto skip
385+
finally
386+
println("cleanup")
387+
end
388+
@label skip
389+
end
390+
""") broken=true
391+
392+
# Test that @goto within try/catch/else is allowed when there's no finally
393+
@test JuliaLowering.include_string(test_mod, """
394+
function f()
395+
try
396+
@goto skip
397+
catch
398+
end
399+
@label skip
400+
return 42
401+
end
402+
f()
403+
""") == 42
404+
end
405+
350406
end

JuliaSyntax/src/integration/expr.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,12 @@ end
367367
if kind(secondchildhead) == K"VERSION"
368368
# Encode the syntax version into `loc` so that the argument order
369369
# matches what ordinary macros expect.
370-
loc = Core.MacroSource(loc, popat!(args, 2))
370+
# Core.MacroSource was added in Julia 1.13+; fall back to plain loc on older versions.
371+
if isdefined(Core, :MacroSource)
372+
loc = Core.MacroSource(loc, popat!(args, 2))
373+
else
374+
popat!(args, 2) # discard the version argument
375+
end
371376
end
372377
end
373378
do_lambda = _extract_do_lambda!(args)

JuliaSyntax/src/julia/kinds.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ register_kinds!(JuliaSyntax, 0, [
242242
"abstract"
243243
"as"
244244
"doc"
245+
"goto"
245246
"mutable"
246247
"outer"
247248
"primitive"

0 commit comments

Comments
 (0)