Commit 24048b2af6

Andrew Kelley <superjoe30@gmail.com>
2016-12-06 00:43:16
IR: implement break and continue
1 parent 0541532
src/all_types.hpp
@@ -642,9 +642,11 @@ struct AstNodeBoolLiteral {
 };
 
 struct AstNodeBreakExpr {
+    bool is_inline; // TODO
 };
 
 struct AstNodeContinueExpr {
+    bool is_inline; // TODO
 };
 
 struct AstNodeArrayType {
@@ -1233,11 +1235,21 @@ struct LabelTableEntry {
     bool used;
 };
 
+enum ScopeId {
+    ScopeIdDecls,
+    ScopeIdBlock,
+    ScopeIdDefer,
+    ScopeIdVarDecl,
+    ScopeIdCImport,
+    ScopeIdLoop,
+    ScopeIdFnDef,
+};
+
 struct Scope {
-    AstNode *node;
+    ScopeId id;
+    AstNode *source_node;
 
-    // if the scope has a parent, this is it. Every scope has a parent except
-    // ScopeIdGlobal
+    // if the scope has a parent, this is it
     Scope *parent;
 
     ZigLLVMDIScope *di_scope;
@@ -1293,6 +1305,7 @@ struct ScopeCImport {
 // This scope is created for a loop such as for or while in order to
 // make break and continue statements work.
 // NodeTypeForExpr or NodeTypeWhileExpr
+// TODO I think we can get rid of this
 struct ScopeLoop {
     Scope base;
 };
src/analyze.cpp
@@ -130,15 +130,16 @@ ScopeDecls *get_container_scope(TypeTableEntry *type_entry) {
     return *get_container_scope_ptr(type_entry);
 }
 
-void init_scope(Scope *dest, AstNode *node, Scope *parent) {
-    dest->node = node;
+void init_scope(Scope *dest, ScopeId id, AstNode *source_node, Scope *parent) {
+    dest->id = id;
+    dest->source_node = source_node;
     dest->parent = parent;
 }
 
 static ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import) {
     assert(node->type == NodeTypeRoot || node->type == NodeTypeContainerDecl);
     ScopeDecls *scope = allocate<ScopeDecls>(1);
-    init_scope(&scope->base, node, parent);
+    init_scope(&scope->base, ScopeIdDecls, node, parent);
     scope->decl_table.init(4);
     scope->container_type = container_type;
     scope->import = import;
@@ -148,7 +149,7 @@ static ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEnt
 Scope *create_block_scope(AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeBlock);
     ScopeBlock *scope = allocate<ScopeBlock>(1);
-    init_scope(&scope->base, node, parent);
+    init_scope(&scope->base, ScopeIdBlock, node, parent);
     scope->label_table.init(1);
     return &scope->base;
 }
@@ -156,14 +157,13 @@ Scope *create_block_scope(AstNode *node, Scope *parent) {
 Scope *create_defer_scope(AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeDefer);
     ScopeDefer *scope = allocate<ScopeDefer>(1);
-    init_scope(&scope->base, node, parent);
+    init_scope(&scope->base, ScopeIdDefer, node, parent);
     return &scope->base;
 }
 
 Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var) {
-    assert(node->type == NodeTypeVariableDeclaration || node->type == NodeTypeParamDecl);
     ScopeVarDecl *scope = allocate<ScopeVarDecl>(1);
-    init_scope(&scope->base, node, parent);
+    init_scope(&scope->base, ScopeIdVarDecl, node, parent);
     scope->var = var;
     return &scope->base;
 }
@@ -171,7 +171,7 @@ Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var) {
 Scope *create_cimport_scope(AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeFnCallExpr);
     ScopeCImport *scope = allocate<ScopeCImport>(1);
-    init_scope(&scope->base, node, parent);
+    init_scope(&scope->base, ScopeIdCImport, node, parent);
     buf_resize(&scope->c_import_buf, 0);
     return &scope->base;
 }
@@ -179,21 +179,21 @@ Scope *create_cimport_scope(AstNode *node, Scope *parent) {
 Scope *create_loop_scope(AstNode *node, Scope *parent) {
     assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr);
     ScopeLoop *scope = allocate<ScopeLoop>(1);
-    init_scope(&scope->base, node, parent);
+    init_scope(&scope->base, ScopeIdLoop, node, parent);
     return &scope->base;
 }
 
 ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {
     assert(node->type == NodeTypeFnDef);
     ScopeFnDef *scope = allocate<ScopeFnDef>(1);
-    init_scope(&scope->base, node, parent);
+    init_scope(&scope->base, ScopeIdFnDef, node, parent);
     scope->fn_entry = fn_entry;
     return scope;
 }
 
 ImportTableEntry *get_scope_import(Scope *scope) {
     while (scope) {
-        if (scope->node->type == NodeTypeRoot || scope->node->type == NodeTypeContainerDecl) {
+        if (scope->id == ScopeIdDecls) {
             ScopeDecls *decls_scope = (ScopeDecls *)scope;
             assert(decls_scope->import);
             return decls_scope->import;
@@ -1991,9 +1991,7 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry *
 
 Tld *find_decl(Scope *scope, Buf *name) {
     while (scope) {
-        if (scope->node->type == NodeTypeRoot ||
-            scope->node->type == NodeTypeContainerDecl)
-        {
+        if (scope->id == ScopeIdDecls) {
             ScopeDecls *decls_scope = (ScopeDecls *)scope;
             auto entry = decls_scope->decl_table.maybe_get(name);
             if (entry)
@@ -2006,15 +2004,11 @@ Tld *find_decl(Scope *scope, Buf *name) {
 
 VariableTableEntry *find_variable(CodeGen *g, Scope *scope, Buf *name) {
     while (scope) {
-        if (scope->node->type == NodeTypeVariableDeclaration ||
-            scope->node->type == NodeTypeParamDecl)
-        {
+        if (scope->id == ScopeIdVarDecl) {
             ScopeVarDecl *var_scope = (ScopeVarDecl *)scope;
             if (buf_eql_buf(name, &var_scope->var->name))
                 return var_scope->var;
-        } else if (scope->node->type == NodeTypeRoot ||
-                   scope->node->type == NodeTypeContainerDecl)
-        {
+        } else if (scope->id == ScopeIdDecls) {
             ScopeDecls *decls_scope = (ScopeDecls *)scope;
             auto entry = decls_scope->decl_table.maybe_get(name);
             if (entry) {
@@ -2034,7 +2028,7 @@ VariableTableEntry *find_variable(CodeGen *g, Scope *scope, Buf *name) {
 
 FnTableEntry *scope_fn_entry(Scope *scope) {
     while (scope) {
-        if (scope->node->type == NodeTypeFnDef) {
+        if (scope->id == ScopeIdFnDef) {
             ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
             return fn_scope->fn_entry;
         }
src/ast_render.cpp
@@ -364,6 +364,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
         case NodeTypeSwitchProng:
         case NodeTypeSwitchRange:
         case NodeTypeLabel:
+        case NodeTypeStructValueField:
             zig_unreachable();
         case NodeTypeRoot:
             for (size_t i = 0; i < node->data.root.top_level_decls.length; i += 1) {
@@ -602,9 +603,30 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
             }
         case NodeTypeContainerInitExpr:
             render_node_ungrouped(ar, node->data.container_init_expr.type);
-            fprintf(ar->f, "{");
-            assert(node->data.container_init_expr.entries.length == 0);
+            if (node->data.container_init_expr.kind == ContainerInitKindStruct) {
+                fprintf(ar->f, "{\n");
+                ar->indent += ar->indent_size;
+            } else {
+                fprintf(ar->f, "{");
+            }
+            for (size_t i = 0; i < node->data.container_init_expr.entries.length; i += 1) {
+                AstNode *entry = node->data.container_init_expr.entries.at(i);
+                if (entry->type == NodeTypeStructValueField) {
+                    Buf *name = entry->data.struct_val_field.name;
+                    AstNode *expr = entry->data.struct_val_field.expr;
+                    fprintf(ar->f, ".%s = ", buf_ptr(name));
+                    render_node_grouped(ar, expr);
+                    fprintf(ar->f, ",\n");
+                } else {
+                    if (i != 0)
+                        fprintf(ar->f, ", ");
+                    render_node_grouped(ar, entry);
+                }
+            }
             fprintf(ar->f, "}");
+            if (node->data.container_init_expr.kind == ContainerInitKindStruct) {
+                ar->indent -= ar->indent_size;
+            }
             break;
         case NodeTypeArrayType:
             {
@@ -788,7 +810,40 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
             }
         case NodeTypeGoto:
             {
-                fprintf(ar->f, "goto %s", buf_ptr(node->data.goto_expr.name));
+                const char *inline_str = node->data.goto_expr.is_inline ? "inline " : "";
+                fprintf(ar->f, "%sgoto %s", inline_str, buf_ptr(node->data.goto_expr.name));
+                break;
+            }
+        case NodeTypeForExpr:
+            {
+                const char *inline_str = node->data.for_expr.is_inline ? "inline " : "";
+                fprintf(ar->f, "%sfor (", inline_str);
+                render_node_grouped(ar, node->data.for_expr.array_expr);
+                fprintf(ar->f, ") ");
+                if (node->data.for_expr.elem_node) {
+                    fprintf(ar->f, "|");
+                    if (node->data.for_expr.elem_is_ptr)
+                        fprintf(ar->f, "*");
+                    render_node_grouped(ar, node->data.for_expr.elem_node);
+                    if (node->data.for_expr.index_node) {
+                        fprintf(ar->f, ", ");
+                        render_node_grouped(ar, node->data.for_expr.index_node);
+                    }
+                    fprintf(ar->f, "| ");
+                }
+                render_node_grouped(ar, node->data.for_expr.body);
+                break;
+            }
+        case NodeTypeBreak:
+            {
+                const char *inline_str = node->data.break_expr.is_inline ? "inline " : "";
+                fprintf(ar->f, "%sbreak", inline_str);
+                break;
+            }
+        case NodeTypeContinue:
+            {
+                const char *inline_str = node->data.continue_expr.is_inline ? "inline " : "";
+                fprintf(ar->f, "%scontinue", inline_str);
                 break;
             }
         case NodeTypeFnDecl:
@@ -797,12 +852,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
         case NodeTypeUnwrapErrorExpr:
         case NodeTypeSliceExpr:
         case NodeTypeStructField:
-        case NodeTypeStructValueField:
         case NodeTypeUse:
         case NodeTypeZeroesLiteral:
-        case NodeTypeForExpr:
-        case NodeTypeBreak:
-        case NodeTypeContinue:
             zig_panic("TODO more ast rendering");
     }
 }
src/codegen.cpp
@@ -277,40 +277,55 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
     if (scope->di_scope)
         return scope->di_scope;
 
-    if (scope->node->type == NodeTypeFnDef) {
-        assert(scope->parent);
-        ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
-        FnTableEntry *fn_table_entry = fn_scope->fn_entry;
-        unsigned line_number = fn_table_entry->proto_node->line + 1;
-        unsigned scope_line = line_number;
-        bool is_definition = fn_table_entry->fn_def_node != nullptr;
-        unsigned flags = 0;
-        bool is_optimized = g->is_release_build;
-        ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder,
-            get_di_scope(g, scope->parent), buf_ptr(&fn_table_entry->symbol_name), "",
-            scope->node->owner->di_file, line_number,
-            fn_table_entry->type_entry->di_type, fn_table_entry->internal_linkage,
-            is_definition, scope_line, flags, is_optimized, nullptr);
-
-        scope->di_scope = ZigLLVMSubprogramToScope(subprogram);
-        ZigLLVMFnSetSubprogram(fn_llvm_value(g, fn_table_entry), subprogram);
-    } else if (scope->node->type == NodeTypeRoot) {
-        scope->di_scope = ZigLLVMFileToScope(scope->node->owner->di_file);
-    } else if (scope->node->type == NodeTypeContainerDecl) {
-        ScopeDecls *decls_scope = (ScopeDecls *)scope;
-        assert(decls_scope->container_type);
-        scope->di_scope = ZigLLVMTypeToScope(decls_scope->container_type->di_type);
-    } else {
-        assert(scope->parent);
-        ZigLLVMDILexicalBlock *di_block = ZigLLVMCreateLexicalBlock(g->dbuilder,
-            get_di_scope(g, scope->parent),
-            scope->node->owner->di_file,
-            scope->node->line + 1,
-            scope->node->column + 1);
-        scope->di_scope = ZigLLVMLexicalBlockToScope(di_block);
+    ImportTableEntry *import = get_scope_import(scope);
+    switch (scope->id) {
+        case ScopeIdCImport:
+            zig_unreachable();
+        case ScopeIdFnDef:
+        {
+            assert(scope->parent);
+            ScopeFnDef *fn_scope = (ScopeFnDef *)scope;
+            FnTableEntry *fn_table_entry = fn_scope->fn_entry;
+            unsigned line_number = fn_table_entry->proto_node->line + 1;
+            unsigned scope_line = line_number;
+            bool is_definition = fn_table_entry->fn_def_node != nullptr;
+            unsigned flags = 0;
+            bool is_optimized = g->is_release_build;
+            ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder,
+                get_di_scope(g, scope->parent), buf_ptr(&fn_table_entry->symbol_name), "",
+                import->di_file, line_number,
+                fn_table_entry->type_entry->di_type, fn_table_entry->internal_linkage,
+                is_definition, scope_line, flags, is_optimized, nullptr);
+
+            scope->di_scope = ZigLLVMSubprogramToScope(subprogram);
+            ZigLLVMFnSetSubprogram(fn_llvm_value(g, fn_table_entry), subprogram);
+            return scope->di_scope;
+        }
+        case ScopeIdDecls:
+            if (scope->parent) {
+                ScopeDecls *decls_scope = (ScopeDecls *)scope;
+                assert(decls_scope->container_type);
+                scope->di_scope = ZigLLVMTypeToScope(decls_scope->container_type->di_type);
+            } else {
+                scope->di_scope = ZigLLVMFileToScope(import->di_file);
+            }
+            return scope->di_scope;
+        case ScopeIdBlock:
+        case ScopeIdDefer:
+        case ScopeIdVarDecl:
+        case ScopeIdLoop:
+        {
+            assert(scope->parent);
+            ZigLLVMDILexicalBlock *di_block = ZigLLVMCreateLexicalBlock(g->dbuilder,
+                get_di_scope(g, scope->parent),
+                import->di_file,
+                scope->source_node->line + 1,
+                scope->source_node->column + 1);
+            scope->di_scope = ZigLLVMLexicalBlockToScope(di_block);
+            return scope->di_scope;
+        }
     }
-
-    return scope->di_scope;
+    zig_unreachable();
 }
 
 static void clear_debug_source_node(CodeGen *g) {
@@ -400,11 +415,11 @@ static bool ir_want_debug_safety(CodeGen *g, IrInstruction *instruction) {
     // TODO memoize
     Scope *scope = instruction->scope;
     while (scope) {
-        if (scope->node->type == NodeTypeBlock) {
+        if (scope->id == ScopeIdBlock) {
             ScopeBlock *block_scope = (ScopeBlock *)scope;
             if (block_scope->safety_set_node)
                 return !block_scope->safety_off;
-        } else if (scope->node->type == NodeTypeRoot || scope->node->type == NodeTypeContainerDecl) {
+        } else if (scope->id == ScopeIdDecls) {
             ScopeDecls *decls_scope = (ScopeDecls *)scope;
             if (decls_scope->safety_set_node)
                 return !decls_scope->safety_off;
src/ir.cpp
@@ -18,12 +18,17 @@ struct IrExecContext {
     size_t mem_slot_count;
 };
 
+struct LoopStackItem {
+    IrBasicBlock *break_block;
+    IrBasicBlock *continue_block;
+    bool is_inline;
+};
+
 struct IrBuilder {
     CodeGen *codegen;
     IrExecutable *exec;
     IrBasicBlock *current_basic_block;
-    ZigList<IrBasicBlock *> break_block_stack;
-    ZigList<IrBasicBlock *> continue_block_stack;
+    ZigList<LoopStackItem> loop_stack;
 };
 
 struct IrAnalyze {
@@ -1241,13 +1246,17 @@ static void ir_gen_defers_for_block(IrBuilder *irb, Scope *parent_scope, Scope *
         bool gen_error_defers, bool gen_maybe_defers)
 {
     while (inner_scope != outer_scope) {
-        if (inner_scope->node->type == NodeTypeDefer &&
-           ((inner_scope->node->data.defer.kind == ReturnKindUnconditional) ||
-            (gen_error_defers && inner_scope->node->data.defer.kind == ReturnKindError) ||
-            (gen_maybe_defers && inner_scope->node->data.defer.kind == ReturnKindMaybe)))
-        {
-            AstNode *defer_expr_node = inner_scope->node->data.defer.expr;
-            ir_gen_node(irb, defer_expr_node, parent_scope);
+        if (inner_scope->id == ScopeIdDefer) {
+            assert(inner_scope->source_node->type == NodeTypeDefer);
+            ReturnKind defer_kind = inner_scope->source_node->data.defer.kind;
+            if (defer_kind == ReturnKindUnconditional ||
+                (gen_error_defers && defer_kind == ReturnKindError) ||
+                (gen_maybe_defers && defer_kind == ReturnKindMaybe))
+            {
+                AstNode *defer_expr_node = inner_scope->source_node->data.defer.expr;
+                ir_gen_node(irb, defer_expr_node, parent_scope);
+            }
+
         }
         inner_scope = inner_scope->parent;
     }
@@ -2070,11 +2079,12 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
 
     ir_set_cursor_at_end(irb, body_block);
 
-    irb->break_block_stack.append(end_block);
-    irb->continue_block_stack.append(continue_block);
+    LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
+    loop_stack_item->break_block = end_block;
+    loop_stack_item->continue_block = continue_block;
+    loop_stack_item->is_inline = is_inline;
     ir_gen_node(irb, node->data.while_expr.body, scope);
-    irb->break_block_stack.pop();
-    irb->continue_block_stack.pop();
+    irb->loop_stack.pop();
 
     ir_build_br(irb, scope, node, continue_block, is_inline);
     ir_set_cursor_at_end(irb, end_block);
@@ -2096,9 +2106,11 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
     }
     assert(elem_node->type == NodeTypeSymbol);
 
-    IrInstruction *array_val = ir_gen_node(irb, array_node, parent_scope);
-    if (array_val == irb->codegen->invalid_instruction)
-        return array_val;
+    IrInstruction *array_val_ptr = ir_gen_node_extra(irb, array_node, parent_scope, LValPurposeAddressOf);
+    if (array_val_ptr == irb->codegen->invalid_instruction)
+        return array_val_ptr;
+
+    IrInstruction *array_val = ir_build_load_ptr(irb, parent_scope, array_node, array_val_ptr);
 
     IrInstruction *array_type = ir_build_typeof(irb, parent_scope, array_node, array_val);
     IrInstruction *pointer_type = ir_build_to_ptr_type(irb, parent_scope, array_node, array_type);
@@ -2155,7 +2167,7 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
     ir_build_cond_br(irb, child_scope, node, cond, body_block, end_block, is_inline);
 
     ir_set_cursor_at_end(irb, body_block);
-    IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val, index_val, true);
+    IrInstruction *elem_ptr = ir_build_elem_ptr(irb, child_scope, node, array_val_ptr, index_val, true);
     IrInstruction *elem_val;
     if (node->data.for_expr.elem_is_ptr) {
         elem_val = elem_ptr;
@@ -2164,11 +2176,12 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
     }
     ir_build_store_ptr(irb, child_scope, node, elem_var_ptr, elem_val);
 
-    irb->break_block_stack.append(end_block);
-    irb->continue_block_stack.append(continue_block);
+    LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
+    loop_stack_item->break_block = end_block;
+    loop_stack_item->continue_block = continue_block;
+    loop_stack_item->is_inline = is_inline;
     ir_gen_node(irb, body_node, child_scope);
-    irb->break_block_stack.pop();
-    irb->continue_block_stack.pop();
+    irb->loop_stack.pop();
 
     ir_build_br(irb, child_scope, node, continue_block, is_inline);
 
@@ -2195,14 +2208,14 @@ static IrInstruction *ir_gen_this_literal(IrBuilder *irb, Scope *scope, AstNode
         return ir_build_const_fn(irb, scope, node, fn_entry);
     }
 
-    if (scope->node->type == NodeTypeContainerDecl) {
+    if (scope->id == ScopeIdDecls) {
         ScopeDecls *decls_scope = (ScopeDecls *)scope;
         TypeTableEntry *container_type = decls_scope->container_type;
         assert(container_type);
         return ir_build_const_type(irb, scope, node, container_type);
     }
 
-    if (scope->node->type == NodeTypeBlock)
+    if (scope->id == ScopeIdBlock)
         return ir_build_const_scope(irb, scope, node, scope);
 
     zig_unreachable();
@@ -2246,8 +2259,7 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n
 
         return ir_build_array_type(irb, scope, node, size_value, child_type);
     } else {
-        IrInstruction *child_type = ir_gen_node_extra(irb, child_type_node,
-                scope, LValPurposeAddressOf);
+        IrInstruction *child_type = ir_gen_node(irb, child_type_node, scope);
         if (child_type == irb->codegen->invalid_instruction)
             return child_type;
 
@@ -2575,7 +2587,7 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
 
 static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name) {
     while (scope) {
-        if (scope->node->type == NodeTypeBlock) {
+        if (scope->id == ScopeIdBlock) {
             ScopeBlock *block_scope = (ScopeBlock *)scope;
             auto entry = block_scope->label_table.maybe_get(name);
             if (entry)
@@ -2589,7 +2601,7 @@ static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name)
 
 static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) {
     while (scope) {
-        if (scope->node->type == NodeTypeBlock)
+        if (scope->id == ScopeIdBlock)
             return (ScopeBlock *)scope;
         scope = scope->parent;
     }
@@ -2637,6 +2649,36 @@ static IrInstruction *ir_gen_goto(IrBuilder *irb, Scope *scope, AstNode *node) {
     return ir_build_unreachable(irb, scope, node);
 }
 
+static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeBreak);
+
+    if (irb->loop_stack.length == 0) {
+        add_node_error(irb->codegen, node,
+            buf_sprintf("'break' expression outside loop"));
+        return irb->codegen->invalid_instruction;
+    }
+
+    bool is_inline = ir_should_inline(irb) || node->data.break_expr.is_inline;
+    LoopStackItem *loop_stack_item = &irb->loop_stack.last();
+    IrBasicBlock *dest_block = loop_stack_item->break_block;
+    return ir_build_br(irb, scope, node, dest_block, is_inline);
+}
+
+static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *scope, AstNode *node) {
+    assert(node->type == NodeTypeContinue);
+
+    if (irb->loop_stack.length == 0) {
+        add_node_error(irb->codegen, node,
+            buf_sprintf("'continue' expression outside loop"));
+        return irb->codegen->invalid_instruction;
+    }
+
+    bool is_inline = ir_should_inline(irb) || node->data.continue_expr.is_inline;
+    LoopStackItem *loop_stack_item = &irb->loop_stack.last();
+    IrBasicBlock *dest_block = loop_stack_item->continue_block;
+    return ir_build_br(irb, scope, node, dest_block, is_inline);
+}
+
 static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LValPurpose lval) {
     if (lval == LValPurposeNone)
         return value;
@@ -2712,11 +2754,13 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
             return ir_lval_wrap(irb, scope, ir_gen_goto(irb, scope, node), lval);
         case NodeTypeTypeLiteral:
             return ir_lval_wrap(irb, scope, ir_gen_type_literal(irb, scope, node), lval);
+        case NodeTypeBreak:
+            return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval);
+        case NodeTypeContinue:
+            return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval);
         case NodeTypeUnwrapErrorExpr:
         case NodeTypeDefer:
         case NodeTypeSliceExpr:
-        case NodeTypeBreak:
-        case NodeTypeContinue:
         case NodeTypeCharLiteral:
         case NodeTypeZeroesLiteral:
         case NodeTypeErrorType:
@@ -7704,87 +7748,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //    }
 //}
 //
-//static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node)
-//{
-//    assert(node->type == NodeTypeWhileExpr);
-//
-//    AstNode **condition_node = &node->data.while_expr.condition;
-//    AstNode *while_body_node = node->data.while_expr.body;
-//    AstNode **continue_expr_node = &node->data.while_expr.continue_expr;
-//
-//    TypeTableEntry *condition_type = analyze_expression(g, import, context,
-//            g->builtin_types.entry_bool, *condition_node);
-//
-//    if (*continue_expr_node) {
-//        analyze_expression(g, import, context, g->builtin_types.entry_void, *continue_expr_node);
-//    }
-//
-//    BlockContext *child_context = new_block_context(node, context);
-//    child_context->parent_loop_node = node;
-//
-//    analyze_expression(g, import, child_context, g->builtin_types.entry_void, while_body_node);
-//
-//
-//    TypeTableEntry *expr_return_type = g->builtin_types.entry_void;
-//
-//    if (condition_type->id == TypeTableEntryIdInvalid) {
-//        expr_return_type = g->builtin_types.entry_invalid;
-//    } else {
-//        // if the condition is a simple constant expression and there are no break statements
-//        // then the return type is unreachable
-//        ConstExprValue *const_val = &get_resolved_expr(*condition_node)->const_val;
-//        if (const_val->ok) {
-//            if (const_val->data.x_bool) {
-//                node->data.while_expr.condition_always_true = true;
-//                if (!node->data.while_expr.contains_break) {
-//                    expr_return_type = g->builtin_types.entry_unreachable;
-//                }
-//            }
-//        }
-//    }
-//
-//    return expr_return_type;
-//}
-//
-//static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *node)
-//{
-//    assert(node->type == NodeTypeBreak);
-//
-//    AstNode *loop_node = context->parent_loop_node;
-//    if (loop_node) {
-//        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"));
-//    }
-//    return g->builtin_types.entry_unreachable;
-//}
-//
-//static TypeTableEntry *analyze_continue_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
-//        TypeTableEntry *expected_type, AstNode *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;
-//}
-//
 //static TypeTableEntry *analyze_defer(CodeGen *g, ImportTableEntry *import, BlockContext *parent_context,
 //        TypeTableEntry *expected_type, AstNode *node)
 //{
@@ -8592,22 +8555,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
 //    }
 //}
 //
-//static LLVMValueRef gen_break(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeBreak);
-//    LLVMBasicBlockRef dest_block = g->break_block_stack.last();
-//
-//    set_debug_source_node(g, node);
-//    return LLVMBuildBr(g->builder, dest_block);
-//}
-
-//static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) {
-//    assert(node->type == NodeTypeContinue);
-//    LLVMBasicBlockRef dest_block = g->continue_block_stack.last();
-//
-//    set_debug_source_node(g, node);
-//    return LLVMBuildBr(g->builder, dest_block);
-//}
-//
 //static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl,
 //        bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type, bool var_is_ptr)
 //{
src/ir_print.cpp
@@ -104,7 +104,7 @@ static void ir_print_const_value(IrPrint *irp, TypeTableEntry *type_entry, Const
             }
         case TypeTableEntryIdBlock:
             {
-                AstNode *node = const_val->data.x_block->node;
+                AstNode *node = const_val->data.x_block->source_node;
                 fprintf(irp->f, "(scope:%zu:%zu)", node->line + 1, node->column + 1);
                 return;
             }
test/self_hosted2.zig
@@ -139,6 +139,20 @@ fn testFnWithInlineArgs() {
 }
 
 
+fn testContinueInForLoop() {
+    const array = []i32 {1, 2, 3, 4, 5};
+    var sum : i32 = 0;
+    for (array) |x| {
+        sum += x;
+        if (x < 3) {
+            continue;
+        }
+        break;
+    }
+    assert(sum == 6);
+}
+
+
 fn assert(ok: bool) {
     if (!ok)
         @unreachable();
@@ -158,6 +172,7 @@ fn runAllTests() {
     testCompileTimeFib();
     testCompileTimeGenericEval();
     testFnWithInlineArgs();
+    testContinueInForLoop();
 }
 
 export nakedcc fn _start() -> unreachable {