Commit 44ca5e19dc

Andrew Kelley <superjoe30@gmail.com>
2015-12-24 23:30:32
add error for break outside loop
also fix break in nested loops
1 parent 5ceaae2
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;
 }
 
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<LLVMBasicBlockRef> break_block_stack;
     bool c_stdint_used;
     AstNode *root_export_decl;
     int version_major;
@@ -226,6 +226,8 @@ struct BlockContext {
     HashMap<Buf *, VariableTableEntry *, buf_hash, buf_eql_buf> variable_table;
     ZigList<CastNode *> cast_expr_alloca_list;
     ZigList<StructValExprNode *> struct_val_expr_alloca_list;
+    bool break_allowed;
+    bool next_child_break_allowed;
     LLVMZigDIScope *di_scope;
 };
 
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) {
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) {