authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-18 22:21:54-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-18 22:21:54-04:00
log06909ceaab8ecb33d1f41049870797a3ae721610
tree921bdc2b695512725e60e028521cef2d386c93da
parentca4341f7ba845e7af3c6f2be52cd60c51ec6d68f

support break in suspend blocks

* you can label suspend blocks * labeled break supports suspend blocks See #803

9 files changed, 136 insertions(+), 13 deletions(-)

doc/langref.html.in+1-1
...@@ -5918,7 +5918,7 @@ Defer(body) = ("defer" | "deferror") body...@@ -5918,7 +5918,7 @@ Defer(body) = ("defer" | "deferror") body
59185918
5919IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))5919IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
59205920
5921SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))5921SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
59225922
5923IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)5923IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
59245924
src/all_types.hpp+13
...@@ -867,6 +867,7 @@ struct AstNodeAwaitExpr {...@@ -867,6 +867,7 @@ struct AstNodeAwaitExpr {
867};867};
868868
869struct AstNodeSuspend {869struct AstNodeSuspend {
870 Buf *name;
870 AstNode *block;871 AstNode *block;
871 AstNode *promise_symbol;872 AstNode *promise_symbol;
872};873};
...@@ -1757,6 +1758,7 @@ enum ScopeId {...@@ -1757,6 +1758,7 @@ enum ScopeId {
1757 ScopeIdVarDecl,1758 ScopeIdVarDecl,
1758 ScopeIdCImport,1759 ScopeIdCImport,
1759 ScopeIdLoop,1760 ScopeIdLoop,
1761 ScopeIdSuspend,
1760 ScopeIdFnDef,1762 ScopeIdFnDef,
1761 ScopeIdCompTime,1763 ScopeIdCompTime,
1762 ScopeIdCoroPrelude,1764 ScopeIdCoroPrelude,
...@@ -1852,6 +1854,17 @@ struct ScopeLoop {...@@ -1852,6 +1854,17 @@ struct ScopeLoop {
1852 ZigList<IrBasicBlock *> *incoming_blocks;1854 ZigList<IrBasicBlock *> *incoming_blocks;
1853};1855};
18541856
1857// This scope is created for a suspend block in order to have labeled
1858// suspend for breaking out of a suspend and for detecting if a suspend
1859// block is inside a suspend block.
1860struct ScopeSuspend {
1861 Scope base;
1862
1863 Buf *name;
1864 IrBasicBlock *resume_block;
1865 bool reported_err;
1866};
1867
1855// This scope is created for a comptime expression.1868// This scope is created for a comptime expression.
1856// NodeTypeCompTime, NodeTypeSwitchExpr1869// NodeTypeCompTime, NodeTypeSwitchExpr
1857struct ScopeCompTime {1870struct ScopeCompTime {
src/analyze.cpp+9
...@@ -156,6 +156,14 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {...@@ -156,6 +156,14 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
156 return scope;156 return scope;
157}157}
158158
159ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {
160 assert(node->type == NodeTypeSuspend);
161 ScopeSuspend *scope = allocate<ScopeSuspend>(1);
162 init_scope(&scope->base, ScopeIdSuspend, node, parent);
163 scope->name = node->data.suspend.name;
164 return scope;
165}
166
159ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {167ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {
160 ScopeFnDef *scope = allocate<ScopeFnDef>(1);168 ScopeFnDef *scope = allocate<ScopeFnDef>(1);
161 init_scope(&scope->base, ScopeIdFnDef, node, parent);169 init_scope(&scope->base, ScopeIdFnDef, node, parent);
...@@ -3616,6 +3624,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {...@@ -3616,6 +3624,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {
3616 case ScopeIdVarDecl:3624 case ScopeIdVarDecl:
3617 case ScopeIdCImport:3625 case ScopeIdCImport:
3618 case ScopeIdLoop:3626 case ScopeIdLoop:
3627 case ScopeIdSuspend:
3619 case ScopeIdCompTime:3628 case ScopeIdCompTime:
3620 case ScopeIdCoroPrelude:3629 case ScopeIdCoroPrelude:
3621 scope = scope->parent;3630 scope = scope->parent;
src/analyze.hpp+1
...@@ -104,6 +104,7 @@ ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);...@@ -104,6 +104,7 @@ ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);
104Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var);104Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var);
105ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);105ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);
106ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);106ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);
107ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent);
107ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry);108ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry);
108ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import);109ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import);
109Scope *create_comptime_scope(AstNode *node, Scope *parent);110Scope *create_comptime_scope(AstNode *node, Scope *parent);
src/codegen.cpp+1
...@@ -654,6 +654,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {...@@ -654,6 +654,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
654 }654 }
655 case ScopeIdDeferExpr:655 case ScopeIdDeferExpr:
656 case ScopeIdLoop:656 case ScopeIdLoop:
657 case ScopeIdSuspend:
657 case ScopeIdCompTime:658 case ScopeIdCompTime:
658 case ScopeIdCoroPrelude:659 case ScopeIdCoroPrelude:
659 return get_di_scope(g, scope->parent);660 return get_di_scope(g, scope->parent);
src/ir.cpp+50-10
...@@ -2829,6 +2829,18 @@ static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock *...@@ -2829,6 +2829,18 @@ static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock *
2829 ir_set_cursor_at_end(irb, basic_block);2829 ir_set_cursor_at_end(irb, basic_block);
2830}2830}
28312831
2832static ScopeSuspend *get_scope_suspend(Scope *scope) {
2833 while (scope) {
2834 if (scope->id == ScopeIdSuspend)
2835 return (ScopeSuspend *)scope;
2836 if (scope->id == ScopeIdFnDef)
2837 return nullptr;
2838
2839 scope = scope->parent;
2840 }
2841 return nullptr;
2842}
2843
2832static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {2844static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
2833 while (scope) {2845 while (scope) {
2834 if (scope->id == ScopeIdDeferExpr)2846 if (scope->id == ScopeIdDeferExpr)
...@@ -5665,6 +5677,15 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop...@@ -5665,6 +5677,15 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop
5665 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);5677 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
5666}5678}
56675679
5680static IrInstruction *ir_gen_break_from_suspend(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeSuspend *suspend_scope) {
5681 IrInstruction *is_comptime = ir_build_const_bool(irb, break_scope, node, false);
5682
5683 IrBasicBlock *dest_block = suspend_scope->resume_block;
5684 ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
5685
5686 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
5687}
5688
5668static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {5689static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
5669 assert(node->type == NodeTypeBreak);5690 assert(node->type == NodeTypeBreak);
56705691
...@@ -5704,6 +5725,13 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *...@@ -5704,6 +5725,13 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
5704 assert(this_block_scope->end_block != nullptr);5725 assert(this_block_scope->end_block != nullptr);
5705 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);5726 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
5706 }5727 }
5728 } else if (search_scope->id == ScopeIdSuspend) {
5729 ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope;
5730 if (node->data.break_expr.name != nullptr &&
5731 (this_suspend_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_suspend_scope->name)))
5732 {
5733 return ir_gen_break_from_suspend(irb, break_scope, node, this_suspend_scope);
5734 }
5707 }5735 }
5708 search_scope = search_scope->parent;5736 search_scope = search_scope->parent;
5709 }5737 }
...@@ -6290,14 +6318,26 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6290,14 +6318,26 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
6290 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);6318 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);
6291 if (scope_defer_expr) {6319 if (scope_defer_expr) {
6292 if (!scope_defer_expr->reported_err) {6320 if (!scope_defer_expr->reported_err) {
6293 add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression"));6321 ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression"));
6322 add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here"));
6294 scope_defer_expr->reported_err = true;6323 scope_defer_expr->reported_err = true;
6295 }6324 }
6296 return irb->codegen->invalid_instruction;6325 return irb->codegen->invalid_instruction;
6297 }6326 }
6327 ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope);
6328 if (existing_suspend_scope) {
6329 if (!existing_suspend_scope->reported_err) {
6330 ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block"));
6331 add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here"));
6332 existing_suspend_scope->reported_err = true;
6333 }
6334 return irb->codegen->invalid_instruction;
6335 }
62986336
6299 Scope *outer_scope = irb->exec->begin_scope;6337 Scope *outer_scope = irb->exec->begin_scope;
63006338
6339 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
6340 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
63016341
6302 IrInstruction *suspend_code;6342 IrInstruction *suspend_code;
6303 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);6343 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);
...@@ -6316,28 +6356,28 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6316,28 +6356,28 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
6316 } else {6356 } else {
6317 child_scope = parent_scope;6357 child_scope = parent_scope;
6318 }6358 }
6359 ScopeSuspend *suspend_scope = create_suspend_scope(node, child_scope);
6360 suspend_scope->resume_block = resume_block;
6361 child_scope = &suspend_scope->base;
6319 IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);6362 IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);
6320 ir_gen_node(irb, node->data.suspend.block, child_scope);6363 ir_gen_node(irb, node->data.suspend.block, child_scope);
6321 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false);6364 suspend_code = ir_mark_gen(ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false));
6322 }6365 }
63236366
6324 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
6325 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
6326
6327 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);6367 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
6328 cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0);6368 cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));
6329 cases[0].block = resume_block;6369 cases[0].block = resume_block;
6330 cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1);6370 cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));
6331 cases[1].block = cleanup_block;6371 cases[1].block = cleanup_block;
6332 ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,6372 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
6333 2, cases, const_bool_false);6373 2, cases, const_bool_false));
63346374
6335 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);6375 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
6336 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);6376 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
6337 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));6377 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
63386378
6339 ir_set_cursor_at_end_and_append_block(irb, resume_block);6379 ir_set_cursor_at_end_and_append_block(irb, resume_block);
6340 return ir_build_const_void(irb, parent_scope, node);6380 return ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
6341}6381}
63426382
6343static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,6383static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
src/parser.cpp+23-2
...@@ -648,12 +648,30 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m...@@ -648,12 +648,30 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
648}648}
649649
650/*650/*
651SuspendExpression(body) = "suspend" "|" Symbol "|" body651SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
652*/652*/
653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
654 size_t orig_token_index = *token_index;654 size_t orig_token_index = *token_index;
655655
656 Token *suspend_token = &pc->tokens->at(*token_index);656 Token *name_token = nullptr;
657 Token *token = &pc->tokens->at(*token_index);
658 if (token->id == TokenIdSymbol) {
659 *token_index += 1;
660 Token *colon_token = &pc->tokens->at(*token_index);
661 if (colon_token->id == TokenIdColon) {
662 *token_index += 1;
663 name_token = token;
664 token = &pc->tokens->at(*token_index);
665 } else if (mandatory) {
666 ast_expect_token(pc, colon_token, TokenIdColon);
667 zig_unreachable();
668 } else {
669 *token_index = orig_token_index;
670 return nullptr;
671 }
672 }
673
674 Token *suspend_token = token;
657 if (suspend_token->id == TokenIdKeywordSuspend) {675 if (suspend_token->id == TokenIdKeywordSuspend) {
658 *token_index += 1;676 *token_index += 1;
659 } else if (mandatory) {677 } else if (mandatory) {
...@@ -675,6 +693,9 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b...@@ -675,6 +693,9 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
675 }693 }
676694
677 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);695 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
696 if (name_token != nullptr) {
697 node->data.suspend.name = token_buf(name_token);
698 }
678 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);699 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
679 ast_eat_token(pc, token_index, TokenIdBinOr);700 ast_eat_token(pc, token_index, TokenIdBinOr);
680 node->data.suspend.block = ast_parse_block(pc, token_index, true);701 node->data.suspend.block = ast_parse_block(pc, token_index, true);
test/cases/coroutines.zig+18
...@@ -224,3 +224,21 @@ async fn printTrace(p: promise->error!void) void {...@@ -224,3 +224,21 @@ async fn printTrace(p: promise->error!void) void {
224 }224 }
225 };225 };
226}226}
227
228test "break from suspend" {
229 var buf: [500]u8 = undefined;
230 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
231 var my_result: i32 = 1;
232 const p = try async<a> testBreakFromSuspend(&my_result);
233 cancel p;
234 std.debug.assert(my_result == 2);
235}
236
237async fn testBreakFromSuspend(my_result: &i32) void {
238 s: suspend |p| {
239 break :s;
240 }
241 *my_result += 1;
242 suspend;
243 *my_result += 1;
244}
test/compile_errors.zig+20
...@@ -1,6 +1,26 @@...@@ -1,6 +1,26 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: &tests.CompileErrorContext) void {3pub fn addCases(cases: &tests.CompileErrorContext) void {
4 cases.add("suspend inside suspend block",
5 \\const std = @import("std");
6 \\
7 \\export fn entry() void {
8 \\ var buf: [500]u8 = undefined;
9 \\ var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
10 \\ const p = (async<a> foo()) catch unreachable;
11 \\ cancel p;
12 \\}
13 \\
14 \\async fn foo() void {
15 \\ suspend |p| {
16 \\ suspend |p1| {
17 \\ }
18 \\ }
19 \\}
20 ,
21 ".tmp_source.zig:12:9: error: cannot suspend inside suspend block",
22 ".tmp_source.zig:11:5: note: other suspend block here");
23
4 cases.add("assign inline fn to non-comptime var",24 cases.add("assign inline fn to non-comptime var",
5 \\export fn entry() void {25 \\export fn entry() void {
6 \\ var a = b;26 \\ var a = b;