Commit 32642ac9cb

Andrew Kelley <superjoe30@gmail.com>
2016-02-04 10:49:12
for loop supports break and continue
See #51
1 parent fdadab4
src/all_types.hpp
@@ -474,6 +474,7 @@ struct AstNodeWhileExpr {
     // populated by semantic analyzer
     bool condition_always_true;
     bool contains_break;
+    bool contains_continue;
     Expr resolved_expr;
     BlockContext *block_context;
 };
@@ -486,6 +487,7 @@ struct AstNodeForExpr {
 
     // populated by semantic analyzer
     bool contains_break;
+    bool contains_continue;
     Expr resolved_expr;
     VariableTableEntry *elem_var;
     VariableTableEntry *index_var;
src/analyze.cpp
@@ -3354,6 +3354,7 @@ static TypeTableEntry *analyze_for_expr(CodeGen *g, ImportTableEntry *import, Bl
     }
 
     BlockContext *child_context = new_block_context(node, context);
+    child_context->parent_loop_node = node;
 
     AstNode *elem_var_node = node->data.for_expr.elem_node;
     elem_var_node->block_context = child_context;
@@ -3385,8 +3386,13 @@ static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import,
 
     AstNode *loop_node = context->parent_loop_node;
     if (loop_node) {
-        assert(loop_node->type == NodeTypeWhileExpr);
-        loop_node->data.while_expr.contains_break = true;
+        if (loop_node->type == NodeTypeWhileExpr) {
+            loop_node->data.while_expr.contains_break = true;
+        } else if (loop_node->type == NodeTypeForExpr) {
+            loop_node->data.for_expr.contains_break = true;
+        } else {
+            zig_unreachable();
+        }
     } else {
         add_node_error(g, node, buf_sprintf("'break' expression outside loop"));
     }
@@ -3396,7 +3402,16 @@ static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import,
 static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
         TypeTableEntry *expected_type, AstNode *node)
 {
-    if (!context->parent_loop_node) {
+    AstNode *loop_node = context->parent_loop_node;
+    if (loop_node) {
+        if (loop_node->type == NodeTypeWhileExpr) {
+            loop_node->data.while_expr.contains_continue = true;
+        } else if (loop_node->type == NodeTypeForExpr) {
+            loop_node->data.for_expr.contains_continue = true;
+        } else {
+            zig_unreachable();
+        }
+    } else {
         add_node_error(g, node, buf_sprintf("'continue' expression outside loop"));
     }
     return g->builtin_types.entry_unreachable;
src/codegen.cpp
@@ -2089,6 +2089,7 @@ static LLVMValueRef gen_for_expr(CodeGen *g, AstNode *node) {
     LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForCond");
     LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForBody");
     LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForEnd");
+    LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForContinue");
 
     LLVMValueRef array_val = gen_array_base_ptr(g, node->data.for_expr.array_expr);
     add_debug_source_node(g, node);
@@ -2122,17 +2123,21 @@ static LLVMValueRef gen_for_expr(CodeGen *g, AstNode *node) {
     gen_assign_raw(g, node, BinOpTypeAssign, elem_var->value_ref, elem_val,
             elem_var->type, child_type);
     g->break_block_stack.append(end_block);
-    g->continue_block_stack.append(cond_block);
+    g->continue_block_stack.append(continue_block);
     gen_expr(g, node->data.for_expr.body);
     g->break_block_stack.pop();
     g->continue_block_stack.pop();
     if (get_expr_type(node->data.for_expr.body)->id != TypeTableEntryIdUnreachable) {
         add_debug_source_node(g, node);
-        LLVMValueRef new_index_val = LLVMBuildAdd(g->builder, index_val, one_const, "");
-        LLVMBuildStore(g->builder, new_index_val, index_ptr);
-        LLVMBuildBr(g->builder, cond_block);
+        LLVMBuildBr(g->builder, continue_block);
     }
 
+    LLVMPositionBuilderAtEnd(g->builder, continue_block);
+    add_debug_source_node(g, node);
+    LLVMValueRef new_index_val = LLVMBuildAdd(g->builder, index_val, one_const, "");
+    LLVMBuildStore(g->builder, new_index_val, index_ptr);
+    LLVMBuildBr(g->builder, cond_block);
+
     LLVMPositionBuilderAtEnd(g->builder, end_block);
     return nullptr;
 }
test/self_hosted.zig
@@ -52,17 +52,33 @@ error SecondError;
 #attribute("test")
 fn constant_enum_with_payload() {
     should_be_empty(AnEnumWithPayload.Empty);
-    should_be_13(AnEnumWithPayload.Full(13));
+    should_be_not_empty(AnEnumWithPayload.Full(13));
 }
 
 fn should_be_empty(x: AnEnumWithPayload) {
     if (x != AnEnumWithPayload.Empty) unreachable{}
 }
 
-fn should_be_13(x: AnEnumWithPayload) {
+fn should_be_not_empty(x: AnEnumWithPayload) {
+    if (x == AnEnumWithPayload.Empty) unreachable{}
 }
 
 enum AnEnumWithPayload {
     Empty,
     Full: i32,
 }
+
+
+#attribute("test")
+fn continue_in_for_loop() {
+    const array = []i32 {1, 2, 3, 4, 5};
+    var sum : i32 = 0;
+    for (x, array) {
+        sum += x;
+        if (x < 3) {
+            continue;
+        }
+        break;
+    }
+    if (sum != 6) unreachable{}
+}