| author | |
| committer | |
| log | fb05b96492f4fb1476106bf735788ac16f69c7ef |
| tree | 14ec51f41d29214e550317f283aa8dcdbf223b9d |
| parent | 9ecbabfc4ce857e43db2b056bc83272cb24b0bbd |
| parent | 895f262a55b9951647efef4528c17cf64d6b7c07 |
21 files changed, 189 insertions(+), 130 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" { |
| 4690 | var a_promise: promise = undefined; | 4690 | var a_promise: promise = undefined; |
| 4691 | var result = false; | 4691 | var result = false; |
| 4692 | async fn testSuspendBlock() void { | 4692 | async 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 | } |
| 4735 | async fn testResumeFromSuspend(my_result: *i32) void { | 4735 | async 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 | } |
| 4792 | async fn another() i32 { | 4792 | async 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 |
| 7388 | 7398 | ||
| 7389 | IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) | 7399 | IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) |
| 7390 | 7400 | ||
| 7391 | SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) | 7401 | SuspendExpression(body) = "suspend" option( body ) |
| 7392 | 7402 | ||
| 7393 | IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) | 7403 | IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) |
| 7394 | 7404 |
src/all_types.hpp+7-1| ... | @@ -899,7 +899,6 @@ struct AstNodeAwaitExpr { | ... | @@ -899,7 +899,6 @@ struct AstNodeAwaitExpr { |
| 899 | 899 | ||
| 900 | struct AstNodeSuspend { | 900 | struct AstNodeSuspend { |
| 901 | AstNode *block; | 901 | AstNode *block; |
| 902 | AstNode *promise_symbol; | ||
| 903 | }; | 902 | }; |
| 904 | 903 | ||
| 905 | struct AstNodePromiseType { | 904 | struct 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 | }; |
| 2795 | 2797 | ||
| 2798 | struct IrInstructionHandle { | ||
| 2799 | IrInstruction base; | ||
| 2800 | }; | ||
| 2801 | |||
| 2796 | enum IrOverflowOp { | 2802 | enum 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 | } |
| 4148 | 4148 | ||
| 4149 | static 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 | |||
| 4162 | static 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 | |||
| 4149 | static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { | 4169 | static 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); |
| 6006 | 6028 | ||
| 6007 | } | 6029 | } |
| 6030 | |||
| 6008 | assert(!g->errors.length); | 6031 | assert(!g->errors.length); |
| 6009 | 6032 | ||
| 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 | } |
| 582 | 582 | ||
| 583 | static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) { | ||
| 584 | return IrInstructionIdHandle; | ||
| 585 | } | ||
| 586 | |||
| 583 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) { | 587 | static 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 | } |
| 2242 | 2246 | ||
| 2247 | static 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 | |||
| 2252 | static 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 | |||
| 2243 | static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node, | 2258 | static 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 | } |
| 3845 | 3860 | ||
| 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 | } |
| 19009 | 19025 | ||
| 19026 | static 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 | |||
| 19010 | static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { | 19034 | static 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 | } |
| 793 | 793 | ||
| 794 | static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) { | ||
| 795 | fprintf(irp->f, "@handle()"); | ||
| 796 | } | ||
| 797 | |||
| 794 | static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) { | 798 | static 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+9-16| ... | @@ -648,12 +648,11 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m | ... | @@ -648,12 +648,11 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m |
| 648 | } | 648 | } |
| 649 | 649 | ||
| 650 | /* | 650 | /* |
| 651 | SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) | 651 | SuspendExpression(body) = "suspend" option( body ) |
| 652 | */ | 652 | */ |
| 653 | static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { | 653 | static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { |
| 654 | size_t orig_token_index = *token_index; | ||
| 655 | |||
| 656 | Token *suspend_token = &pc->tokens->at(*token_index); | 654 | Token *suspend_token = &pc->tokens->at(*token_index); |
| 655 | |||
| 657 | if (suspend_token->id == TokenIdKeywordSuspend) { | 656 | if (suspend_token->id == TokenIdKeywordSuspend) { |
| 658 | *token_index += 1; | 657 | *token_index += 1; |
| 659 | } else if (mandatory) { | 658 | } else if (mandatory) { |
| ... | @@ -663,23 +662,18 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b | ... | @@ -663,23 +662,18 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b |
| 663 | return nullptr; | 662 | return nullptr; |
| 664 | } | 663 | } |
| 665 | 664 | ||
| 666 | Token *bar_token = &pc->tokens->at(*token_index); | 665 | Token *lbrace = &pc->tokens->at(*token_index); |
| 667 | if (bar_token->id == TokenIdBinOr) { | 666 | if (lbrace->id == TokenIdLBrace) { |
| 668 | *token_index += 1; | 667 | AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); |
| 668 | node->data.suspend.block = ast_parse_block(pc, token_index, true); | ||
| 669 | return node; | ||
| 669 | } else if (mandatory) { | 670 | } else if (mandatory) { |
| 670 | ast_expect_token(pc, suspend_token, TokenIdBinOr); | 671 | ast_expect_token(pc, lbrace, TokenIdLBrace); |
| 671 | zig_unreachable(); | 672 | zig_unreachable(); |
| 672 | } else { | 673 | } else { |
| 673 | *token_index = orig_token_index; | 674 | *token_index -= 1; |
| 674 | return nullptr; | 675 | return nullptr; |
| 675 | } | 676 | } |
| 676 | |||
| 677 | 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); | ||
| 681 | |||
| 682 | return node; | ||
| 683 | } | 677 | } |
| 684 | 678 | ||
| 685 | /* | 679 | /* |
| ... | @@ -3134,7 +3128,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont | ... | @@ -3134,7 +3128,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont |
| 3134 | visit_field(&node->data.await_expr.expr, visit, context); | 3128 | visit_field(&node->data.await_expr.expr, visit, context); |
| 3135 | break; | 3129 | break; |
| 3136 | case NodeTypeSuspend: | 3130 | case NodeTypeSuspend: |
| 3137 | visit_field(&node->data.suspend.promise_symbol, visit, context); | ||
| 3138 | visit_field(&node->data.suspend.block, visit, context); | 3131 | visit_field(&node->data.suspend.block, visit, context); |
| 3139 | break; | 3132 | break; |
| 3140 | } | 3133 | } |
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 the | 71 | /// 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 values | 96 | // TODO integrate this function with named return values |
| 97 | // so we can get rid of this extra result copy | 97 | // 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 | } |
| 101 | 101 | ||
| 102 | async fn testFuture(loop: *Loop) void { | 102 | async 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); |
| 107 | 107 | ||
| ... | @@ -115,15 +115,15 @@ async fn testFuture(loop: *Loop) void { | ... | @@ -115,15 +115,15 @@ async fn testFuture(loop: *Loop) void { |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | async fn waitOnFuture(future: *Future(i32)) i32 { | 117 | async 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 | } |
| 123 | 123 | ||
| 124 | async fn resolveFuture(future: *Future(i32)) void { | 124 | async 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 frame | 56 | // 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 | } |
| 62 | 62 | ||
| 63 | // TODO this allocation elision should be guaranteed because we await it in | 63 | // 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 | } |
| 91 | 91 | ||
| 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 #1194 | 94 | // 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 | }; |
| 99 | 99 | ||
| ... | @@ -141,8 +141,8 @@ test "std.event.Lock" { | ... | @@ -141,8 +141,8 @@ test "std.event.Lock" { |
| 141 | 141 | ||
| 142 | async fn testLock(loop: *Loop, lock: *Lock) void { | 142 | async fn testLock(loop: *Loop, lock: *Lock) void { |
| 143 | // TODO explicitly put next tick node memory in the coroutine frame #1194 | 143 | // 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 { |
| 331 | 331 | ||
| 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 #1194 | 335 | // 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/O | 439 | /// 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/ast.zig-12| ... | @@ -1778,19 +1778,12 @@ pub const Node = struct { | ... | @@ -1778,19 +1778,12 @@ pub const Node = struct { |
| 1778 | 1778 | ||
| 1779 | pub const Suspend = struct { | 1779 | pub const Suspend = struct { |
| 1780 | base: Node, | 1780 | base: Node, |
| 1781 | label: ?TokenIndex, | ||
| 1782 | suspend_token: TokenIndex, | 1781 | suspend_token: TokenIndex, |
| 1783 | payload: ?*Node, | ||
| 1784 | body: ?*Node, | 1782 | body: ?*Node, |
| 1785 | 1783 | ||
| 1786 | pub fn iterate(self: *Suspend, index: usize) ?*Node { | 1784 | pub fn iterate(self: *Suspend, index: usize) ?*Node { |
| 1787 | var i = index; | 1785 | var i = index; |
| 1788 | 1786 | ||
| 1789 | if (self.payload) |payload| { | ||
| 1790 | if (i < 1) return payload; | ||
| 1791 | i -= 1; | ||
| 1792 | } | ||
| 1793 | |||
| 1794 | if (self.body) |body| { | 1787 | if (self.body) |body| { |
| 1795 | if (i < 1) return body; | 1788 | if (i < 1) return body; |
| 1796 | i -= 1; | 1789 | i -= 1; |
| ... | @@ -1800,7 +1793,6 @@ pub const Node = struct { | ... | @@ -1800,7 +1793,6 @@ pub const Node = struct { |
| 1800 | } | 1793 | } |
| 1801 | 1794 | ||
| 1802 | pub fn firstToken(self: *Suspend) TokenIndex { | 1795 | pub fn firstToken(self: *Suspend) TokenIndex { |
| 1803 | if (self.label) |label| return label; | ||
| 1804 | return self.suspend_token; | 1796 | return self.suspend_token; |
| 1805 | } | 1797 | } |
| 1806 | 1798 | ||
| ... | @@ -1809,10 +1801,6 @@ pub const Node = struct { | ... | @@ -1809,10 +1801,6 @@ pub const Node = struct { |
| 1809 | return body.lastToken(); | 1801 | return body.lastToken(); |
| 1810 | } | 1802 | } |
| 1811 | 1803 | ||
| 1812 | if (self.payload) |payload| { | ||
| 1813 | return payload.lastToken(); | ||
| 1814 | } | ||
| 1815 | |||
| 1816 | return self.suspend_token; | 1804 | return self.suspend_token; |
| 1817 | } | 1805 | } |
| 1818 | }; | 1806 | }; |
std/zig/parse.zig+14-19| ... | @@ -852,19 +852,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { | ... | @@ -852,19 +852,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { |
| 852 | }) catch unreachable; | 852 | }) catch unreachable; |
| 853 | continue; | 853 | continue; |
| 854 | }, | 854 | }, |
| 855 | Token.Id.Keyword_suspend => { | ||
| 856 | const node = try arena.create(ast.Node.Suspend{ | ||
| 857 | .base = ast.Node{ .id = ast.Node.Id.Suspend }, | ||
| 858 | .label = ctx.label, | ||
| 859 | .suspend_token = token_index, | ||
| 860 | .payload = null, | ||
| 861 | .body = null, | ||
| 862 | }); | ||
| 863 | ctx.opt_ctx.store(&node.base); | ||
| 864 | stack.append(State{ .SuspendBody = node }) catch unreachable; | ||
| 865 | try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); | ||
| 866 | continue; | ||
| 867 | }, | ||
| 868 | Token.Id.Keyword_inline => { | 855 | Token.Id.Keyword_inline => { |
| 869 | stack.append(State{ | 856 | stack.append(State{ |
| 870 | .Inline = InlineCtx{ | 857 | .Inline = InlineCtx{ |
| ... | @@ -1415,10 +1402,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { | ... | @@ -1415,10 +1402,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { |
| 1415 | }, | 1402 | }, |
| 1416 | 1403 | ||
| 1417 | State.SuspendBody => |suspend_node| { | 1404 | State.SuspendBody => |suspend_node| { |
| 1418 | if (suspend_node.payload != null) { | 1405 | const token = nextToken(&tok_it, &tree); |
| 1419 | try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); | 1406 | switch (token.ptr.id) { |
| 1407 | Token.Id.Semicolon => { | ||
| 1408 | prevToken(&tok_it, &tree); | ||
| 1409 | continue; | ||
| 1410 | }, | ||
| 1411 | Token.Id.LBrace => { | ||
| 1412 | prevToken(&tok_it, &tree); | ||
| 1413 | try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); | ||
| 1414 | continue; | ||
| 1415 | }, | ||
| 1416 | else => { | ||
| 1417 | ((try tree.errors.addOne())).* = Error{ .InvalidToken = Error.InvalidToken{ .token = token.index } }; | ||
| 1418 | }, | ||
| 1420 | } | 1419 | } |
| 1421 | continue; | ||
| 1422 | }, | 1420 | }, |
| 1423 | State.AsyncAllocator => |async_node| { | 1421 | State.AsyncAllocator => |async_node| { |
| 1424 | if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) { | 1422 | if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) { |
| ... | @@ -3086,15 +3084,12 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con | ... | @@ -3086,15 +3084,12 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con |
| 3086 | Token.Id.Keyword_suspend => { | 3084 | Token.Id.Keyword_suspend => { |
| 3087 | const node = try arena.create(ast.Node.Suspend{ | 3085 | const node = try arena.create(ast.Node.Suspend{ |
| 3088 | .base = ast.Node{ .id = ast.Node.Id.Suspend }, | 3086 | .base = ast.Node{ .id = ast.Node.Id.Suspend }, |
| 3089 | .label = null, | ||
| 3090 | .suspend_token = token_index, | 3087 | .suspend_token = token_index, |
| 3091 | .payload = null, | ||
| 3092 | .body = null, | 3088 | .body = null, |
| 3093 | }); | 3089 | }); |
| 3094 | ctx.store(&node.base); | 3090 | ctx.store(&node.base); |
| 3095 | 3091 | ||
| 3096 | stack.append(State{ .SuspendBody = node }) catch unreachable; | 3092 | stack.append(State{ .SuspendBody = node }) catch unreachable; |
| 3097 | try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); | ||
| 3098 | return true; | 3093 | return true; |
| 3099 | }, | 3094 | }, |
| 3100 | Token.Id.Keyword_if => { | 3095 | Token.Id.Keyword_if => { |
std/zig/parser_test.zig+4-4| ... | @@ -898,11 +898,11 @@ test "zig fmt: union(enum(u32)) with assigned enum values" { | ... | @@ -898,11 +898,11 @@ test "zig fmt: union(enum(u32)) with assigned enum values" { |
| 898 | ); | 898 | ); |
| 899 | } | 899 | } |
| 900 | 900 | ||
| 901 | test "zig fmt: labeled suspend" { | 901 | test "zig fmt: resume from suspend block" { |
| 902 | try testCanonical( | 902 | try testCanonical( |
| 903 | \\fn foo() void { | 903 | \\fn foo() void { |
| 904 | \\ s: suspend |p| { | 904 | \\ suspend { |
| 905 | \\ break :s; | 905 | \\ resume @handle(); |
| 906 | \\ } | 906 | \\ } |
| 907 | \\} | 907 | \\} |
| 908 | \\ | 908 | \\ |
| ... | @@ -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 | \\} |
std/zig/render.zig+1-15| ... | @@ -323,21 +323,7 @@ fn renderExpression( | ... | @@ -323,21 +323,7 @@ fn renderExpression( |
| 323 | ast.Node.Id.Suspend => { | 323 | ast.Node.Id.Suspend => { |
| 324 | const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); | 324 | const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); |
| 325 | 325 | ||
| 326 | if (suspend_node.label) |label| { | 326 | if (suspend_node.body) |body| { |
| 327 | try renderToken(tree, stream, label, indent, start_col, Space.None); | ||
| 328 | try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); | ||
| 329 | } | ||
| 330 | |||
| 331 | if (suspend_node.payload) |payload| { | ||
| 332 | if (suspend_node.body) |body| { | ||
| 333 | try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); | ||
| 334 | try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); | ||
| 335 | return renderExpression(allocator, stream, tree, indent, start_col, body, space); | ||
| 336 | } else { | ||
| 337 | try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); | ||
| 338 | return renderExpression(allocator, stream, tree, indent, start_col, payload, space); | ||
| 339 | } | ||
| 340 | } else if (suspend_node.body) |body| { | ||
| 341 | try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); | 327 | try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); |
| 342 | return renderExpression(allocator, stream, tree, indent, start_col, body, space); | 328 | return renderExpression(allocator, stream, tree, indent, start_col, body, space); |
| 343 | } else { | 329 | } else { |
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 | } |
| 31 | async fn await_another() Foo { | 31 | async 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+12-7| ... | @@ -62,10 +62,15 @@ test "coroutine suspend with block" { | ... | @@ -62,10 +62,15 @@ test "coroutine suspend with block" { |
| 62 | var a_promise: promise = undefined; | 62 | var a_promise: promise = undefined; |
| 63 | var result = false; | 63 | var result = false; |
| 64 | async fn testSuspendBlock() void { | 64 | async 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 | } |
| 71 | 76 | ||
| ... | @@ -93,9 +98,9 @@ async fn await_amain() void { | ... | @@ -93,9 +98,9 @@ async fn await_amain() void { |
| 93 | } | 98 | } |
| 94 | async fn await_another() i32 { | 99 | async 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,8 +249,8 @@ test "break from suspend" { | ... | @@ -244,8 +249,8 @@ test "break from suspend" { |
| 244 | std.debug.assert(my_result == 2); | 249 | std.debug.assert(my_result == 2); |
| 245 | } | 250 | } |
| 246 | async fn testBreakFromSuspend(my_result: *i32) void { | 251 | async 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; |
test/compile_errors.zig+23-2| ... | @@ -1,6 +1,27 @@ | ... | @@ -1,6 +1,27 @@ |
| 1 | const tests = @import("tests.zig"); | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | ||
| 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 3 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4 | cases.add( | ||
| 5 | "@handle() called outside of function definition", | ||
| 6 | \\var handle_undef: promise = undefined; | ||
| 7 | \\var handle_dummy: promise = @handle(); | ||
| 8 | \\export fn entry() bool { | ||
| 9 | \\ return handle_undef == handle_dummy; | ||
| 10 | \\} | ||
| 11 | , | ||
| 12 | ".tmp_source.zig:2:29: error: @handle() called outside of function definition", | ||
| 13 | ); | ||
| 14 | |||
| 15 | cases.add( | ||
| 16 | "@handle() in non-async function", | ||
| 17 | \\export fn entry() bool { | ||
| 18 | \\ var handle_undef: promise = undefined; | ||
| 19 | \\ return handle_undef == @handle(); | ||
| 20 | \\} | ||
| 21 | , | ||
| 22 | ".tmp_source.zig:3:28: error: @handle() in non-async function", | ||
| 23 | ); | ||
| 24 | |||
| 4 | cases.add( | 25 | cases.add( |
| 5 | "while loop body expression ignored", | 26 | "while loop body expression ignored", |
| 6 | \\fn returns() usize { | 27 | \\fn returns() usize { |
| ... | @@ -367,8 +388,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { | ... | @@ -367,8 +388,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 367 | \\} | 388 | \\} |
| 368 | \\ | 389 | \\ |
| 369 | \\async fn foo() void { | 390 | \\async fn foo() void { |
| 370 | \\ suspend |p| { | 391 | \\ suspend { |
| 371 | \\ suspend |p1| { | 392 | \\ suspend { |
| 372 | \\ } | 393 | \\ } |
| 373 | \\ } | 394 | \\ } |
| 374 | \\} | 395 | \\} |