diff --git a/JuliaLowering/src/desugaring.jl b/JuliaLowering/src/desugaring.jl index 8ad4fc2c3e2c4..2714fc9811b50 100644 --- a/JuliaLowering/src/desugaring.jl +++ b/JuliaLowering/src/desugaring.jl @@ -4459,8 +4459,8 @@ function expand_forms_2(ctx::DesugaringContext, ex::SyntaxTree, docs=nothing) # Convert Symbol (from Expr conversion) to symbolic_label if label_kind == K"Symbol" label = @ast ctx label label.name_val::K"symbolic_label" - elseif !(label_kind == K"Identifier" || label_kind == K"symbolic_label" || - is_contextual_keyword(label_kind)) + elseif !(label_kind == K"Identifier" || label_kind == K"Placeholder" || + label_kind == K"symbolic_label" || is_contextual_keyword(label_kind)) throw(LoweringError(label, "Invalid break label: expected identifier")) end if nc == 2 diff --git a/JuliaLowering/src/linear_ir.jl b/JuliaLowering/src/linear_ir.jl index ba4a78f9fa45a..086eac07bd606 100644 --- a/JuliaLowering/src/linear_ir.jl +++ b/JuliaLowering/src/linear_ir.jl @@ -308,8 +308,16 @@ function emit_break(ctx, ex) name = ex[1].name_val target = get(ctx.break_targets, name, nothing) if isnothing(target) - ty = name == "loop_exit" ? "break" : "continue" - throw(LoweringError(ex, "$ty must be used inside a `while` or `for` loop")) + if name == "loop_exit" + throw(LoweringError(ex, "`break` must be used inside a `while` or `for` loop")) + elseif name == "loop_cont" + throw(LoweringError(ex, "`continue` must be used inside a `while` or `for` loop")) + elseif endswith(name, "#cont") + label = name[1:end-5] + throw(LoweringError(ex, "`continue $label` is not inside a `@label $label` loop")) + else + throw(LoweringError(ex, "`break $name` is not inside a `@label $name` block")) + end end # Handle valued break (break name val) if numchildren(ex) >= 2 diff --git a/JuliaLowering/test/branching_ir.jl b/JuliaLowering/test/branching_ir.jl index f7a63f40291e6..0ea9786b8fd0b 100644 --- a/JuliaLowering/test/branching_ir.jl +++ b/JuliaLowering/test/branching_ir.jl @@ -237,3 +237,20 @@ x = @label foo LoweringError: x = @label foo # └─┘ ── misplaced label in value position + +######################################## +# Labeled block with underscore label and valued break +@label _ begin + a + break _ 42 + b +end +#--------------------- +1 (= slot₁/__result core.nothing) +2 TestMod.a +3 (= slot₁/__result 42) +4 (goto label₇) +5 TestMod.b +6 (= slot₁/__result %₅) +7 slot₁/__result +8 (return %₇) diff --git a/JuliaLowering/test/loops_ir.jl b/JuliaLowering/test/loops_ir.jl index 709322a084c68..8c5f5a9a55d1d 100644 --- a/JuliaLowering/test/loops_ir.jl +++ b/JuliaLowering/test/loops_ir.jl @@ -119,7 +119,7 @@ break #--------------------- LoweringError: break -└───┘ ── break must be used inside a `while` or `for` loop +└───┘ ── `break` must be used inside a `while` or `for` loop ######################################## # Error: continue outside for/while @@ -127,7 +127,7 @@ continue #--------------------- LoweringError: continue -└──────┘ ── continue must be used inside a `while` or `for` loop +└──────┘ ── `continue` must be used inside a `while` or `for` loop ######################################## # Error: `outer` without outer local variable diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index d7160ee6c8d90..bc4f2bc59cbab 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -5069,7 +5069,10 @@ f(x) = yt(x) ((break) (let ((labl (assq (cadr e) break-labels))) (if (not labl) - (error "break or continue outside loop") + (let ((name (cadr e))) + (if (memq name '(loop-exit loop-cont)) + (error "break or continue outside loop") + (error (string "`break " name "` not in a block with label `" name "`")))) (begin ;; Check if this is a valued break (break name val) (if (length> e 2)