/
githubmirror
/
julia
Обзор
Документация
Войти
/
githubmirror
/
julia
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
test/goto.jl
695 строк
17 KB
Em Chu
[JuliaLowering] Fix and test empty identifiers (#61913)
04 июн 2026, 23:21
Не верифицирован
04 июн 2026, 23:21
9d21fa4
Код
Авторство
О чём код?
# This file is a part of Julia. License is MIT: https://julialang.org/license # Basic goto tests function goto_test1() @goto a return false @label a return true end @test goto_test1() @test eval(:(@label a)) === nothing @test Expr(:error, "label \"a\" referenced but not defined") == Meta.lower(@__MODULE__, :(@goto a)) @test Expr(:error, "label \"a\" defined multiple times") == Meta.lower(@__MODULE__, quote function goto_test2() @goto a @label a @label a return end end) @test Expr(:error, "label \"a\" referenced but not defined") == Meta.lower(@__MODULE__, quote function goto_test3() @goto a return end end) @test Expr(:error, "misplaced label") == Meta.lower(@__MODULE__, quote function goto_test4() @goto a try @label a catch end end end) # test that labels in macros are reassigned if unescaped macro goto_test5_macro1() return :(@label a) end macro goto_test5_macro2() return :(@goto a) end macro goto_test5_macro3() return esc(:(@label a)) end @test Expr(:error, "label \"a\" referenced but not defined") == Meta.lower(@__MODULE__, quote function goto_test5_1() @goto a @goto_test5_macro1 return end end) let e = Meta.lower(@__MODULE__, quote function goto_test5_2() @goto_test5_macro2 @label a return end end) @test (e::Expr).head === :error @test occursin(r"label \"#\d+#a\" referenced but not defined", e.args[1]) end function goto_test5_3() @goto a return false @goto_test5_macro3 return true end @test goto_test5_3() # Test that symbolicgoto (new syntax version) from try/catch/else with finally is an error let lowered = Meta.lower(@__MODULE__, quote function goto_test6() try $(Expr(:symbolicgoto, :a)) finally end $(Expr(:symboliclabel, :a)) return end end) @test lowered.head === :error @test occursin("goto from a try/finally block is not permitted", lowered.args[1]) end let lowered = Meta.lower(@__MODULE__, quote function goto_test6_catch() try error() catch $(Expr(:symbolicgoto, :a)) finally end $(Expr(:symboliclabel, :a)) return end end) @test lowered.head === :error @test occursin("goto from a catch/finally block is not permitted", lowered.args[1]) end let lowered = Meta.lower(@__MODULE__, quote function goto_test6_else() try catch else $(Expr(:symbolicgoto, :a)) finally end $(Expr(:symboliclabel, :a)) return end end) @test lowered.head === :error @test occursin("goto from an else/finally block is not permitted", lowered.args[1]) end # Test that oldsymbolicgoto (old syntax version / @goto) from try/catch/else with finally is allowed let lowered = Meta.lower(@__MODULE__, quote function goto_test6_old_try() try $(Expr(:oldsymbolicgoto, :a)) finally end $(Expr(:symboliclabel, :a)) return end end) @test lowered.head !== :error end let lowered = Meta.lower(@__MODULE__, quote function goto_test6_old_catch() try error() catch $(Expr(:oldsymbolicgoto, :a)) finally end $(Expr(:symboliclabel, :a)) return end end) @test lowered.head !== :error end let lowered = Meta.lower(@__MODULE__, quote function goto_test6_old_else() try catch else $(Expr(:oldsymbolicgoto, :a)) finally end $(Expr(:symboliclabel, :a)) return end end) @test lowered.head !== :error end function goto_test6() @goto a @label a end @test goto_test6() === nothing function goto_test7(x) @label a if x @goto a end end @test goto_test7(false) === nothing module GotoMacroTest macro goto_test8_macro() quote function $(esc(:goto_test8))() @label a @goto a end end end end GotoMacroTest.@goto_test8_macro # issue #15600 function t0_15600(flag) flag && @goto return2 return 1 @label return2 return 2 end @test t0_15600(true) == 2 @test t0_15600(false) == 1 function t1_15600(flag) flag || @goto return2 return 1 @label return2 return 2 end @test t1_15600(true) == 1 @test t1_15600(false) == 2 # issue #15561 function f15561() a = @goto crater @label crater end @test f15561() === nothing # issue #28077 function foo28077() s = 0 i = 0 @label L i += 1 s += i if i < 10 @goto L end return s end @test foo28077() == 55 # Block break tests (@label name expr with break name val) # Basic block break with value function block_break_test1() @label myblock begin for i in 1:10 if i > 5 break myblock i * 2 end end 0 end end @test block_break_test1() == 12 # Block break without value (returns nothing) function block_break_test2() result = @label myblock begin for i in 1:10 if i > 5 break myblock end end 42 end result end @test block_break_test2() === nothing # Block break from nested loops function block_break_test3() @label outer begin for i in 1:5 for j in 1:5 if i * j > 10 local result = (i, j) break outer result end end end (0, 0) end end @test block_break_test3() == (3, 4) # Block break with no break executed (returns body value) function block_break_test4() @label myblock begin x = 1 + 2 x * 3 end end @test block_break_test4() == 9 # Block break in tail position function block_break_test5(n) @label myblock begin if n > 0 break myblock n * 2 end -1 end end @test block_break_test5(5) == 10 @test block_break_test5(-1) == -1 # Error: cannot use @goto to jump to @label block @test Expr(:error, "cannot use @goto to jump to @label block \"myblock\"") == Meta.lower(@__MODULE__, quote function block_break_test_error1() @goto myblock @label myblock begin 42 end end end) # Break with value DOES work for labeled loops (returns the value) function block_break_value_from_loop() @label loop_exit while true break loop_exit 42 end end @test block_break_value_from_loop() == 42 # Error: duplicate label (symbolicblock and symboliclabel) @test Expr(:error, "label \"foo\" defined multiple times") == Meta.lower(@__MODULE__, quote function block_break_test_error3() @label foo begin 42 end @label foo end end) # Nested block breaks function block_break_nested() @label outer begin x = @label inner begin if true break inner 10 end 0 end x + 5 end end @test block_break_nested() == 15 # Block break with computed value function block_break_computed(arr) @label search begin for (i, v) in enumerate(arr) if v > 100 break search i => v end end nothing end end @test block_break_computed([1, 50, 150, 200]) == (3 => 150) @test block_break_computed([1, 2, 3]) === nothing # Labeled continue tests function labeled_continue_test1() result = Int[] @label outer for i in 1:5 for j in 1:5 if j > 2 continue outer # skip to next i end push!(result, i * 10 + j) end end result end @test labeled_continue_test1() == [11, 12, 21, 22, 31, 32, 41, 42, 51, 52] # Labeled break from outer loop function labeled_break_loop_test() result = Int[] @label outer for i in 1:5 for j in 1:5 if i == 3 && j == 2 break outer # exit both loops end push!(result, i * 10 + j) end end result end @test labeled_break_loop_test() == [11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31] # Labeled break with value from loop function labeled_break_value_test() @label outer for i in 1:10 for j in 1:10 if i * j > 50 local result = (i, j, i * j) break outer result end end end end @test labeled_break_value_test() == (6, 9, 54) # While loop with labeled continue function while_labeled_continue() result = Int[] i = 0 @label outer while i < 5 i += 1 j = 0 while j < 5 j += 1 if j > 2 continue outer end push!(result, i * 10 + j) end end result end @test while_labeled_continue() == [11, 12, 21, 22, 31, 32, 41, 42, 51, 52] # Nested labeled blocks with loops function nested_labeled_loops() @label outer for i in 1:3 x = @label inner for j in 1:3 if j == 2 break inner j * 100 end end if x > 100 break outer x + i end end end @test nested_labeled_loops() == 201 # Ternary operator disambiguation: `cond ? break : val` should work # The space after `:` indicates ternary, not labeled break function ternary_break_test() for i in 1:10 x = i > 5 ? break : i * 2 @test x == i * 2 end end ternary_break_test() function ternary_continue_test() result = Int[] for i in 1:10 i > 5 ? continue : push!(result, i) end result end @test ternary_continue_test() == [1, 2, 3, 4, 5] # Also test labeled break/continue in ternary still works function ternary_labeled_break() @label outer for i in 1:5 for j in 1:5 # labeled break in ternary condition i == 3 && j == 2 ? break outer : nothing end end 42 end @test ternary_labeled_break() == 42 function ternary_labeled_continue() result = Int[] @label outer for i in 1:5 for j in 1:5 # labeled continue in ternary condition j > 2 ? continue outer : push!(result, i * 10 + j) end end result end @test ternary_labeled_continue() == [11, 12, 21, 22, 31, 32, 41, 42, 51, 52] # Tests for combined labeled and ordinary break/continue # Ensure that ordinary break/continue still work inside labeled loops # Ordinary continue inside a labeled loop function combined_ordinary_continue() result = Int[] @label outer for i in 1:5 if i == 3 continue # ordinary continue - should skip to next i end push!(result, i) end result end @test combined_ordinary_continue() == [1, 2, 4, 5] # Ordinary break inside a labeled loop function combined_ordinary_break() result = Int[] @label outer for i in 1:5 if i == 3 break # ordinary break - should exit the loop end push!(result, i) end result end @test combined_ordinary_break() == [1, 2] # Both labeled and ordinary continue in the same loop function combined_labeled_and_ordinary_continue() result = Int[] @label outer for i in 1:4 for j in 1:4 if j == 1 continue # ordinary continue - skip to next j end if j == 2 continue outer # labeled continue - skip to next i end push!(result, i * 10 + j) end push!(result, i * 100) # should not be reached due to continue outer at j==2 end result end @test combined_labeled_and_ordinary_continue() == Int64[] # Both labeled and ordinary break in the same nested loop function combined_labeled_and_ordinary_break() result = Int[] @label outer for i in 1:4 for j in 1:4 push!(result, i * 10 + j) if j == 2 && i == 1 break # ordinary break - exit inner loop only end if j == 1 && i == 2 break outer # labeled break - exit outer loop end end push!(result, i * 100) end result end # i=1: j=1->11, j=2->12, break inner, 100 # i=2: j=1->21, break outer @test combined_labeled_and_ordinary_break() == [11, 12, 100, 21] # Multiple nested loops with mixed break/continue function combined_triple_nested() result = Int[] @label outer for i in 1:3 @label middle for j in 1:3 for k in 1:3 if k == 2 continue # skip to next k end if k == 3 && j == 1 continue middle # skip to next j end if k == 3 && j == 2 continue outer # skip to next i end push!(result, i * 100 + j * 10 + k) end push!(result, -(i * 10 + j)) # middle loop epilogue end push!(result, i * 1000) # outer loop epilogue end result end # i=1, j=1: k=1->111, k=2 skipped, k=3 continue middle # i=1, j=2: k=1->121, k=2 skipped, k=3 continue outer # i=2, j=1: k=1->211, k=2 skipped, k=3 continue middle # i=2, j=2: k=1->221, k=2 skipped, k=3 continue outer # i=3, j=1: k=1->311, k=2 skipped, k=3 continue middle # i=3, j=2: k=1->321, k=2 skipped, k=3 continue outer @test combined_triple_nested() == [111, 121, 211, 221, 311, 321] # Anonymous @label block (1-arg form) inside labeled loop # break should exit the inner @label block, not the loop function anon_block_in_labeled_loop() result = Int[] @label outer for i in 1:3 x = @label begin if i == 2 break _ 100 # break out of anonymous block with value end i * 10 end push!(result, x) if i == 2 && x == 100 continue # ordinary continue - should work end push!(result, x + 1) end result end @test anon_block_in_labeled_loop() == [10, 11, 100, 30, 31] # break / break _ / break _ nothing are all equivalent in @label blocks function break_equivalence_test() @test (@label begin; break; error("unreachable"); end) === nothing @test (@label begin; break _; error("unreachable"); end) === nothing @test (@label begin; break _ nothing; error("unreachable"); end) === nothing @test (@label begin; break _ 42; error("unreachable"); end) == 42 end break_equivalence_test() # @label block inside loop: break exits the @label block, not the loop function break_exits_innermost_label() result = Int[] for i in 1:3 @label begin if i == 2 break # exits @label block, NOT the for loop end push!(result, i) end end result end @test break_exits_innermost_label() == [1, 3] # Nested @label blocks (inner shadows outer) function nested_anon_label_blocks() x = @label begin y = @label begin break _ 10 end break _ y + 5 end x end @test nested_anon_label_blocks() == 15 # @label _ and @label _ body are errors @test_throws "use `@label expr` for anonymous blocks; `@label _` is not allowed" @eval @label _ @test_throws "use `@label expr` for anonymous blocks; `@label _ expr` is not allowed" @eval @label _ begin 42 end # Plain break inside named @label block is disallowed @test Expr(:error, "plain `break` inside `@label blk` block is disallowed; use `break blk` to exit the block") == Meta.lower(@__MODULE__, quote function break_in_named_block() for i in 1:3 @label blk begin break end end end end) # continue inside anonymous @label block is disallowed @test Expr(:error, "`continue` inside an anonymous `@label` block is not allowed") == Meta.lower(@__MODULE__, quote function continue_in_label_block() for i in 1:3 @label begin continue end end end end) # issue #61129 - @goto into a while loop with break should not error function goto_into_while_break() @goto foo while true @label foo break end end @test goto_into_while_break() === nothing # empty label @test Core.eval(@__MODULE__, Expr(:symbolicblock, Symbol(""), Expr(:break, Symbol(""), 1))) == 1