authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-02 13:37:24-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-02 13:37:24-04:00
log44fd3045ce85abb3e10dc0ad70d195b7d8044c43
treea5b027f354a392cf360a69a9049311320fdf08ba
parent9ecbabfc4ce857e43db2b056bc83272cb24b0bbd
parent96a94e7da933dafec25356c435f5725c3cb0ce04

Merge branch 'handle-builtin-issue1296' of https://github.com/kristate/zig into kristate-handle-builtin-issue1296


18 files changed, 204 insertions(+), 81 deletions(-)

doc/langref.html.in+18-8
...@@ -4690,9 +4690,9 @@ test "coroutine suspend with block" {...@@ -4690,9 +4690,9 @@ test "coroutine suspend with block" {
4690var a_promise: promise = undefined;4690var a_promise: promise = undefined;
4691var result = false;4691var result = false;
4692async fn testSuspendBlock() void {4692async fn testSuspendBlock() void {
4693 suspend |p| {4693 suspend {
4694 comptime assert(@typeOf(p) == promise->void);4694 comptime assert(@typeOf(@handle()) == promise->void);
4695 a_promise = p;4695 a_promise = @handle();
4696 }4696 }
4697 result = true;4697 result = true;
4698}4698}
...@@ -4733,8 +4733,8 @@ test "resume from suspend" {...@@ -4733,8 +4733,8 @@ test "resume from suspend" {
4733 std.debug.assert(my_result == 2);4733 std.debug.assert(my_result == 2);
4734}4734}
4735async fn testResumeFromSuspend(my_result: *i32) void {4735async fn testResumeFromSuspend(my_result: *i32) void {
4736 suspend |p| {4736 suspend {
4737 resume p;4737 resume @handle();
4738 }4738 }
4739 my_result.* += 1;4739 my_result.* += 1;
4740 suspend;4740 suspend;
...@@ -4791,9 +4791,9 @@ async fn amain() void {...@@ -4791,9 +4791,9 @@ async fn amain() void {
4791}4791}
4792async fn another() i32 {4792async fn another() i32 {
4793 seq('c');4793 seq('c');
4794 suspend |p| {4794 suspend {
4795 seq('d');4795 seq('d');
4796 a_promise = p;4796 a_promise = @handle();
4797 }4797 }
4798 seq('g');4798 seq('g');
4799 return 1234;4799 return 1234;
...@@ -5383,6 +5383,16 @@ test "main" {...@@ -5383,6 +5383,16 @@ test "main" {
5383 This function is only valid within function scope.5383 This function is only valid within function scope.
5384 </p>5384 </p>
5385 {#header_close#}5385 {#header_close#}
5386 {#header_open|@handle#}
5387 <pre><code class="zig">@handle()</code></pre>
5388 <p>
5389 This function returns a <code>promise->T</code> type, where <code>T</code>
5390 is the return type of the async function in scope.
5391 </p>
5392 <p>
5393 This function is only valid within an async function scope.
5394 </p>
5395 {#header_close#}
5386 {#header_open|@import#}5396 {#header_open|@import#}
5387 <pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre>5397 <pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre>
5388 <p>5398 <p>
...@@ -7388,7 +7398,7 @@ Defer(body) = ("defer" | "deferror") body...@@ -7388,7 +7398,7 @@ Defer(body) = ("defer" | "deferror") body
73887398
7389IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))7399IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
73907400
7391SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))7401SuspendExpression(body) = "suspend" option( body )
73927402
7393IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)7403IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
73947404
src/all_types.hpp+7-1
...@@ -899,7 +899,6 @@ struct AstNodeAwaitExpr {...@@ -899,7 +899,6 @@ struct AstNodeAwaitExpr {
899899
900struct AstNodeSuspend {900struct AstNodeSuspend {
901 AstNode *block;901 AstNode *block;
902 AstNode *promise_symbol;
903};902};
904903
905struct AstNodePromiseType {904struct AstNodePromiseType {
...@@ -1358,6 +1357,7 @@ enum BuiltinFnId {...@@ -1358,6 +1357,7 @@ enum BuiltinFnId {
1358 BuiltinFnIdBreakpoint,1357 BuiltinFnIdBreakpoint,
1359 BuiltinFnIdReturnAddress,1358 BuiltinFnIdReturnAddress,
1360 BuiltinFnIdFrameAddress,1359 BuiltinFnIdFrameAddress,
1360 BuiltinFnIdHandle,
1361 BuiltinFnIdEmbedFile,1361 BuiltinFnIdEmbedFile,
1362 BuiltinFnIdCmpxchgWeak,1362 BuiltinFnIdCmpxchgWeak,
1363 BuiltinFnIdCmpxchgStrong,1363 BuiltinFnIdCmpxchgStrong,
...@@ -1716,6 +1716,7 @@ struct CodeGen {...@@ -1716,6 +1716,7 @@ struct CodeGen {
1716 LLVMValueRef coro_save_fn_val;1716 LLVMValueRef coro_save_fn_val;
1717 LLVMValueRef coro_promise_fn_val;1717 LLVMValueRef coro_promise_fn_val;
1718 LLVMValueRef coro_alloc_helper_fn_val;1718 LLVMValueRef coro_alloc_helper_fn_val;
1719 LLVMValueRef coro_frame_fn_val;
1719 LLVMValueRef merge_err_ret_traces_fn_val;1720 LLVMValueRef merge_err_ret_traces_fn_val;
1720 LLVMValueRef add_error_return_trace_addr_fn_val;1721 LLVMValueRef add_error_return_trace_addr_fn_val;
1721 LLVMValueRef stacksave_fn_val;1722 LLVMValueRef stacksave_fn_val;
...@@ -2076,6 +2077,7 @@ enum IrInstructionId {...@@ -2076,6 +2077,7 @@ enum IrInstructionId {
2076 IrInstructionIdBreakpoint,2077 IrInstructionIdBreakpoint,
2077 IrInstructionIdReturnAddress,2078 IrInstructionIdReturnAddress,
2078 IrInstructionIdFrameAddress,2079 IrInstructionIdFrameAddress,
2080 IrInstructionIdHandle,
2079 IrInstructionIdAlignOf,2081 IrInstructionIdAlignOf,
2080 IrInstructionIdOverflowOp,2082 IrInstructionIdOverflowOp,
2081 IrInstructionIdTestErr,2083 IrInstructionIdTestErr,
...@@ -2793,6 +2795,10 @@ struct IrInstructionFrameAddress {...@@ -2793,6 +2795,10 @@ struct IrInstructionFrameAddress {
2793 IrInstruction base;2795 IrInstruction base;
2794};2796};
27952797
2798struct IrInstructionHandle {
2799 IrInstruction base;
2800};
2801
2796enum IrOverflowOp {2802enum IrOverflowOp {
2797 IrOverflowOpAdd,2803 IrOverflowOpAdd,
2798 IrOverflowOpSub,2804 IrOverflowOpSub,
src/ast_render.cpp-3
...@@ -1112,9 +1112,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -1112,9 +1112,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
1112 {1112 {
1113 fprintf(ar->f, "suspend");1113 fprintf(ar->f, "suspend");
1114 if (node->data.suspend.block != nullptr) {1114 if (node->data.suspend.block != nullptr) {
1115 fprintf(ar->f, " |");
1116 render_node_grouped(ar, node->data.suspend.promise_symbol);
1117 fprintf(ar->f, "| ");
1118 render_node_grouped(ar, node->data.suspend.block);1115 render_node_grouped(ar, node->data.suspend.block);
1119 }1116 }
1120 break;1117 break;
src/codegen.cpp+24
...@@ -4146,6 +4146,26 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable...@@ -4146,6 +4146,26 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable
4146 return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, "");4146 return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, "");
4147}4147}
41484148
4149static LLVMValueRef get_handle_fn_val(CodeGen *g) {
4150 if (g->coro_frame_fn_val)
4151 return g->coro_frame_fn_val;
4152
4153 LLVMTypeRef fn_type = LLVMFunctionType( LLVMPointerType(LLVMInt8Type(), 0)
4154 , nullptr, 0, false);
4155 Buf *name = buf_sprintf("llvm.coro.frame");
4156 g->coro_frame_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type);
4157 assert(LLVMGetIntrinsicID(g->coro_frame_fn_val));
4158
4159 return g->coro_frame_fn_val;
4160}
4161
4162static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable,
4163 IrInstructionHandle *instruction)
4164{
4165 LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_promise->type_ref);
4166 return LLVMBuildCall(g->builder, get_handle_fn_val(g), &zero, 0, "");
4167}
4168
4149static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) {4169static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) {
4150 TypeTableEntry *int_type = instruction->result_ptr_type;4170 TypeTableEntry *int_type = instruction->result_ptr_type;
4151 assert(int_type->id == TypeTableEntryIdInt);4171 assert(int_type->id == TypeTableEntryIdInt);
...@@ -4910,6 +4930,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4910,6 +4930,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4910 return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction);4930 return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction);
4911 case IrInstructionIdFrameAddress:4931 case IrInstructionIdFrameAddress:
4912 return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction);4932 return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction);
4933 case IrInstructionIdHandle:
4934 return ir_render_handle(g, executable, (IrInstructionHandle *)instruction);
4913 case IrInstructionIdOverflowOp:4935 case IrInstructionIdOverflowOp:
4914 return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);4936 return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);
4915 case IrInstructionIdTestErr:4937 case IrInstructionIdTestErr:
...@@ -6005,6 +6027,7 @@ static void do_code_gen(CodeGen *g) {...@@ -6005,6 +6027,7 @@ static void do_code_gen(CodeGen *g) {
6005 ir_render(g, fn_table_entry);6027 ir_render(g, fn_table_entry);
60066028
6007 }6029 }
6030
6008 assert(!g->errors.length);6031 assert(!g->errors.length);
60096032
6010 if (buf_len(&g->global_asm) != 0) {6033 if (buf_len(&g->global_asm) != 0) {
...@@ -6344,6 +6367,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -6344,6 +6367,7 @@ static void define_builtin_fns(CodeGen *g) {
6344 create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0);6367 create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0);
6345 create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0);6368 create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0);
6346 create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0);6369 create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0);
6370 create_builtin_fn(g, BuiltinFnIdHandle, "handle", 0);
6347 create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3);6371 create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3);
6348 create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3);6372 create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3);
6349 create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1);6373 create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1);
src/ir.cpp+39-12
...@@ -580,6 +580,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *)...@@ -580,6 +580,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *)
580 return IrInstructionIdFrameAddress;580 return IrInstructionIdFrameAddress;
581}581}
582582
583static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) {
584 return IrInstructionIdHandle;
585}
586
583static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {587static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
584 return IrInstructionIdAlignOf;588 return IrInstructionIdAlignOf;
585}589}
...@@ -2240,6 +2244,17 @@ static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction...@@ -2240,6 +2244,17 @@ static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction
2240 return new_instruction;2244 return new_instruction;
2241}2245}
22422246
2247static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) {
2248 IrInstructionHandle *instruction = ir_build_instruction<IrInstructionHandle>(irb, scope, source_node);
2249 return &instruction->base;
2250}
2251
2252static IrInstruction *ir_build_handle_from(IrBuilder *irb, IrInstruction *old_instruction) {
2253 IrInstruction *new_instruction = ir_build_handle(irb, old_instruction->scope, old_instruction->source_node);
2254 ir_link_new_instruction(new_instruction, old_instruction);
2255 return new_instruction;
2256}
2257
2243static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node,2258static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node,
2244 IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,2259 IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,
2245 IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)2260 IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)
...@@ -3843,6 +3858,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3843,6 +3858,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3843 return irb->codegen->invalid_instruction;3858 return irb->codegen->invalid_instruction;
3844 }3859 }
38453860
3861 bool is_async = exec_is_async(irb->exec);
3862
3846 switch (builtin_fn->id) {3863 switch (builtin_fn->id) {
3847 case BuiltinFnIdInvalid:3864 case BuiltinFnIdInvalid:
3848 zig_unreachable();3865 zig_unreachable();
...@@ -4475,6 +4492,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4475,6 +4492,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4475 return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval);4492 return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval);
4476 case BuiltinFnIdFrameAddress:4493 case BuiltinFnIdFrameAddress:
4477 return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval);4494 return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval);
4495 case BuiltinFnIdHandle:
4496 if (!irb->exec->fn_entry) {
4497 add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition"));
4498 return irb->codegen->invalid_instruction;
4499 }
4500 if (!is_async) {
4501 add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function"));
4502 return irb->codegen->invalid_instruction;
4503 }
4504 return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval);
4478 case BuiltinFnIdAlignOf:4505 case BuiltinFnIdAlignOf:
4479 {4506 {
4480 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4507 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -7069,19 +7096,8 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -7069,19 +7096,8 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
7069 if (node->data.suspend.block == nullptr) {7096 if (node->data.suspend.block == nullptr) {
7070 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);7097 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
7071 } else {7098 } else {
7072 assert(node->data.suspend.promise_symbol != nullptr);
7073 assert(node->data.suspend.promise_symbol->type == NodeTypeSymbol);
7074 Buf *promise_symbol_name = node->data.suspend.promise_symbol->data.symbol_expr.symbol;
7075 Scope *child_scope;7099 Scope *child_scope;
7076 if (!buf_eql_str(promise_symbol_name, "_")) {7100 ScopeSuspend *suspend_scope = create_suspend_scope(node, parent_scope);
7077 VariableTableEntry *promise_var = ir_create_var(irb, node, parent_scope, promise_symbol_name,
7078 true, true, false, const_bool_false);
7079 ir_build_var_decl(irb, parent_scope, node, promise_var, nullptr, nullptr, irb->exec->coro_handle);
7080 child_scope = promise_var->child_scope;
7081 } else {
7082 child_scope = parent_scope;
7083 }
7084 ScopeSuspend *suspend_scope = create_suspend_scope(node, child_scope);
7085 suspend_scope->resume_block = resume_block;7101 suspend_scope->resume_block = resume_block;
7086 child_scope = &suspend_scope->base;7102 child_scope = &suspend_scope->base;
7087 IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);7103 IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);
...@@ -19007,6 +19023,14 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn...@@ -19007,6 +19023,14 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn
19007 return u8_ptr_const;19023 return u8_ptr_const;
19008}19024}
1900919025
19026static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) {
19027 ir_build_handle_from(&ira->new_irb, &instruction->base);
19028
19029 FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
19030 assert(fn_entry != nullptr);
19031 return get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type);
19032}
19033
19010static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {19034static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
19011 IrInstruction *type_value = instruction->type_value->other;19035 IrInstruction *type_value = instruction->type_value->other;
19012 if (type_is_invalid(type_value->value.type))19036 if (type_is_invalid(type_value->value.type))
...@@ -20982,6 +21006,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -20982,6 +21006,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
20982 return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);21006 return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);
20983 case IrInstructionIdFrameAddress:21007 case IrInstructionIdFrameAddress:
20984 return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);21008 return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);
21009 case IrInstructionIdHandle:
21010 return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction);
20985 case IrInstructionIdAlignOf:21011 case IrInstructionIdAlignOf:
20986 return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);21012 return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);
20987 case IrInstructionIdOverflowOp:21013 case IrInstructionIdOverflowOp:
...@@ -21274,6 +21300,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -21274,6 +21300,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
21274 case IrInstructionIdAlignOf:21300 case IrInstructionIdAlignOf:
21275 case IrInstructionIdReturnAddress:21301 case IrInstructionIdReturnAddress:
21276 case IrInstructionIdFrameAddress:21302 case IrInstructionIdFrameAddress:
21303 case IrInstructionIdHandle:
21277 case IrInstructionIdTestErr:21304 case IrInstructionIdTestErr:
21278 case IrInstructionIdUnwrapErrCode:21305 case IrInstructionIdUnwrapErrCode:
21279 case IrInstructionIdOptionalWrap:21306 case IrInstructionIdOptionalWrap:
src/ir_print.cpp+7
...@@ -791,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst...@@ -791,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst
791 fprintf(irp->f, "@frameAddress()");791 fprintf(irp->f, "@frameAddress()");
792}792}
793793
794static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) {
795 fprintf(irp->f, "@handle()");
796}
797
794static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) {798static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) {
795 fprintf(irp->f, "@returnAddress()");799 fprintf(irp->f, "@returnAddress()");
796}800}
...@@ -1556,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1556,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1556 case IrInstructionIdFrameAddress:1560 case IrInstructionIdFrameAddress:
1557 ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction);1561 ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction);
1558 break;1562 break;
1563 case IrInstructionIdHandle:
1564 ir_print_handle(irp, (IrInstructionHandle *)instruction);
1565 break;
1559 case IrInstructionIdAlignOf:1566 case IrInstructionIdAlignOf:
1560 ir_print_align_of(irp, (IrInstructionAlignOf *)instruction);1567 ir_print_align_of(irp, (IrInstructionAlignOf *)instruction);
1561 break;1568 break;
src/parser.cpp+17-16
...@@ -648,35 +648,37 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m...@@ -648,35 +648,37 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
648}648}
649649
650/*650/*
651SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))651SuspendExpression(body) = "suspend" option( 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;
655 Token *token = &pc->tokens->at(*token_index);
656 Token *suspend_token = nullptr;
655657
656 Token *suspend_token = &pc->tokens->at(*token_index);658 if (token->id == TokenIdKeywordSuspend) {
657 if (suspend_token->id == TokenIdKeywordSuspend) {
658 *token_index += 1;659 *token_index += 1;
660 suspend_token = token;
661 token = &pc->tokens->at(*token_index);
659 } else if (mandatory) {662 } else if (mandatory) {
660 ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend);663 ast_expect_token(pc, token, TokenIdKeywordSuspend);
661 zig_unreachable();664 zig_unreachable();
662 } else {665 } else {
663 return nullptr;666 return nullptr;
664 }667 }
665668
666 Token *bar_token = &pc->tokens->at(*token_index);669 //guessing that semicolon is checked elsewhere?
667 if (bar_token->id == TokenIdBinOr) {670 if (token->id != TokenIdLBrace) {
668 *token_index += 1;671 if (mandatory) {
669 } else if (mandatory) {672 ast_expect_token(pc, token, TokenIdLBrace);
670 ast_expect_token(pc, suspend_token, TokenIdBinOr);673 zig_unreachable();
671 zig_unreachable();674 } else {
672 } else {675 *token_index = orig_token_index;
673 *token_index = orig_token_index;676 return nullptr;
674 return nullptr;677 }
675 }678 }
676679
680 //Expect that we have a block;
677 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);681 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
678 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
679 ast_eat_token(pc, token_index, TokenIdBinOr);
680 node->data.suspend.block = ast_parse_block(pc, token_index, true);682 node->data.suspend.block = ast_parse_block(pc, token_index, true);
681683
682 return node;684 return node;
...@@ -3134,7 +3136,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont...@@ -3134,7 +3136,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
3134 visit_field(&node->data.await_expr.expr, visit, context);3136 visit_field(&node->data.await_expr.expr, visit, context);
3135 break;3137 break;
3136 case NodeTypeSuspend:3138 case NodeTypeSuspend:
3137 visit_field(&node->data.suspend.promise_symbol, visit, context);
3138 visit_field(&node->data.suspend.block, visit, context);3139 visit_field(&node->data.suspend.block, visit, context);
3139 break;3140 break;
3140 }3141 }
std/event/channel.zig+4-4
...@@ -71,10 +71,10 @@ pub fn Channel(comptime T: type) type {...@@ -71,10 +71,10 @@ pub fn Channel(comptime T: type) type {
71 /// puts a data item in the channel. The promise completes when the value has been added to the71 /// puts a data item in the channel. The promise completes when the value has been added to the
72 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.72 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
73 pub async fn put(self: *SelfChannel, data: T) void {73 pub async fn put(self: *SelfChannel, data: T) void {
74 suspend |handle| {74 suspend {
75 var my_tick_node = Loop.NextTickNode{75 var my_tick_node = Loop.NextTickNode{
76 .next = undefined,76 .next = undefined,
77 .data = handle,77 .data = @handle(),
78 };78 };
79 var queue_node = std.atomic.Queue(PutNode).Node{79 var queue_node = std.atomic.Queue(PutNode).Node{
80 .data = PutNode{80 .data = PutNode{
...@@ -96,10 +96,10 @@ pub fn Channel(comptime T: type) type {...@@ -96,10 +96,10 @@ pub fn Channel(comptime T: type) type {
96 // TODO integrate this function with named return values96 // TODO integrate this function with named return values
97 // so we can get rid of this extra result copy97 // so we can get rid of this extra result copy
98 var result: T = undefined;98 var result: T = undefined;
99 suspend |handle| {99 suspend {
100 var my_tick_node = Loop.NextTickNode{100 var my_tick_node = Loop.NextTickNode{
101 .next = undefined,101 .next = undefined,
102 .data = handle,102 .data = @handle(),
103 };103 };
104 var queue_node = std.atomic.Queue(GetNode).Node{104 var queue_node = std.atomic.Queue(GetNode).Node{
105 .data = GetNode{105 .data = GetNode{
std/event/future.zig+6-6
...@@ -100,8 +100,8 @@ test "std.event.Future" {...@@ -100,8 +100,8 @@ test "std.event.Future" {
100}100}
101101
102async fn testFuture(loop: *Loop) void {102async fn testFuture(loop: *Loop) void {
103 suspend |p| {103 suspend {
104 resume p;104 resume @handle();
105 }105 }
106 var future = Future(i32).init(loop);106 var future = Future(i32).init(loop);
107107
...@@ -115,15 +115,15 @@ async fn testFuture(loop: *Loop) void {...@@ -115,15 +115,15 @@ async fn testFuture(loop: *Loop) void {
115}115}
116116
117async fn waitOnFuture(future: *Future(i32)) i32 {117async fn waitOnFuture(future: *Future(i32)) i32 {
118 suspend |p| {118 suspend {
119 resume p;119 resume @handle();
120 }120 }
121 return (await (async future.get() catch @panic("memory"))).*;121 return (await (async future.get() catch @panic("memory"))).*;
122}122}
123123
124async fn resolveFuture(future: *Future(i32)) void {124async fn resolveFuture(future: *Future(i32)) void {
125 suspend |p| {125 suspend {
126 resume p;126 resume @handle();
127 }127 }
128 future.data = 6;128 future.data = 6;
129 future.resolve();129 future.resolve();
std/event/group.zig+2-2
...@@ -54,10 +54,10 @@ pub fn Group(comptime ReturnType: type) type {...@@ -54,10 +54,10 @@ pub fn Group(comptime ReturnType: type) type {
54 const S = struct {54 const S = struct {
55 async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType {55 async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType {
56 // TODO this is a hack to make the memory following be inside the coro frame56 // TODO this is a hack to make the memory following be inside the coro frame
57 suspend |p| {57 suspend {
58 var my_node: Stack.Node = undefined;58 var my_node: Stack.Node = undefined;
59 node.* = &my_node;59 node.* = &my_node;
60 resume p;60 resume @handle();
61 }61 }
6262
63 // TODO this allocation elision should be guaranteed because we await it in63 // TODO this allocation elision should be guaranteed because we await it in
std/event/lock.zig+4-4
...@@ -90,10 +90,10 @@ pub const Lock = struct {...@@ -90,10 +90,10 @@ pub const Lock = struct {
90 }90 }
9191
92 pub async fn acquire(self: *Lock) Held {92 pub async fn acquire(self: *Lock) Held {
93 suspend |handle| {93 suspend {
94 // TODO explicitly put this memory in the coroutine frame #119494 // TODO explicitly put this memory in the coroutine frame #1194
95 var my_tick_node = Loop.NextTickNode{95 var my_tick_node = Loop.NextTickNode{
96 .data = handle,96 .data = @handle(),
97 .next = undefined,97 .next = undefined,
98 };98 };
9999
...@@ -141,8 +141,8 @@ test "std.event.Lock" {...@@ -141,8 +141,8 @@ test "std.event.Lock" {
141141
142async fn testLock(loop: *Loop, lock: *Lock) void {142async fn testLock(loop: *Loop, lock: *Lock) void {
143 // TODO explicitly put next tick node memory in the coroutine frame #1194143 // TODO explicitly put next tick node memory in the coroutine frame #1194
144 suspend |p| {144 suspend {
145 resume p;145 resume @handle();
146 }146 }
147 const handle1 = async lockRunner(lock) catch @panic("out of memory");147 const handle1 = async lockRunner(lock) catch @panic("out of memory");
148 var tick_node1 = Loop.NextTickNode{148 var tick_node1 = Loop.NextTickNode{
std/event/loop.zig+7-7
...@@ -331,11 +331,11 @@ pub const Loop = struct {...@@ -331,11 +331,11 @@ pub const Loop = struct {
331331
332 pub async fn waitFd(self: *Loop, fd: i32) !void {332 pub async fn waitFd(self: *Loop, fd: i32) !void {
333 defer self.removeFd(fd);333 defer self.removeFd(fd);
334 suspend |p| {334 suspend {
335 // TODO explicitly put this memory in the coroutine frame #1194335 // TODO explicitly put this memory in the coroutine frame #1194
336 var resume_node = ResumeNode{336 var resume_node = ResumeNode{
337 .id = ResumeNode.Id.Basic,337 .id = ResumeNode.Id.Basic,
338 .handle = p,338 .handle = @handle(),
339 };339 };
340 try self.addFd(fd, &resume_node);340 try self.addFd(fd, &resume_node);
341 }341 }
...@@ -417,11 +417,11 @@ pub const Loop = struct {...@@ -417,11 +417,11 @@ pub const Loop = struct {
417 pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) {417 pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) {
418 const S = struct {418 const S = struct {
419 async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType {419 async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType {
420 suspend |p| {420 suspend {
421 handle.* = p;421 handle.* = @handle();
422 var my_tick_node = Loop.NextTickNode{422 var my_tick_node = Loop.NextTickNode{
423 .next = undefined,423 .next = undefined,
424 .data = p,424 .data = @handle(),
425 };425 };
426 loop.onNextTick(&my_tick_node);426 loop.onNextTick(&my_tick_node);
427 }427 }
...@@ -439,10 +439,10 @@ pub const Loop = struct {...@@ -439,10 +439,10 @@ pub const Loop = struct {
439 /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O439 /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O
440 /// is performed.440 /// is performed.
441 pub async fn yield(self: *Loop) void {441 pub async fn yield(self: *Loop) void {
442 suspend |p| {442 suspend {
443 var my_tick_node = Loop.NextTickNode{443 var my_tick_node = Loop.NextTickNode{
444 .next = undefined,444 .next = undefined,
445 .data = p,445 .data = @handle(),
446 };446 };
447 self.onNextTick(&my_tick_node);447 self.onNextTick(&my_tick_node);
448 }448 }
std/event/tcp.zig+4-4
...@@ -88,8 +88,8 @@ pub const Server = struct {...@@ -88,8 +88,8 @@ pub const Server = struct {
88 },88 },
89 error.ProcessFdQuotaExceeded => {89 error.ProcessFdQuotaExceeded => {
90 errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);90 errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
91 suspend |p| {91 suspend {
92 self.waiting_for_emfile_node = PromiseNode.init(p);92 self.waiting_for_emfile_node = PromiseNode.init( @handle() );
93 std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);93 std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
94 }94 }
95 continue;95 continue;
...@@ -141,8 +141,8 @@ test "listen on a port, send bytes, receive bytes" {...@@ -141,8 +141,8 @@ test "listen on a port, send bytes, receive bytes" {
141 (await next_handler) catch |err| {141 (await next_handler) catch |err| {
142 std.debug.panic("unable to handle connection: {}\n", err);142 std.debug.panic("unable to handle connection: {}\n", err);
143 };143 };
144 suspend |p| {144 suspend {
145 cancel p;145 cancel @handle();
146 }146 }
147 }147 }
148 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {148 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {
std/zig/parser_test.zig+1-1
...@@ -1784,7 +1784,7 @@ test "zig fmt: coroutines" {...@@ -1784,7 +1784,7 @@ test "zig fmt: coroutines" {
1784 \\ x += 1;1784 \\ x += 1;
1785 \\ suspend;1785 \\ suspend;
1786 \\ x += 1;1786 \\ x += 1;
1787 \\ suspend |p| {}1787 \\ suspend;
1788 \\ const p: promise->void = async simpleAsyncFn() catch unreachable;1788 \\ const p: promise->void = async simpleAsyncFn() catch unreachable;
1789 \\ await p;1789 \\ await p;
1790 \\}1790 \\}
test/cases/cancel.zig+2-2
...@@ -85,8 +85,8 @@ async fn b4() void {...@@ -85,8 +85,8 @@ async fn b4() void {
85 defer {85 defer {
86 defer_b4 = true;86 defer_b4 = true;
87 }87 }
88 suspend |p| {88 suspend {
89 b4_handle = p;89 b4_handle = @handle();
90 }90 }
91 suspend;91 suspend;
92}92}
test/cases/coroutine_await_struct.zig+2-2
...@@ -30,9 +30,9 @@ async fn await_amain() void {...@@ -30,9 +30,9 @@ async fn await_amain() void {
30}30}
31async fn await_another() Foo {31async fn await_another() Foo {
32 await_seq('c');32 await_seq('c');
33 suspend |p| {33 suspend {
34 await_seq('d');34 await_seq('d');
35 await_a_promise = p;35 await_a_promise = @handle();
36 }36 }
37 await_seq('g');37 await_seq('g');
38 return Foo{ .x = 1234 };38 return Foo{ .x = 1234 };
test/cases/coroutines.zig+28-7
...@@ -62,10 +62,15 @@ test "coroutine suspend with block" {...@@ -62,10 +62,15 @@ test "coroutine suspend with block" {
62var a_promise: promise = undefined;62var a_promise: promise = undefined;
63var result = false;63var result = false;
64async fn testSuspendBlock() void {64async fn testSuspendBlock() void {
65 suspend |p| {65 suspend {
66 comptime assert(@typeOf(p) == promise->void);66 comptime assert(@typeOf(@handle()) == promise->void);
67 a_promise = p;67 a_promise = @handle();
68 }68 }
69
70 //Test to make sure that @handle() works as advertised (issue #1296)
71 //var our_handle: promise = @handle();
72 assert( a_promise == @handle() );
73
69 result = true;74 result = true;
70}75}
7176
...@@ -93,9 +98,9 @@ async fn await_amain() void {...@@ -93,9 +98,9 @@ async fn await_amain() void {
93}98}
94async fn await_another() i32 {99async fn await_another() i32 {
95 await_seq('c');100 await_seq('c');
96 suspend |p| {101 suspend {
97 await_seq('d');102 await_seq('d');
98 await_a_promise = p;103 await_a_promise = @handle();
99 }104 }
100 await_seq('g');105 await_seq('g');
101 return 1234;106 return 1234;
...@@ -244,10 +249,26 @@ test "break from suspend" {...@@ -244,10 +249,26 @@ test "break from suspend" {
244 std.debug.assert(my_result == 2);249 std.debug.assert(my_result == 2);
245}250}
246async fn testBreakFromSuspend(my_result: *i32) void {251async fn testBreakFromSuspend(my_result: *i32) void {
247 suspend |p| {252 suspend {
248 resume p;253 resume @handle();
249 }254 }
250 my_result.* += 1;255 my_result.* += 1;
251 suspend;256 suspend;
252 my_result.* += 1;257 my_result.* += 1;
253}258}
259
260test "suspend resume @handle()" {
261 var buf: [500]u8 = undefined;
262 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
263 var my_result: i32 = 1;
264 const p = try async<a> testBreakFromSuspend(&my_result);
265 std.debug.assert(my_result == 2);
266}
267async fn testSuspendResumeAtHandle() void {
268 suspend {
269 resume @handle();
270 }
271 my_result.* += 1;
272 suspend;
273 my_result.* += 1;
274}
\ No newline at end of file
test/compile_errors.zig+32-2
...@@ -367,8 +367,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -367,8 +367,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
367 \\}367 \\}
368 \\368 \\
369 \\async fn foo() void {369 \\async fn foo() void {
370 \\ suspend |p| {370 \\ suspend {
371 \\ suspend |p1| {371 \\ suspend {
372 \\ }372 \\ }
373 \\ }373 \\ }
374 \\}374 \\}
...@@ -4738,4 +4738,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -4738,4 +4738,34 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
4738 ,4738 ,
4739 ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic",4739 ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic",
4740 );4740 );
4741
4742 cases.add(
4743 "@handle() called outside of function definition",
4744 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
4745 \\ @import("std").os.exit(126);
4746 \\}
4747 \\
4748 \\var handle_undef: promise = undefined;
4749 \\var handle_dummy: promise = @handle();
4750 \\
4751 \\pub fn main() void {
4752 \\ if (handle_undef == handle_dummy) return 0;
4753 \\}
4754 ,
4755 ".tmp_source.zig:6:29: error: @handle() called outside of function definition",
4756 );
4757
4758 cases.add(
4759 "@handle() in non-async function",
4760 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
4761 \\ @import("std").os.exit(126);
4762 \\}
4763 \\
4764 \\pub fn main() void {
4765 \\ var handle_undef: promise = undefined;
4766 \\ if (handle_undef == @handle()) return 0;
4767 \\}
4768 ,
4769 ".tmp_source.zig:7:25: error: @handle() in non-async function",
4770 );
4741}4771}