From 44ca5e19dc174383b1b0490e636398092711306d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 24 Dec 2015 15:30:32 -0700 Subject: [PATCH] add error for break outside loop also fix break in nested loops --- src/analyze.cpp | 12 ++++++++++++ src/analyze.hpp | 4 +++- src/codegen.cpp | 12 +++++++----- test/run_tests.cpp | 17 +++++++++++++---- 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index a5e1255126..b53259bac5 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -830,6 +830,11 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { context->parent = parent; context->variable_table.init(8); + if (parent) { + context->break_allowed = parent->break_allowed || parent->next_child_break_allowed; + parent->next_child_break_allowed = false; + } + if (node && node->type == NodeTypeFnDef) { AstNode *fn_proto_node = node->data.fn_def.fn_proto; context->fn_entry = fn_proto_node->codegen_node->data.fn_proto_node.fn_table_entry; @@ -1359,13 +1364,20 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, TypeTableEntry *expected_type, AstNode *node) { analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.while_expr.condition); + + context->next_child_break_allowed = true; analyze_expression(g, import, context, g->builtin_types.entry_void, node->data.while_expr.body); + return g->builtin_types.entry_void; } static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { + if (!context->break_allowed) { + add_node_error(g, node, + buf_sprintf("'break' expression not in loop")); + } return g->builtin_types.entry_unreachable; } diff --git a/src/analyze.hpp b/src/analyze.hpp index fae6f0b81f..652788dcc7 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -196,7 +196,7 @@ struct CodeGen { FnTableEntry *cur_fn; LLVMBasicBlockRef cur_basic_block; BlockContext *cur_block_context; - LLVMBasicBlockRef cur_break_block; + ZigList break_block_stack; bool c_stdint_used; AstNode *root_export_decl; int version_major; @@ -226,6 +226,8 @@ struct BlockContext { HashMap variable_table; ZigList cast_expr_alloca_list; ZigList struct_val_expr_alloca_list; + bool break_allowed; + bool next_child_break_allowed; LLVMZigDIScope *di_scope; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index d4870e002b..b20eb8801c 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1024,10 +1024,12 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { LLVMBuildCondBr(g->builder, cond_val, body_block, end_block); LLVMPositionBuilderAtEnd(g->builder, body_block); - g->cur_break_block = end_block; + g->break_block_stack.append(end_block); gen_expr(g, node->data.while_expr.body); - g->cur_break_block = nullptr; - LLVMBuildBr(g->builder, cond_block); + g->break_block_stack.pop(); + if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) { + LLVMBuildBr(g->builder, cond_block); + } LLVMPositionBuilderAtEnd(g->builder, end_block); return nullptr; @@ -1035,10 +1037,10 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { static LLVMValueRef gen_break(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeBreak); - assert(g->cur_break_block); + LLVMBasicBlockRef dest_block = g->break_block_stack.last(); add_debug_source_node(g, node); - return LLVMBuildBr(g->builder, g->cur_break_block); + return LLVMBuildBr(g->builder, dest_block); } static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 91cf6591eb..165113ebe9 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -664,11 +664,14 @@ use "std.zig"; export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { var i : i32 = 0; while true { - if i >= 4 { - break; + while true { + if i >= 4 { + break; + } + print_str("loop\n"); + i += 1; } - print_str("loop\n"); - i += 1; + break; } return 0; } @@ -949,6 +952,12 @@ fn f() { }; } )SOURCE", 1, ".tmp_source.zig:11:9: error: no member named 'foo' in 'A'"); + + add_compile_fail_case("invalid break expression", R"SOURCE( +fn f() { + break; +} + )SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression not in loop"); } static void print_compiler_invocation(TestCase *test_case) {