| author | |
| committer | |
| log | 44ca5e19dc174383b1b0490e636398092711306d |
| tree | 5aafee34cee55228bdfd059313389c2a49a63001 |
| parent | 5ceaae288c4f80fe5ce1449cd9d2efe0e541d629 |
also fix break in nested loops4 files changed, 35 insertions(+), 10 deletions(-)
src/analyze.cpp+12| ... | ... | @@ -830,6 +830,11 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { |
| 830 | 830 | context->parent = parent; |
| 831 | 831 | context->variable_table.init(8); |
| 832 | 832 | |
| 833 | if (parent) { | |
| 834 | context->break_allowed = parent->break_allowed || parent->next_child_break_allowed; | |
| 835 | parent->next_child_break_allowed = false; | |
| 836 | } | |
| 837 | ||
| 833 | 838 | if (node && node->type == NodeTypeFnDef) { |
| 834 | 839 | AstNode *fn_proto_node = node->data.fn_def.fn_proto; |
| 835 | 840 | 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, |
| 1359 | 1364 | TypeTableEntry *expected_type, AstNode *node) |
| 1360 | 1365 | { |
| 1361 | 1366 | analyze_expression(g, import, context, g->builtin_types.entry_bool, node->data.while_expr.condition); |
| 1367 | ||
| 1368 | context->next_child_break_allowed = true; | |
| 1362 | 1369 | analyze_expression(g, import, context, g->builtin_types.entry_void, node->data.while_expr.body); |
| 1370 | ||
| 1363 | 1371 | return g->builtin_types.entry_void; |
| 1364 | 1372 | } |
| 1365 | 1373 | |
| 1366 | 1374 | static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 1367 | 1375 | TypeTableEntry *expected_type, AstNode *node) |
| 1368 | 1376 | { |
| 1377 | if (!context->break_allowed) { | |
| 1378 | add_node_error(g, node, | |
| 1379 | buf_sprintf("'break' expression not in loop")); | |
| 1380 | } | |
| 1369 | 1381 | return g->builtin_types.entry_unreachable; |
| 1370 | 1382 | } |
| 1371 | 1383 |
src/analyze.hpp+3-1| ... | ... | @@ -196,7 +196,7 @@ struct CodeGen { |
| 196 | 196 | FnTableEntry *cur_fn; |
| 197 | 197 | LLVMBasicBlockRef cur_basic_block; |
| 198 | 198 | BlockContext *cur_block_context; |
| 199 | LLVMBasicBlockRef cur_break_block; | |
| 199 | ZigList<LLVMBasicBlockRef> break_block_stack; | |
| 200 | 200 | bool c_stdint_used; |
| 201 | 201 | AstNode *root_export_decl; |
| 202 | 202 | int version_major; |
| ... | ... | @@ -226,6 +226,8 @@ struct BlockContext { |
| 226 | 226 | HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table; |
| 227 | 227 | ZigList<CastNode *> cast_expr_alloca_list; |
| 228 | 228 | ZigList<StructValExprNode *> struct_val_expr_alloca_list; |
| 229 | bool break_allowed; | |
| 230 | bool next_child_break_allowed; | |
| 229 | 231 | LLVMZigDIScope *di_scope; |
| 230 | 232 | }; |
| 231 | 233 |
src/codegen.cpp+7-5| ... | ... | @@ -1024,10 +1024,12 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { |
| 1024 | 1024 | LLVMBuildCondBr(g->builder, cond_val, body_block, end_block); |
| 1025 | 1025 | |
| 1026 | 1026 | LLVMPositionBuilderAtEnd(g->builder, body_block); |
| 1027 | g->cur_break_block = end_block; | |
| 1027 | g->break_block_stack.append(end_block); | |
| 1028 | 1028 | gen_expr(g, node->data.while_expr.body); |
| 1029 | g->cur_break_block = nullptr; | |
| 1030 | LLVMBuildBr(g->builder, cond_block); | |
| 1029 | g->break_block_stack.pop(); | |
| 1030 | if (get_expr_type(node->data.while_expr.body)->id != TypeTableEntryIdUnreachable) { | |
| 1031 | LLVMBuildBr(g->builder, cond_block); | |
| 1032 | } | |
| 1031 | 1033 | |
| 1032 | 1034 | LLVMPositionBuilderAtEnd(g->builder, end_block); |
| 1033 | 1035 | return nullptr; |
| ... | ... | @@ -1035,10 +1037,10 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { |
| 1035 | 1037 | |
| 1036 | 1038 | static LLVMValueRef gen_break(CodeGen *g, AstNode *node) { |
| 1037 | 1039 | assert(node->type == NodeTypeBreak); |
| 1038 | assert(g->cur_break_block); | |
| 1040 | LLVMBasicBlockRef dest_block = g->break_block_stack.last(); | |
| 1039 | 1041 | |
| 1040 | 1042 | add_debug_source_node(g, node); |
| 1041 | return LLVMBuildBr(g->builder, g->cur_break_block); | |
| 1043 | return LLVMBuildBr(g->builder, dest_block); | |
| 1042 | 1044 | } |
| 1043 | 1045 | |
| 1044 | 1046 | static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
test/run_tests.cpp+13-4| ... | ... | @@ -664,11 +664,14 @@ use "std.zig"; |
| 664 | 664 | export fn main(argc : isize, argv : &&u8, env : &&u8) -> i32 { |
| 665 | 665 | var i : i32 = 0; |
| 666 | 666 | while true { |
| 667 | if i >= 4 { | |
| 668 | break; | |
| 667 | while true { | |
| 668 | if i >= 4 { | |
| 669 | break; | |
| 670 | } | |
| 671 | print_str("loop\n"); | |
| 672 | i += 1; | |
| 669 | 673 | } |
| 670 | print_str("loop\n"); | |
| 671 | i += 1; | |
| 674 | break; | |
| 672 | 675 | } |
| 673 | 676 | return 0; |
| 674 | 677 | } |
| ... | ... | @@ -949,6 +952,12 @@ fn f() { |
| 949 | 952 | }; |
| 950 | 953 | } |
| 951 | 954 | )SOURCE", 1, ".tmp_source.zig:11:9: error: no member named 'foo' in 'A'"); |
| 955 | ||
| 956 | add_compile_fail_case("invalid break expression", R"SOURCE( | |
| 957 | fn f() { | |
| 958 | break; | |
| 959 | } | |
| 960 | )SOURCE", 1, ".tmp_source.zig:3:5: error: 'break' expression not in loop"); | |
| 952 | 961 | } |
| 953 | 962 | |
| 954 | 963 | static void print_compiler_invocation(TestCase *test_case) { |