diff --git a/compiler/checker/checker.odin b/compiler/checker/checker.odin index 46a8d17..70e0f26 100644 --- a/compiler/checker/checker.odin +++ b/compiler/checker/checker.odin @@ -12266,7 +12266,9 @@ build_value_block :: proc( ) -> (value: hir.Expr_Id, value_type: types.Type) { checker := ctx.checker n := len(body_stmts) - if n == 0 || checker.ast_module.statements[body_stmts[n - 1]].kind != .Yield { + if n == 0 || + checker.ast_module.statements[body_stmts[n - 1]].kind != .Yield || + symbol.is_valid(checker.ast_module.statements[body_stmts[n - 1]].label) { // Build whatever is there so inner errors (and misplaced yields) surface, then // report the missing trailing yield. inner := build_block(ctx, body_stmts) @@ -12657,6 +12659,21 @@ emit_value_branch :: proc( emit_slot_assign(checker, out, slot^, value, span) return true } + if n == 1 { + block_stmt := checker.ast_module.statements[branch_stmts[0]] + if block_stmt.kind == .Block && symbol.is_valid(block_stmt.label) { + expected := slot_type^ if slot^ != hir.INVALID_LOCAL else types.INVALID + value, value_type := build_value_labeled_block( + ctx, out, block_stmt.body, block_stmt.label, expected, block_stmt.span, + ) + if checker.module.exprs[value].kind == .Invalid { + return false + } + value = adopt_value_slot(ctx, slot, slot_type, value, value_type, span) + emit_slot_assign(checker, out, slot^, value, span) + return true + } + } ends_in_yield := n > 0 && checker.ast_module.statements[branch_stmts[n - 1]].kind == .Yield && !symbol.is_valid(checker.ast_module.statements[branch_stmts[n - 1]].label) diff --git a/compiler_tests.odin b/compiler_tests.odin index 9684e0f..9854785 100644 --- a/compiler_tests.odin +++ b/compiler_tests.odin @@ -6268,6 +6268,40 @@ value_loop_label_does_not_shadow_own_yield_target :: proc(t: ^testing.T) { testing.expect_value(t, len(diagnostics.items), 0) } +@(test) +labeled_block_supplies_value_if_branch_through_catch :: proc(t: ^testing.T) { + text := `Failure :: enum { bad } +ScanResult :: struct { end usize } +scan func() ScanResult ! Failure { return .bad } +main func() i32 { + start usize = 3 + end :: if (true) done: { + result :: scan() catch { + yield :done start + } + yield :done result.end + } else { + yield start + } + if (end == start) return 0 + return 1 +} +` + source_file := source.Source{path="test.bro", text=text} + diagnostics := source.init_diagnostics(&source_file) + defer source.destroy_diagnostics(&diagnostics) + symbols := symbol.init_table() + defer symbol.destroy_table(&symbols) + stream := lexer.lex(&source_file, &diagnostics, &symbols) + defer delete(stream.items) + ast_module := parser.parse(&stream, &source_file, &diagnostics) + defer ast.destroy_module(&ast_module) + hir_module := checker.check(&ast_module, &diagnostics, &symbols) + defer hir.destroy_module(&hir_module) + + testing.expect_value(t, len(diagnostics.items), 0) +} + @(test) native_union_compiles_and_runs :: proc(t: ^testing.T) { output := "/tmp/brolang-test-unions"