authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-05-21 10:41:57-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-05-21 10:44:55-04:00
log9f3cca861557dab5c56d4ee5a35acd93180ef862
tree09187b849588dccd412f4c5f43276be35288cba9
parent1c6f415a6416f8b29897393b8adfef45e1a2b51f

add error for break/continue exiting defer expression

See #284

6 files changed, 145 insertions(+), 74 deletions(-)

src/all_types.hpp+6-1
......@@ -1636,9 +1636,14 @@ struct ScopeCImport {
16361636// This scope is created for a loop such as for or while in order to
16371637// make break and continue statements work.
16381638// NodeTypeForExpr or NodeTypeWhileExpr
1639// TODO I think we can get rid of this
16401639struct ScopeLoop {
16411640 Scope base;
1641
1642 IrBasicBlock *break_block;
1643 IrBasicBlock *continue_block;
1644 IrInstruction *is_comptime;
1645 ZigList<IrInstruction *> *incoming_values;
1646 ZigList<IrBasicBlock *> *incoming_blocks;
16421647};
16431648
16441649// This scope is created for a comptime expression.
src/analyze.cpp+2-2
......@@ -122,11 +122,11 @@ ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent) {
122122 return scope;
123123}
124124
125Scope *create_loop_scope(AstNode *node, Scope *parent) {
125ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
126126 assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr);
127127 ScopeLoop *scope = allocate<ScopeLoop>(1);
128128 init_scope(&scope->base, ScopeIdLoop, node, parent);
129 return &scope->base;
129 return scope;
130130}
131131
132132ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {
src/analyze.hpp+1-1
......@@ -97,7 +97,7 @@ ScopeDefer *create_defer_scope(AstNode *node, Scope *parent);
9797ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);
9898Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var);
9999ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);
100Scope *create_loop_scope(AstNode *node, Scope *parent);
100ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);
101101ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry);
102102ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import);
103103Scope *create_comptime_scope(AstNode *node, Scope *parent);
src/ir.cpp+95-67
......@@ -19,19 +19,10 @@ struct IrExecContext {
1919 size_t mem_slot_count;
2020};
2121
22struct LoopStackItem {
23 IrBasicBlock *break_block;
24 IrBasicBlock *continue_block;
25 IrInstruction *is_comptime;
26 ZigList<IrInstruction *> *incoming_values;
27 ZigList<IrBasicBlock *> *incoming_blocks;
28};
29
3022struct IrBuilder {
3123 CodeGen *codegen;
3224 IrExecutable *exec;
3325 IrBasicBlock *current_basic_block;
34 ZigList<LoopStackItem> loop_stack;
3526};
3627
3728struct IrAnalyze {
......@@ -59,18 +50,6 @@ static IrInstruction *ir_gen_node_extra(IrBuilder *irb, AstNode *node, Scope *sc
5950static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *instruction);
6051static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type);
6152
62static LoopStackItem *add_loop_stack_item(IrBuilder *irb, IrBasicBlock *break_block, IrBasicBlock *continue_block,
63 IrInstruction *is_comptime, ZigList<IrBasicBlock *> *incoming_blocks, ZigList<IrInstruction *> *incoming_values)
64{
65 LoopStackItem *loop_stack_item = irb->loop_stack.add_one();
66 loop_stack_item->break_block = break_block;
67 loop_stack_item->continue_block = continue_block;
68 loop_stack_item->is_comptime = is_comptime;
69 loop_stack_item->incoming_blocks = incoming_blocks;
70 loop_stack_item->incoming_values = incoming_values;
71 return loop_stack_item;
72}
73
7453ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
7554 assert(const_val->type->id == TypeTableEntryIdPointer);
7655 assert(const_val->special == ConstValSpecialStatic);
......@@ -4748,13 +4727,20 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
47484727 var_ptr_value : ir_build_load_ptr(irb, payload_scope, symbol_node, var_ptr_value);
47494728 ir_build_var_decl(irb, payload_scope, symbol_node, payload_var, nullptr, var_value);
47504729 }
4730
47514731 ZigList<IrInstruction *> incoming_values = {0};
47524732 ZigList<IrBasicBlock *> incoming_blocks = {0};
4753 add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
4754 IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, payload_scope);
4733
4734 ScopeLoop *loop_scope = create_loop_scope(node, payload_scope);
4735 loop_scope->break_block = end_block;
4736 loop_scope->continue_block = continue_block;
4737 loop_scope->is_comptime = is_comptime;
4738 loop_scope->incoming_blocks = &incoming_blocks;
4739 loop_scope->incoming_values = &incoming_values;
4740
4741 IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
47554742 if (body_result == irb->codegen->invalid_instruction)
47564743 return body_result;
4757 irb->loop_stack.pop();
47584744
47594745 if (!instr_is_unreachable(body_result))
47604746 ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime));
......@@ -4821,13 +4807,20 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
48214807 IrInstruction *var_value = node->data.while_expr.var_is_ptr ?
48224808 var_ptr_value : ir_build_load_ptr(irb, child_scope, symbol_node, var_ptr_value);
48234809 ir_build_var_decl(irb, child_scope, symbol_node, payload_var, nullptr, var_value);
4810
48244811 ZigList<IrInstruction *> incoming_values = {0};
48254812 ZigList<IrBasicBlock *> incoming_blocks = {0};
4826 add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
4827 IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, child_scope);
4813
4814 ScopeLoop *loop_scope = create_loop_scope(node, child_scope);
4815 loop_scope->break_block = end_block;
4816 loop_scope->continue_block = continue_block;
4817 loop_scope->is_comptime = is_comptime;
4818 loop_scope->incoming_blocks = &incoming_blocks;
4819 loop_scope->incoming_values = &incoming_values;
4820
4821 IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
48284822 if (body_result == irb->codegen->invalid_instruction)
48294823 return body_result;
4830 irb->loop_stack.pop();
48314824
48324825 if (!instr_is_unreachable(body_result))
48334826 ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
......@@ -4887,11 +4880,17 @@ static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *n
48874880
48884881 ZigList<IrInstruction *> incoming_values = {0};
48894882 ZigList<IrBasicBlock *> incoming_blocks = {0};
4890 add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
4891 IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, scope);
4883
4884 ScopeLoop *loop_scope = create_loop_scope(node, scope);
4885 loop_scope->break_block = end_block;
4886 loop_scope->continue_block = continue_block;
4887 loop_scope->is_comptime = is_comptime;
4888 loop_scope->incoming_blocks = &incoming_blocks;
4889 loop_scope->incoming_values = &incoming_values;
4890
4891 IrInstruction *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base);
48924892 if (body_result == irb->codegen->invalid_instruction)
48934893 return body_result;
4894 irb->loop_stack.pop();
48954894
48964895 if (!instr_is_unreachable(body_result))
48974896 ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime));
......@@ -4952,12 +4951,10 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
49524951 IrInstruction *is_comptime = ir_build_const_bool(irb, parent_scope, node,
49534952 ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline);
49544953
4955 Scope *child_scope = create_loop_scope(node, parent_scope);
4956
49574954 // TODO make it an error to write to element variable or i variable.
49584955 Buf *elem_var_name = elem_node->data.symbol_expr.symbol;
4959 VariableTableEntry *elem_var = ir_create_var(irb, elem_node, child_scope, elem_var_name, true, false, false, is_comptime);
4960 child_scope = elem_var->child_scope;
4956 VariableTableEntry *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime);
4957 Scope *child_scope = elem_var->child_scope;
49614958
49624959 IrInstruction *undefined_value = ir_build_const_undefined(irb, child_scope, elem_node);
49634960 ir_build_var_decl(irb, child_scope, elem_node, elem_var, elem_var_type, undefined_value);
......@@ -5010,9 +5007,14 @@ static IrInstruction *ir_gen_for_expr(IrBuilder *irb, Scope *parent_scope, AstNo
50105007
50115008 ZigList<IrInstruction *> incoming_values = {0};
50125009 ZigList<IrBasicBlock *> incoming_blocks = {0};
5013 add_loop_stack_item(irb, end_block, continue_block, is_comptime, &incoming_blocks, &incoming_values);
5014 IrInstruction *body_result = ir_gen_node(irb, body_node, child_scope);
5015 irb->loop_stack.pop();
5010 ScopeLoop *loop_scope = create_loop_scope(node, child_scope);
5011 loop_scope->break_block = end_block;
5012 loop_scope->continue_block = continue_block;
5013 loop_scope->is_comptime = is_comptime;
5014 loop_scope->incoming_blocks = &incoming_blocks;
5015 loop_scope->incoming_values = &incoming_values;
5016
5017 IrInstruction *body_result = ir_gen_node(irb, body_node, &loop_scope->base);
50165018
50175019 if (!instr_is_unreachable(body_result))
50185020 ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime));
......@@ -5584,62 +5586,88 @@ static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNo
55845586 return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval);
55855587}
55865588
5587static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *scope, AstNode *node) {
5589static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
55885590 assert(node->type == NodeTypeBreak);
55895591
5590 if (irb->loop_stack.length == 0) {
5591 add_node_error(irb->codegen, node,
5592 buf_sprintf("'break' expression outside loop"));
5593 return irb->codegen->invalid_instruction;
5594 }
5592 // Search up the scope. We'll find one of these things first:
5593 // * function definition scope or global scope => error, break outside loop
5594 // * defer expression scope => error, cannot break out of defer expression
5595 // * loop scope => OK
55955596
5596 LoopStackItem *loop_stack_item = &irb->loop_stack.last();
5597 Scope *search_scope = break_scope;
5598 ScopeLoop *loop_scope;
5599 for (;;) {
5600 if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
5601 add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
5602 return irb->codegen->invalid_instruction;
5603 } else if (search_scope->id == ScopeIdDeferExpr) {
5604 add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression"));
5605 return irb->codegen->invalid_instruction;
5606 } else if (search_scope->id == ScopeIdLoop) {
5607 loop_scope = (ScopeLoop *)search_scope;
5608 break;
5609 }
5610 search_scope = search_scope->parent;
5611 }
55975612
55985613 IrInstruction *is_comptime;
5599 if (ir_should_inline(irb->exec, scope)) {
5600 is_comptime = ir_build_const_bool(irb, scope, node, true);
5614 if (ir_should_inline(irb->exec, break_scope)) {
5615 is_comptime = ir_build_const_bool(irb, break_scope, node, true);
56015616 } else {
5602 is_comptime = loop_stack_item->is_comptime;
5617 is_comptime = loop_scope->is_comptime;
56035618 }
56045619
56055620 IrInstruction *result_value;
56065621 if (node->data.break_expr.expr) {
5607 result_value = ir_gen_node(irb, node->data.break_expr.expr, scope);
5622 result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope);
56085623 if (result_value == irb->codegen->invalid_instruction)
56095624 return irb->codegen->invalid_instruction;
56105625 } else {
5611 result_value = ir_build_const_void(irb, scope, node);
5626 result_value = ir_build_const_void(irb, break_scope, node);
56125627 }
56135628
5614 IrBasicBlock *dest_block = loop_stack_item->break_block;
5615 ir_gen_defers_for_block(irb, scope, dest_block->scope, false);
5629 IrBasicBlock *dest_block = loop_scope->break_block;
5630 ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
56165631
5617 loop_stack_item->incoming_blocks->append(irb->current_basic_block);
5618 loop_stack_item->incoming_values->append(result_value);
5619 return ir_build_br(irb, scope, node, dest_block, is_comptime);
5632 loop_scope->incoming_blocks->append(irb->current_basic_block);
5633 loop_scope->incoming_values->append(result_value);
5634 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
56205635}
56215636
5622static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *scope, AstNode *node) {
5637static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, AstNode *node) {
56235638 assert(node->type == NodeTypeContinue);
56245639
5625 if (irb->loop_stack.length == 0) {
5626 add_node_error(irb->codegen, node,
5627 buf_sprintf("'continue' expression outside loop"));
5628 return irb->codegen->invalid_instruction;
5629 }
5640 // Search up the scope. We'll find one of these things first:
5641 // * function definition scope or global scope => error, break outside loop
5642 // * defer expression scope => error, cannot break out of defer expression
5643 // * loop scope => OK
56305644
5631 LoopStackItem *loop_stack_item = &irb->loop_stack.last();
5645 Scope *search_scope = continue_scope;
5646 ScopeLoop *loop_scope;
5647 for (;;) {
5648 if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
5649 add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
5650 return irb->codegen->invalid_instruction;
5651 } else if (search_scope->id == ScopeIdDeferExpr) {
5652 add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression"));
5653 return irb->codegen->invalid_instruction;
5654 } else if (search_scope->id == ScopeIdLoop) {
5655 loop_scope = (ScopeLoop *)search_scope;
5656 break;
5657 }
5658 search_scope = search_scope->parent;
5659 }
56325660
56335661 IrInstruction *is_comptime;
5634 if (ir_should_inline(irb->exec, scope)) {
5635 is_comptime = ir_build_const_bool(irb, scope, node, true);
5662 if (ir_should_inline(irb->exec, continue_scope)) {
5663 is_comptime = ir_build_const_bool(irb, continue_scope, node, true);
56365664 } else {
5637 is_comptime = loop_stack_item->is_comptime;
5665 is_comptime = loop_scope->is_comptime;
56385666 }
56395667
5640 IrBasicBlock *dest_block = loop_stack_item->continue_block;
5641 ir_gen_defers_for_block(irb, scope, dest_block->scope, false);
5642 return ir_build_br(irb, scope, node, dest_block, is_comptime);
5668 IrBasicBlock *dest_block = loop_scope->continue_block;
5669 ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, false);
5670 return ir_build_br(irb, continue_scope, node, dest_block, is_comptime);
56435671}
56445672
56455673static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {
test/cases/defer.zig+17-1
......@@ -13,7 +13,7 @@ fn runSomeErrorDefers(x: bool) -> %bool {
1313 return if (x) x else error.FalseNotAllowed;
1414}
1515
16test "mixingNormalAndErrorDefers" {
16test "mixing normal and error defers" {
1717 assert(%%runSomeErrorDefers(true));
1818 assert(result[0] == 'c');
1919 assert(result[1] == 'a');
......@@ -27,3 +27,19 @@ test "mixingNormalAndErrorDefers" {
2727 assert(result[1] == 'b');
2828 assert(result[2] == 'a');
2929}
30
31test "break and continue inside loop inside defer expression" {
32 testBreakContInDefer(10);
33 comptime testBreakContInDefer(10);
34}
35
36fn testBreakContInDefer(x: usize) {
37 defer {
38 var i: usize = 0;
39 while (i < x) : (i += 1) {
40 if (i < 5) continue;
41 if (i == 5) break;
42 }
43 assert(i == 5);
44 };
45}
test/compile_errors.zig+24-2
......@@ -472,13 +472,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
472472 \\export fn f() {
473473 \\ break;
474474 \\}
475 , ".tmp_source.zig:2:5: error: 'break' expression outside loop");
475 , ".tmp_source.zig:2:5: error: break expression outside loop");
476476
477477 cases.add("invalid continue expression",
478478 \\export fn f() {
479479 \\ continue;
480480 \\}
481 , ".tmp_source.zig:2:5: error: 'continue' expression outside loop");
481 , ".tmp_source.zig:2:5: error: continue expression outside loop");
482482
483483 cases.add("invalid maybe type",
484484 \\export fn f() {
......@@ -1860,4 +1860,26 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
18601860 \\}
18611861 ,
18621862 ".tmp_source.zig:2:14: error: array access of non-array type 'type'");
1863
1864 cases.add("cannot break out of defer expression",
1865 \\export fn foo() {
1866 \\ while (true) {
1867 \\ defer {
1868 \\ break;
1869 \\ }
1870 \\ }
1871 \\}
1872 ,
1873 ".tmp_source.zig:4:13: error: cannot break out of defer expression");
1874
1875 cases.add("cannot continue out of defer expression",
1876 \\export fn foo() {
1877 \\ while (true) {
1878 \\ defer {
1879 \\ continue;
1880 \\ }
1881 \\ }
1882 \\}
1883 ,
1884 ".tmp_source.zig:4:13: error: cannot continue out of defer expression");
18631885}