| author | |
| committer | |
| log | 65140b2fba4e55d713de506f2bed259ca9410cbf |
| tree | e52660c1726caabc730c8869499e5801f027f1b1 |
| parent | 951124e1772c7013c2b1a674cf98a0b638c36262 |
| parent | fb05b96492f4fb1476106bf735788ac16f69c7ef |
25 files changed, 314 insertions(+), 186 deletions(-)
doc/langref.html.in+70-8| ... | @@ -134,6 +134,58 @@ pub fn main() void { | ... | @@ -134,6 +134,58 @@ pub fn main() void { |
| 134 | </p> | 134 | </p> |
| 135 | {#see_also|Values|@import|Errors|Root Source File#} | 135 | {#see_also|Values|@import|Errors|Root Source File#} |
| 136 | {#header_close#} | 136 | {#header_close#} |
| 137 | {#header_open|Comments#} | ||
| 138 | {#code_begin|test|comments#} | ||
| 139 | const assert = @import("std").debug.assert; | ||
| 140 | |||
| 141 | test "comments" { | ||
| 142 | // Comments in Zig start with "//" and end at the next LF byte (end of line). | ||
| 143 | // The below line is a comment, and won't be executed. | ||
| 144 | |||
| 145 | //assert(false); | ||
| 146 | |||
| 147 | const x = true; // another comment | ||
| 148 | assert(x); | ||
| 149 | } | ||
| 150 | {#code_end#} | ||
| 151 | <p> | ||
| 152 | There are no multiline comments in Zig (e.g. like <code>/* */</code> | ||
| 153 | comments in C). This helps allow Zig to have the property that each line | ||
| 154 | of code can be tokenized out of context. | ||
| 155 | </p> | ||
| 156 | {#header_open|Doc comments#} | ||
| 157 | <p> | ||
| 158 | A doc comment is one that begins with exactly three slashes (i.e. | ||
| 159 | <code class="zig">///</code> but not <code class="zig">////</code>); | ||
| 160 | multiple doc comments in a row are merged together to form a multiline | ||
| 161 | doc comment. The doc comment documents whatever immediately follows it. | ||
| 162 | </p> | ||
| 163 | {#code_begin|syntax|doc_comments#} | ||
| 164 | /// A structure for storing a timestamp, with nanosecond precision (this is a | ||
| 165 | /// multiline doc comment). | ||
| 166 | const Timestamp = struct { | ||
| 167 | /// The number of seconds since the epoch (this is also a doc comment). | ||
| 168 | seconds: i64, // signed so we can represent pre-1970 (not a doc comment) | ||
| 169 | /// The number of nanoseconds past the second (doc comment again). | ||
| 170 | nanos: u32, | ||
| 171 | |||
| 172 | /// Returns a `Timestamp` struct representing the Unix epoch; that is, the | ||
| 173 | /// moment of 1970 Jan 1 00:00:00 UTC (this is a doc comment too). | ||
| 174 | pub fn unixEpoch() Timestamp { | ||
| 175 | return Timestamp{ | ||
| 176 | .seconds = 0, | ||
| 177 | .nanos = 0, | ||
| 178 | }; | ||
| 179 | } | ||
| 180 | }; | ||
| 181 | {#code_end#} | ||
| 182 | <p> | ||
| 183 | Doc comments are only allowed in certain places; eventually, it will | ||
| 184 | become a compile error have a doc comment in an unexpected place, such as | ||
| 185 | in the middle of an expression, or just before a non-doc comment. | ||
| 186 | </p> | ||
| 187 | {#header_close#} | ||
| 188 | {#header_close#} | ||
| 137 | {#header_open|Values#} | 189 | {#header_open|Values#} |
| 138 | {#code_begin|exe|values#} | 190 | {#code_begin|exe|values#} |
| 139 | const std = @import("std"); | 191 | const std = @import("std"); |
| ... | @@ -4638,9 +4690,9 @@ test "coroutine suspend with block" { | ... | @@ -4638,9 +4690,9 @@ test "coroutine suspend with block" { |
| 4638 | var a_promise: promise = undefined; | 4690 | var a_promise: promise = undefined; |
| 4639 | var result = false; | 4691 | var result = false; |
| 4640 | async fn testSuspendBlock() void { | 4692 | async fn testSuspendBlock() void { |
| 4641 | suspend |p| { | 4693 | suspend { |
| 4642 | comptime assert(@typeOf(p) == promise->void); | 4694 | comptime assert(@typeOf(@handle()) == promise->void); |
| 4643 | a_promise = p; | 4695 | a_promise = @handle(); |
| 4644 | } | 4696 | } |
| 4645 | result = true; | 4697 | result = true; |
| 4646 | } | 4698 | } |
| ... | @@ -4681,8 +4733,8 @@ test "resume from suspend" { | ... | @@ -4681,8 +4733,8 @@ test "resume from suspend" { |
| 4681 | std.debug.assert(my_result == 2); | 4733 | std.debug.assert(my_result == 2); |
| 4682 | } | 4734 | } |
| 4683 | async fn testResumeFromSuspend(my_result: *i32) void { | 4735 | async fn testResumeFromSuspend(my_result: *i32) void { |
| 4684 | suspend |p| { | 4736 | suspend { |
| 4685 | resume p; | 4737 | resume @handle(); |
| 4686 | } | 4738 | } |
| 4687 | my_result.* += 1; | 4739 | my_result.* += 1; |
| 4688 | suspend; | 4740 | suspend; |
| ... | @@ -4739,9 +4791,9 @@ async fn amain() void { | ... | @@ -4739,9 +4791,9 @@ async fn amain() void { |
| 4739 | } | 4791 | } |
| 4740 | async fn another() i32 { | 4792 | async fn another() i32 { |
| 4741 | seq('c'); | 4793 | seq('c'); |
| 4742 | suspend |p| { | 4794 | suspend { |
| 4743 | seq('d'); | 4795 | seq('d'); |
| 4744 | a_promise = p; | 4796 | a_promise = @handle(); |
| 4745 | } | 4797 | } |
| 4746 | seq('g'); | 4798 | seq('g'); |
| 4747 | return 1234; | 4799 | return 1234; |
| ... | @@ -5331,6 +5383,16 @@ test "main" { | ... | @@ -5331,6 +5383,16 @@ test "main" { |
| 5331 | This function is only valid within function scope. | 5383 | This function is only valid within function scope. |
| 5332 | </p> | 5384 | </p> |
| 5333 | {#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#} | ||
| 5334 | {#header_open|@import#} | 5396 | {#header_open|@import#} |
| 5335 | <pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre> | 5397 | <pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre> |
| 5336 | <p> | 5398 | <p> |
| ... | @@ -7336,7 +7398,7 @@ Defer(body) = ("defer" | "deferror") body | ... | @@ -7336,7 +7398,7 @@ Defer(body) = ("defer" | "deferror") body |
| 7336 | 7398 | ||
| 7337 | IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) | 7399 | IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) |
| 7338 | 7400 | ||
| 7339 | SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) | 7401 | SuspendExpression(body) = "suspend" option( body ) |
| 7340 | 7402 | ||
| 7341 | 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) |
| 7342 | 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+12-18| ... | @@ -88,13 +88,11 @@ pub fn Channel(comptime T: type) type { | ... | @@ -88,13 +88,11 @@ pub fn Channel(comptime T: type) type { |
| 88 | /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. | 88 | /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. |
| 89 | pub async fn put(self: *SelfChannel, data: T) void { | 89 | pub async fn put(self: *SelfChannel, data: T) void { |
| 90 | // TODO fix this workaround | 90 | // TODO fix this workaround |
| 91 | var my_handle: promise = undefined; | 91 | suspend { |
| 92 | suspend |p| { | 92 | resume @handle(); |
| 93 | my_handle = p; | ||
| 94 | resume p; | ||
| 95 | } | 93 | } |
| 96 | 94 | ||
| 97 | var my_tick_node = Loop.NextTickNode.init(my_handle); | 95 | var my_tick_node = Loop.NextTickNode.init(@handle()); |
| 98 | var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{ | 96 | var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{ |
| 99 | .tick_node = &my_tick_node, | 97 | .tick_node = &my_tick_node, |
| 100 | .data = data, | 98 | .data = data, |
| ... | @@ -111,7 +109,7 @@ pub fn Channel(comptime T: type) type { | ... | @@ -111,7 +109,7 @@ pub fn Channel(comptime T: type) type { |
| 111 | self.dispatch(); | 109 | self.dispatch(); |
| 112 | } | 110 | } |
| 113 | } | 111 | } |
| 114 | suspend |handle| { | 112 | suspend { |
| 115 | self.putters.put(&queue_node); | 113 | self.putters.put(&queue_node); |
| 116 | _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | 114 | _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 117 | 115 | ||
| ... | @@ -123,16 +121,14 @@ pub fn Channel(comptime T: type) type { | ... | @@ -123,16 +121,14 @@ pub fn Channel(comptime T: type) type { |
| 123 | /// complete when the next item is put in the channel. | 121 | /// complete when the next item is put in the channel. |
| 124 | pub async fn get(self: *SelfChannel) T { | 122 | pub async fn get(self: *SelfChannel) T { |
| 125 | // TODO fix this workaround | 123 | // TODO fix this workaround |
| 126 | var my_handle: promise = undefined; | 124 | suspend { |
| 127 | suspend |p| { | 125 | resume @handle(); |
| 128 | my_handle = p; | ||
| 129 | resume p; | ||
| 130 | } | 126 | } |
| 131 | 127 | ||
| 132 | // TODO integrate this function with named return values | 128 | // TODO integrate this function with named return values |
| 133 | // so we can get rid of this extra result copy | 129 | // so we can get rid of this extra result copy |
| 134 | var result: T = undefined; | 130 | var result: T = undefined; |
| 135 | var my_tick_node = Loop.NextTickNode.init(my_handle); | 131 | var my_tick_node = Loop.NextTickNode.init(@handle()); |
| 136 | var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{ | 132 | var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{ |
| 137 | .tick_node = &my_tick_node, | 133 | .tick_node = &my_tick_node, |
| 138 | .data = GetNode.Data{ | 134 | .data = GetNode.Data{ |
| ... | @@ -152,7 +148,7 @@ pub fn Channel(comptime T: type) type { | ... | @@ -152,7 +148,7 @@ pub fn Channel(comptime T: type) type { |
| 152 | } | 148 | } |
| 153 | } | 149 | } |
| 154 | 150 | ||
| 155 | suspend |_| { | 151 | suspend { |
| 156 | self.getters.put(&queue_node); | 152 | self.getters.put(&queue_node); |
| 157 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | 153 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 158 | 154 | ||
| ... | @@ -176,16 +172,14 @@ pub fn Channel(comptime T: type) type { | ... | @@ -176,16 +172,14 @@ pub fn Channel(comptime T: type) type { |
| 176 | /// for data and will not wait for data to be available. | 172 | /// for data and will not wait for data to be available. |
| 177 | pub async fn getOrNull(self: *SelfChannel) ?T { | 173 | pub async fn getOrNull(self: *SelfChannel) ?T { |
| 178 | // TODO fix this workaround | 174 | // TODO fix this workaround |
| 179 | var my_handle: promise = undefined; | 175 | suspend { |
| 180 | suspend |p| { | 176 | resume @handle(); |
| 181 | my_handle = p; | ||
| 182 | resume p; | ||
| 183 | } | 177 | } |
| 184 | 178 | ||
| 185 | // TODO integrate this function with named return values | 179 | // TODO integrate this function with named return values |
| 186 | // so we can get rid of this extra result copy | 180 | // so we can get rid of this extra result copy |
| 187 | var result: ?T = null; | 181 | var result: ?T = null; |
| 188 | var my_tick_node = Loop.NextTickNode.init(my_handle); | 182 | var my_tick_node = Loop.NextTickNode.init(@handle()); |
| 189 | var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined); | 183 | var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined); |
| 190 | var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{ | 184 | var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{ |
| 191 | .tick_node = &my_tick_node, | 185 | .tick_node = &my_tick_node, |
| ... | @@ -211,7 +205,7 @@ pub fn Channel(comptime T: type) type { | ... | @@ -211,7 +205,7 @@ pub fn Channel(comptime T: type) type { |
| 211 | } | 205 | } |
| 212 | } | 206 | } |
| 213 | 207 | ||
| 214 | suspend |_| { | 208 | suspend { |
| 215 | self.getters.put(&queue_node); | 209 | self.getters.put(&queue_node); |
| 216 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); | 210 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 217 | self.or_null_queue.put(&or_null_node); | 211 | self.or_null_queue.put(&or_null_node); |
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| ... | @@ -65,10 +65,10 @@ pub fn Group(comptime ReturnType: type) type { | ... | @@ -65,10 +65,10 @@ pub fn Group(comptime ReturnType: type) type { |
| 65 | const S = struct { | 65 | const S = struct { |
| 66 | async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType { | 66 | async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType { |
| 67 | // TODO this is a hack to make the memory following be inside the coro frame | 67 | // TODO this is a hack to make the memory following be inside the coro frame |
| 68 | suspend |p| { | 68 | suspend { |
| 69 | var my_node: Stack.Node = undefined; | 69 | var my_node: Stack.Node = undefined; |
| 70 | node.* = &my_node; | 70 | node.* = &my_node; |
| 71 | resume p; | 71 | resume @handle(); |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | // TODO this allocation elision should be guaranteed because we await it in | 74 | // TODO this allocation elision should be guaranteed because we await it in |
std/event/lock.zig+7-32| ... | @@ -92,12 +92,10 @@ pub const Lock = struct { | ... | @@ -92,12 +92,10 @@ pub const Lock = struct { |
| 92 | 92 | ||
| 93 | pub async fn acquire(self: *Lock) Held { | 93 | pub async fn acquire(self: *Lock) Held { |
| 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_handle: promise = undefined; | 95 | suspend { |
| 96 | suspend |p| { | 96 | resume @handle(); |
| 97 | my_handle = p; | ||
| 98 | resume p; | ||
| 99 | } | 97 | } |
| 100 | var my_tick_node = Loop.NextTickNode.init(my_handle); | 98 | var my_tick_node = Loop.NextTickNode.init(@handle()); |
| 101 | 99 | ||
| 102 | errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire | 100 | errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire |
| 103 | suspend |_| { | 101 | suspend |_| { |
| ... | @@ -110,35 +108,12 @@ pub const Lock = struct { | ... | @@ -110,35 +108,12 @@ pub const Lock = struct { |
| 110 | // will attempt to grab the lock. | 108 | // will attempt to grab the lock. |
| 111 | _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | 109 | _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); |
| 112 | 110 | ||
| 113 | while (true) { | 111 | const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); |
| 114 | const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | 112 | if (old_bit == 0) { |
| 115 | if (old_bit != 0) { | ||
| 116 | // We did not obtain the lock. Trust that our queue entry will resume us, and allow | ||
| 117 | // suspend to complete. | ||
| 118 | break; | ||
| 119 | } | ||
| 120 | // We got the lock. However we might have already been resumed from the queue. | ||
| 121 | if (self.queue.get()) |node| { | 113 | if (self.queue.get()) |node| { |
| 122 | // Whether this node is us or someone else, we tail resume it. | 114 | // Whether this node is us or someone else, we tail resume it. |
| 123 | resume node.data; | 115 | resume node.data; |
| 124 | break; | ||
| 125 | } else { | ||
| 126 | // We already got resumed, and there are none left in the queue, which means that | ||
| 127 | // we aren't even supposed to hold the lock right now. | ||
| 128 | _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | ||
| 129 | _ = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | ||
| 130 | |||
| 131 | // There might be a queue item. If we know the queue is empty, we can be done, | ||
| 132 | // because the other actor will try to obtain the lock. | ||
| 133 | // But if there's a queue item, we are the actor which must loop and attempt | ||
| 134 | // to grab the lock again. | ||
| 135 | if (@atomicLoad(u8, &self.queue_empty_bit, AtomicOrder.SeqCst) == 1) { | ||
| 136 | break; | ||
| 137 | } else { | ||
| 138 | continue; | ||
| 139 | } | ||
| 140 | } | 116 | } |
| 141 | unreachable; | ||
| 142 | } | 117 | } |
| 143 | } | 118 | } |
| 144 | 119 | ||
| ... | @@ -168,8 +143,8 @@ test "std.event.Lock" { | ... | @@ -168,8 +143,8 @@ test "std.event.Lock" { |
| 168 | 143 | ||
| 169 | async fn testLock(loop: *Loop, lock: *Lock) void { | 144 | async fn testLock(loop: *Loop, lock: *Lock) void { |
| 170 | // TODO explicitly put next tick node memory in the coroutine frame #1194 | 145 | // TODO explicitly put next tick node memory in the coroutine frame #1194 |
| 171 | suspend |p| { | 146 | suspend { |
| 172 | resume p; | 147 | resume @handle(); |
| 173 | } | 148 | } |
| 174 | const handle1 = async lockRunner(lock) catch @panic("out of memory"); | 149 | const handle1 = async lockRunner(lock) catch @panic("out of memory"); |
| 175 | var tick_node1 = Loop.NextTickNode{ | 150 | var tick_node1 = Loop.NextTickNode{ |
std/event/loop.zig+7-7| ... | @@ -354,11 +354,11 @@ pub const Loop = struct { | ... | @@ -354,11 +354,11 @@ pub const Loop = struct { |
| 354 | 354 | ||
| 355 | pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void { | 355 | pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void { |
| 356 | defer self.linuxRemoveFd(fd); | 356 | defer self.linuxRemoveFd(fd); |
| 357 | suspend |p| { | 357 | suspend { |
| 358 | // TODO explicitly put this memory in the coroutine frame #1194 | 358 | // TODO explicitly put this memory in the coroutine frame #1194 |
| 359 | var resume_node = ResumeNode{ | 359 | var resume_node = ResumeNode{ |
| 360 | .id = ResumeNode.Id.Basic, | 360 | .id = ResumeNode.Id.Basic, |
| 361 | .handle = p, | 361 | .handle = @handle(), |
| 362 | }; | 362 | }; |
| 363 | try self.linuxAddFd(fd, &resume_node, flags); | 363 | try self.linuxAddFd(fd, &resume_node, flags); |
| 364 | } | 364 | } |
| ... | @@ -449,12 +449,12 @@ pub const Loop = struct { | ... | @@ -449,12 +449,12 @@ pub const Loop = struct { |
| 449 | pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) { | 449 | pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) { |
| 450 | const S = struct { | 450 | const S = struct { |
| 451 | async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType { | 451 | async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType { |
| 452 | suspend |p| { | 452 | suspend { |
| 453 | handle.* = p; | 453 | handle.* = @handle(); |
| 454 | var my_tick_node = Loop.NextTickNode{ | 454 | var my_tick_node = Loop.NextTickNode{ |
| 455 | .prev = undefined, | 455 | .prev = undefined, |
| 456 | .next = undefined, | 456 | .next = undefined, |
| 457 | .data = p, | 457 | .data = @handle(), |
| 458 | }; | 458 | }; |
| 459 | loop.onNextTick(&my_tick_node); | 459 | loop.onNextTick(&my_tick_node); |
| 460 | } | 460 | } |
| ... | @@ -472,11 +472,11 @@ pub const Loop = struct { | ... | @@ -472,11 +472,11 @@ pub const Loop = struct { |
| 472 | /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O | 472 | /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O |
| 473 | /// is performed. | 473 | /// is performed. |
| 474 | pub async fn yield(self: *Loop) void { | 474 | pub async fn yield(self: *Loop) void { |
| 475 | suspend |p| { | 475 | suspend { |
| 476 | var my_tick_node = Loop.NextTickNode{ | 476 | var my_tick_node = Loop.NextTickNode{ |
| 477 | .prev = undefined, | 477 | .prev = undefined, |
| 478 | .next = undefined, | 478 | .next = undefined, |
| 479 | .data = p, | 479 | .data = @handle(), |
| 480 | }; | 480 | }; |
| 481 | self.onNextTick(&my_tick_node); | 481 | self.onNextTick(&my_tick_node); |
| 482 | } | 482 | } |
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/fmt/index.zig+43| ... | @@ -18,6 +18,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), | ... | @@ -18,6 +18,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 18 | OpenBrace, | 18 | OpenBrace, |
| 19 | CloseBrace, | 19 | CloseBrace, |
| 20 | FormatString, | 20 | FormatString, |
| 21 | Pointer, | ||
| 21 | }; | 22 | }; |
| 22 | 23 | ||
| 23 | comptime var start_index = 0; | 24 | comptime var start_index = 0; |
| ... | @@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), | ... | @@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 54 | state = State.Start; | 55 | state = State.Start; |
| 55 | start_index = i + 1; | 56 | start_index = i + 1; |
| 56 | }, | 57 | }, |
| 58 | '*' => state = State.Pointer, | ||
| 57 | else => { | 59 | else => { |
| 58 | state = State.FormatString; | 60 | state = State.FormatString; |
| 59 | }, | 61 | }, |
| ... | @@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), | ... | @@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 75 | }, | 77 | }, |
| 76 | else => {}, | 78 | else => {}, |
| 77 | }, | 79 | }, |
| 80 | State.Pointer => switch (c) { | ||
| 81 | '}' => { | ||
| 82 | try output(context, @typeName(@typeOf(args[next_arg]).Child)); | ||
| 83 | try output(context, "@"); | ||
| 84 | try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output); | ||
| 85 | next_arg += 1; | ||
| 86 | state = State.Start; | ||
| 87 | start_index = i + 1; | ||
| 88 | }, | ||
| 89 | else => @compileError("Unexpected format character after '*'"), | ||
| 90 | }, | ||
| 78 | } | 91 | } |
| 79 | } | 92 | } |
| 80 | comptime { | 93 | comptime { |
| ... | @@ -235,6 +248,11 @@ pub fn formatIntValue( | ... | @@ -235,6 +248,11 @@ pub fn formatIntValue( |
| 235 | return formatAsciiChar(value, context, Errors, output); | 248 | return formatAsciiChar(value, context, Errors, output); |
| 236 | } | 249 | } |
| 237 | }, | 250 | }, |
| 251 | 'b' => { | ||
| 252 | radix = 2; | ||
| 253 | uppercase = false; | ||
| 254 | width = 0; | ||
| 255 | }, | ||
| 238 | 'd' => { | 256 | 'd' => { |
| 239 | radix = 10; | 257 | radix = 10; |
| 240 | uppercase = false; | 258 | uppercase = false; |
| ... | @@ -861,6 +879,31 @@ test "fmt.format" { | ... | @@ -861,6 +879,31 @@ test "fmt.format" { |
| 861 | const value: u8 = 'a'; | 879 | const value: u8 = 'a'; |
| 862 | try testFmt("u8: a\n", "u8: {c}\n", value); | 880 | try testFmt("u8: a\n", "u8: {c}\n", value); |
| 863 | } | 881 | } |
| 882 | { | ||
| 883 | const value: u8 = 0b1100; | ||
| 884 | try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); | ||
| 885 | } | ||
| 886 | { | ||
| 887 | const value: [3]u8 = "abc"; | ||
| 888 | try testFmt("array: abc\n", "array: {}\n", value); | ||
| 889 | try testFmt("array: abc\n", "array: {}\n", &value); | ||
| 890 | |||
| 891 | var buf: [100]u8 = undefined; | ||
| 892 | try testFmt( | ||
| 893 | try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)), | ||
| 894 | "array: {*}\n", | ||
| 895 | &value, | ||
| 896 | ); | ||
| 897 | } | ||
| 898 | { | ||
| 899 | const value: []const u8 = "abc"; | ||
| 900 | try testFmt("slice: abc\n", "slice: {}\n", value); | ||
| 901 | } | ||
| 902 | { | ||
| 903 | const value = @intToPtr(*i32, 0xdeadbeef); | ||
| 904 | try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); | ||
| 905 | try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value); | ||
| 906 | } | ||
| 864 | try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); | 907 | try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); |
| 865 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); | 908 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); |
| 866 | try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); | 909 | try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); |
std/os/index.zig+13-13| ... | @@ -120,16 +120,10 @@ pub fn getRandomBytes(buf: []u8) !void { | ... | @@ -120,16 +120,10 @@ pub fn getRandomBytes(buf: []u8) !void { |
| 120 | try posixRead(fd, buf); | 120 | try posixRead(fd, buf); |
| 121 | }, | 121 | }, |
| 122 | Os.windows => { | 122 | Os.windows => { |
| 123 | var hCryptProv: windows.HCRYPTPROV = undefined; | 123 | // Call RtlGenRandom() instead of CryptGetRandom() on Windows |
| 124 | if (windows.CryptAcquireContextA(&hCryptProv, null, null, windows.PROV_RSA_FULL, 0) == 0) { | 124 | // https://github.com/rust-lang-nursery/rand/issues/111 |
| 125 | const err = windows.GetLastError(); | 125 | // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 |
| 126 | return switch (err) { | 126 | if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { |
| 127 | else => unexpectedErrorWindows(err), | ||
| 128 | }; | ||
| 129 | } | ||
| 130 | defer _ = windows.CryptReleaseContext(hCryptProv, 0); | ||
| 131 | |||
| 132 | if (windows.CryptGenRandom(hCryptProv, @intCast(windows.DWORD, buf.len), buf.ptr) == 0) { | ||
| 133 | const err = windows.GetLastError(); | 127 | const err = windows.GetLastError(); |
| 134 | return switch (err) { | 128 | return switch (err) { |
| 135 | else => unexpectedErrorWindows(err), | 129 | else => unexpectedErrorWindows(err), |
| ... | @@ -149,8 +143,14 @@ pub fn getRandomBytes(buf: []u8) !void { | ... | @@ -149,8 +143,14 @@ pub fn getRandomBytes(buf: []u8) !void { |
| 149 | } | 143 | } |
| 150 | 144 | ||
| 151 | test "os.getRandomBytes" { | 145 | test "os.getRandomBytes" { |
| 152 | var buf: [50]u8 = undefined; | 146 | var buf_a: [50]u8 = undefined; |
| 153 | try getRandomBytes(buf[0..]); | 147 | var buf_b: [50]u8 = undefined; |
| 148 | // Call Twice | ||
| 149 | try getRandomBytes(buf_a[0..]); | ||
| 150 | try getRandomBytes(buf_b[0..]); | ||
| 151 | |||
| 152 | // Check if random (not 100% conclusive) | ||
| 153 | assert( !mem.eql(u8, buf_a, buf_b) ); | ||
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | /// Raises a signal in the current kernel thread, ending its execution. | 156 | /// Raises a signal in the current kernel thread, ending its execution. |
| ... | @@ -2823,7 +2823,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { | ... | @@ -2823,7 +2823,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { |
| 2823 | builtin.Os.macosx => { | 2823 | builtin.Os.macosx => { |
| 2824 | var count: c_int = undefined; | 2824 | var count: c_int = undefined; |
| 2825 | var count_len: usize = @sizeOf(c_int); | 2825 | var count_len: usize = @sizeOf(c_int); |
| 2826 | const rc = posix.sysctlbyname(c"hw.ncpu", @ptrCast(*c_void, &count), &count_len, null, 0); | 2826 | const rc = posix.sysctlbyname(c"hw.logicalcpu", @ptrCast(*c_void, &count), &count_len, null, 0); |
| 2827 | const err = posix.getErrno(rc); | 2827 | const err = posix.getErrno(rc); |
| 2828 | switch (err) { | 2828 | switch (err) { |
| 2829 | 0 => return @intCast(usize, count), | 2829 | 0 => return @intCast(usize, count), |
std/os/windows/advapi32.zig+5| ... | @@ -28,3 +28,8 @@ pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, | ... | @@ -28,3 +28,8 @@ pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, |
| 28 | 28 | ||
| 29 | pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, | 29 | pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, |
| 30 | lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS; | 30 | lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS; |
| 31 | |||
| 32 | // RtlGenRandom is known as SystemFunction036 under advapi32 | ||
| 33 | // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ | ||
| 34 | pub extern "advapi32" stdcallcc fn SystemFunction036(output: [*]u8, length: usize) BOOL; | ||
| 35 | pub const RtlGenRandom = SystemFunction036; |
std/os/windows/util.zig+1-1| ... | @@ -166,7 +166,7 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void { | ... | @@ -166,7 +166,7 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void { |
| 166 | } | 166 | } |
| 167 | 167 | ||
| 168 | test "InvalidDll" { | 168 | test "InvalidDll" { |
| 169 | if (builtin.os != builtin.Os.windows) return; | 169 | if (builtin.os != builtin.Os.windows) return error.SkipZigTest; |
| 170 | 170 | ||
| 171 | const DllName = "asdf.dll"; | 171 | const DllName = "asdf.dll"; |
| 172 | const allocator = std.debug.global_allocator; | 172 | const allocator = std.debug.global_allocator; |
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 | \\} |