| 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 | 134 | </p> |
| 135 | 135 | {#see_also|Values|@import|Errors|Root Source File#} |
| 136 | 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 | 189 | {#header_open|Values#} |
| 138 | 190 | {#code_begin|exe|values#} |
| 139 | 191 | const std = @import("std"); |
| ... | ... | @@ -4638,9 +4690,9 @@ test "coroutine suspend with block" { |
| 4638 | 4690 | var a_promise: promise = undefined; |
| 4639 | 4691 | var result = false; |
| 4640 | 4692 | async fn testSuspendBlock() void { |
| 4641 | suspend |p| { | |
| 4642 | comptime assert(@typeOf(p) == promise->void); | |
| 4643 | a_promise = p; | |
| 4693 | suspend { | |
| 4694 | comptime assert(@typeOf(@handle()) == promise->void); | |
| 4695 | a_promise = @handle(); | |
| 4644 | 4696 | } |
| 4645 | 4697 | result = true; |
| 4646 | 4698 | } |
| ... | ... | @@ -4681,8 +4733,8 @@ test "resume from suspend" { |
| 4681 | 4733 | std.debug.assert(my_result == 2); |
| 4682 | 4734 | } |
| 4683 | 4735 | async fn testResumeFromSuspend(my_result: *i32) void { |
| 4684 | suspend |p| { | |
| 4685 | resume p; | |
| 4736 | suspend { | |
| 4737 | resume @handle(); | |
| 4686 | 4738 | } |
| 4687 | 4739 | my_result.* += 1; |
| 4688 | 4740 | suspend; |
| ... | ... | @@ -4739,9 +4791,9 @@ async fn amain() void { |
| 4739 | 4791 | } |
| 4740 | 4792 | async fn another() i32 { |
| 4741 | 4793 | seq('c'); |
| 4742 | suspend |p| { | |
| 4794 | suspend { | |
| 4743 | 4795 | seq('d'); |
| 4744 | a_promise = p; | |
| 4796 | a_promise = @handle(); | |
| 4745 | 4797 | } |
| 4746 | 4798 | seq('g'); |
| 4747 | 4799 | return 1234; |
| ... | ... | @@ -5331,6 +5383,16 @@ test "main" { |
| 5331 | 5383 | This function is only valid within function scope. |
| 5332 | 5384 | </p> |
| 5333 | 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 | 5396 | {#header_open|@import#} |
| 5335 | 5397 | <pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre> |
| 5336 | 5398 | <p> |
| ... | ... | @@ -7336,7 +7398,7 @@ Defer(body) = ("defer" | "deferror") body |
| 7336 | 7398 | |
| 7337 | 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 | 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 | 899 | |
| 900 | 900 | struct AstNodeSuspend { |
| 901 | 901 | AstNode *block; |
| 902 | AstNode *promise_symbol; | |
| 903 | 902 | }; |
| 904 | 903 | |
| 905 | 904 | struct AstNodePromiseType { |
| ... | ... | @@ -1358,6 +1357,7 @@ enum BuiltinFnId { |
| 1358 | 1357 | BuiltinFnIdBreakpoint, |
| 1359 | 1358 | BuiltinFnIdReturnAddress, |
| 1360 | 1359 | BuiltinFnIdFrameAddress, |
| 1360 | BuiltinFnIdHandle, | |
| 1361 | 1361 | BuiltinFnIdEmbedFile, |
| 1362 | 1362 | BuiltinFnIdCmpxchgWeak, |
| 1363 | 1363 | BuiltinFnIdCmpxchgStrong, |
| ... | ... | @@ -1716,6 +1716,7 @@ struct CodeGen { |
| 1716 | 1716 | LLVMValueRef coro_save_fn_val; |
| 1717 | 1717 | LLVMValueRef coro_promise_fn_val; |
| 1718 | 1718 | LLVMValueRef coro_alloc_helper_fn_val; |
| 1719 | LLVMValueRef coro_frame_fn_val; | |
| 1719 | 1720 | LLVMValueRef merge_err_ret_traces_fn_val; |
| 1720 | 1721 | LLVMValueRef add_error_return_trace_addr_fn_val; |
| 1721 | 1722 | LLVMValueRef stacksave_fn_val; |
| ... | ... | @@ -2076,6 +2077,7 @@ enum IrInstructionId { |
| 2076 | 2077 | IrInstructionIdBreakpoint, |
| 2077 | 2078 | IrInstructionIdReturnAddress, |
| 2078 | 2079 | IrInstructionIdFrameAddress, |
| 2080 | IrInstructionIdHandle, | |
| 2079 | 2081 | IrInstructionIdAlignOf, |
| 2080 | 2082 | IrInstructionIdOverflowOp, |
| 2081 | 2083 | IrInstructionIdTestErr, |
| ... | ... | @@ -2793,6 +2795,10 @@ struct IrInstructionFrameAddress { |
| 2793 | 2795 | IrInstruction base; |
| 2794 | 2796 | }; |
| 2795 | 2797 | |
| 2798 | struct IrInstructionHandle { | |
| 2799 | IrInstruction base; | |
| 2800 | }; | |
| 2801 | ||
| 2796 | 2802 | enum IrOverflowOp { |
| 2797 | 2803 | IrOverflowOpAdd, |
| 2798 | 2804 | IrOverflowOpSub, |
src/ast_render.cpp-3| ... | ... | @@ -1112,9 +1112,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 1112 | 1112 | { |
| 1113 | 1113 | fprintf(ar->f, "suspend"); |
| 1114 | 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 | 1115 | render_node_grouped(ar, node->data.suspend.block); |
| 1119 | 1116 | } |
| 1120 | 1117 | break; |
src/codegen.cpp+24| ... | ... | @@ -4146,6 +4146,26 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable |
| 4146 | 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 | 4169 | static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { |
| 4150 | 4170 | TypeTableEntry *int_type = instruction->result_ptr_type; |
| 4151 | 4171 | assert(int_type->id == TypeTableEntryIdInt); |
| ... | ... | @@ -4910,6 +4930,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 4910 | 4930 | return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction); |
| 4911 | 4931 | case IrInstructionIdFrameAddress: |
| 4912 | 4932 | return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction); |
| 4933 | case IrInstructionIdHandle: | |
| 4934 | return ir_render_handle(g, executable, (IrInstructionHandle *)instruction); | |
| 4913 | 4935 | case IrInstructionIdOverflowOp: |
| 4914 | 4936 | return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction); |
| 4915 | 4937 | case IrInstructionIdTestErr: |
| ... | ... | @@ -6005,6 +6027,7 @@ static void do_code_gen(CodeGen *g) { |
| 6005 | 6027 | ir_render(g, fn_table_entry); |
| 6006 | 6028 | |
| 6007 | 6029 | } |
| 6030 | ||
| 6008 | 6031 | assert(!g->errors.length); |
| 6009 | 6032 | |
| 6010 | 6033 | if (buf_len(&g->global_asm) != 0) { |
| ... | ... | @@ -6344,6 +6367,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 6344 | 6367 | create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0); |
| 6345 | 6368 | create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0); |
| 6346 | 6369 | create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0); |
| 6370 | create_builtin_fn(g, BuiltinFnIdHandle, "handle", 0); | |
| 6347 | 6371 | create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3); |
| 6348 | 6372 | create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3); |
| 6349 | 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 | 580 | return IrInstructionIdFrameAddress; |
| 581 | 581 | } |
| 582 | 582 | |
| 583 | static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) { | |
| 584 | return IrInstructionIdHandle; | |
| 585 | } | |
| 586 | ||
| 583 | 587 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) { |
| 584 | 588 | return IrInstructionIdAlignOf; |
| 585 | 589 | } |
| ... | ... | @@ -2240,6 +2244,17 @@ static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction |
| 2240 | 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 | 2258 | static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 2244 | 2259 | IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, |
| 2245 | 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 | 3858 | return irb->codegen->invalid_instruction; |
| 3844 | 3859 | } |
| 3845 | 3860 | |
| 3861 | bool is_async = exec_is_async(irb->exec); | |
| 3862 | ||
| 3846 | 3863 | switch (builtin_fn->id) { |
| 3847 | 3864 | case BuiltinFnIdInvalid: |
| 3848 | 3865 | zig_unreachable(); |
| ... | ... | @@ -4475,6 +4492,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4475 | 4492 | return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval); |
| 4476 | 4493 | case BuiltinFnIdFrameAddress: |
| 4477 | 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 | 4505 | case BuiltinFnIdAlignOf: |
| 4479 | 4506 | { |
| 4480 | 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 | 7096 | if (node->data.suspend.block == nullptr) { |
| 7070 | 7097 | suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false); |
| 7071 | 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 | 7099 | Scope *child_scope; |
| 7076 | if (!buf_eql_str(promise_symbol_name, "_")) { | |
| 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); | |
| 7100 | ScopeSuspend *suspend_scope = create_suspend_scope(node, parent_scope); | |
| 7085 | 7101 | suspend_scope->resume_block = resume_block; |
| 7086 | 7102 | child_scope = &suspend_scope->base; |
| 7087 | 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 | 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 | 19034 | static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { |
| 19011 | 19035 | IrInstruction *type_value = instruction->type_value->other; |
| 19012 | 19036 | if (type_is_invalid(type_value->value.type)) |
| ... | ... | @@ -20982,6 +21006,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 20982 | 21006 | return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction); |
| 20983 | 21007 | case IrInstructionIdFrameAddress: |
| 20984 | 21008 | return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction); |
| 21009 | case IrInstructionIdHandle: | |
| 21010 | return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction); | |
| 20985 | 21011 | case IrInstructionIdAlignOf: |
| 20986 | 21012 | return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction); |
| 20987 | 21013 | case IrInstructionIdOverflowOp: |
| ... | ... | @@ -21274,6 +21300,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 21274 | 21300 | case IrInstructionIdAlignOf: |
| 21275 | 21301 | case IrInstructionIdReturnAddress: |
| 21276 | 21302 | case IrInstructionIdFrameAddress: |
| 21303 | case IrInstructionIdHandle: | |
| 21277 | 21304 | case IrInstructionIdTestErr: |
| 21278 | 21305 | case IrInstructionIdUnwrapErrCode: |
| 21279 | 21306 | case IrInstructionIdOptionalWrap: |
src/ir_print.cpp+7| ... | ... | @@ -791,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst |
| 791 | 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 | 798 | static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) { |
| 795 | 799 | fprintf(irp->f, "@returnAddress()"); |
| 796 | 800 | } |
| ... | ... | @@ -1556,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1556 | 1560 | case IrInstructionIdFrameAddress: |
| 1557 | 1561 | ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction); |
| 1558 | 1562 | break; |
| 1563 | case IrInstructionIdHandle: | |
| 1564 | ir_print_handle(irp, (IrInstructionHandle *)instruction); | |
| 1565 | break; | |
| 1559 | 1566 | case IrInstructionIdAlignOf: |
| 1560 | 1567 | ir_print_align_of(irp, (IrInstructionAlignOf *)instruction); |
| 1561 | 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 | 648 | } |
| 649 | 649 | |
| 650 | 650 | /* |
| 651 | SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) | |
| 651 | SuspendExpression(body) = "suspend" option( body ) | |
| 652 | 652 | */ |
| 653 | 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 | 654 | Token *suspend_token = &pc->tokens->at(*token_index); |
| 655 | ||
| 657 | 656 | if (suspend_token->id == TokenIdKeywordSuspend) { |
| 658 | 657 | *token_index += 1; |
| 659 | 658 | } else if (mandatory) { |
| ... | ... | @@ -663,23 +662,18 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b |
| 663 | 662 | return nullptr; |
| 664 | 663 | } |
| 665 | 664 | |
| 666 | Token *bar_token = &pc->tokens->at(*token_index); | |
| 667 | if (bar_token->id == TokenIdBinOr) { | |
| 668 | *token_index += 1; | |
| 665 | Token *lbrace = &pc->tokens->at(*token_index); | |
| 666 | if (lbrace->id == TokenIdLBrace) { | |
| 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 | 670 | } else if (mandatory) { |
| 670 | ast_expect_token(pc, suspend_token, TokenIdBinOr); | |
| 671 | ast_expect_token(pc, lbrace, TokenIdLBrace); | |
| 671 | 672 | zig_unreachable(); |
| 672 | 673 | } else { |
| 673 | *token_index = orig_token_index; | |
| 674 | *token_index -= 1; | |
| 674 | 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 | 3128 | visit_field(&node->data.await_expr.expr, visit, context); |
| 3135 | 3129 | break; |
| 3136 | 3130 | case NodeTypeSuspend: |
| 3137 | visit_field(&node->data.suspend.promise_symbol, visit, context); | |
| 3138 | 3131 | visit_field(&node->data.suspend.block, visit, context); |
| 3139 | 3132 | break; |
| 3140 | 3133 | } |
std/event/channel.zig+12-18| ... | ... | @@ -88,13 +88,11 @@ pub fn Channel(comptime T: type) type { |
| 88 | 88 | /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. |
| 89 | 89 | pub async fn put(self: *SelfChannel, data: T) void { |
| 90 | 90 | // TODO fix this workaround |
| 91 | var my_handle: promise = undefined; | |
| 92 | suspend |p| { | |
| 93 | my_handle = p; | |
| 94 | resume p; | |
| 91 | suspend { | |
| 92 | resume @handle(); | |
| 95 | 93 | } |
| 96 | 94 | |
| 97 | var my_tick_node = Loop.NextTickNode.init(my_handle); | |
| 95 | var my_tick_node = Loop.NextTickNode.init(@handle()); | |
| 98 | 96 | var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{ |
| 99 | 97 | .tick_node = &my_tick_node, |
| 100 | 98 | .data = data, |
| ... | ... | @@ -111,7 +109,7 @@ pub fn Channel(comptime T: type) type { |
| 111 | 109 | self.dispatch(); |
| 112 | 110 | } |
| 113 | 111 | } |
| 114 | suspend |handle| { | |
| 112 | suspend { | |
| 115 | 113 | self.putters.put(&queue_node); |
| 116 | 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 | 121 | /// complete when the next item is put in the channel. |
| 124 | 122 | pub async fn get(self: *SelfChannel) T { |
| 125 | 123 | // TODO fix this workaround |
| 126 | var my_handle: promise = undefined; | |
| 127 | suspend |p| { | |
| 128 | my_handle = p; | |
| 129 | resume p; | |
| 124 | suspend { | |
| 125 | resume @handle(); | |
| 130 | 126 | } |
| 131 | 127 | |
| 132 | 128 | // TODO integrate this function with named return values |
| 133 | 129 | // so we can get rid of this extra result copy |
| 134 | 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 | 132 | var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{ |
| 137 | 133 | .tick_node = &my_tick_node, |
| 138 | 134 | .data = GetNode.Data{ |
| ... | ... | @@ -152,7 +148,7 @@ pub fn Channel(comptime T: type) type { |
| 152 | 148 | } |
| 153 | 149 | } |
| 154 | 150 | |
| 155 | suspend |_| { | |
| 151 | suspend { | |
| 156 | 152 | self.getters.put(&queue_node); |
| 157 | 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 | 172 | /// for data and will not wait for data to be available. |
| 177 | 173 | pub async fn getOrNull(self: *SelfChannel) ?T { |
| 178 | 174 | // TODO fix this workaround |
| 179 | var my_handle: promise = undefined; | |
| 180 | suspend |p| { | |
| 181 | my_handle = p; | |
| 182 | resume p; | |
| 175 | suspend { | |
| 176 | resume @handle(); | |
| 183 | 177 | } |
| 184 | 178 | |
| 185 | 179 | // TODO integrate this function with named return values |
| 186 | 180 | // so we can get rid of this extra result copy |
| 187 | 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 | 183 | var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined); |
| 190 | 184 | var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{ |
| 191 | 185 | .tick_node = &my_tick_node, |
| ... | ... | @@ -211,7 +205,7 @@ pub fn Channel(comptime T: type) type { |
| 211 | 205 | } |
| 212 | 206 | } |
| 213 | 207 | |
| 214 | suspend |_| { | |
| 208 | suspend { | |
| 215 | 209 | self.getters.put(&queue_node); |
| 216 | 210 | _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst); |
| 217 | 211 | self.or_null_queue.put(&or_null_node); |
std/event/future.zig+6-6| ... | ... | @@ -100,8 +100,8 @@ test "std.event.Future" { |
| 100 | 100 | } |
| 101 | 101 | |
| 102 | 102 | async fn testFuture(loop: *Loop) void { |
| 103 | suspend |p| { | |
| 104 | resume p; | |
| 103 | suspend { | |
| 104 | resume @handle(); | |
| 105 | 105 | } |
| 106 | 106 | var future = Future(i32).init(loop); |
| 107 | 107 | |
| ... | ... | @@ -115,15 +115,15 @@ async fn testFuture(loop: *Loop) void { |
| 115 | 115 | } |
| 116 | 116 | |
| 117 | 117 | async fn waitOnFuture(future: *Future(i32)) i32 { |
| 118 | suspend |p| { | |
| 119 | resume p; | |
| 118 | suspend { | |
| 119 | resume @handle(); | |
| 120 | 120 | } |
| 121 | 121 | return (await (async future.get() catch @panic("memory"))).*; |
| 122 | 122 | } |
| 123 | 123 | |
| 124 | 124 | async fn resolveFuture(future: *Future(i32)) void { |
| 125 | suspend |p| { | |
| 126 | resume p; | |
| 125 | suspend { | |
| 126 | resume @handle(); | |
| 127 | 127 | } |
| 128 | 128 | future.data = 6; |
| 129 | 129 | future.resolve(); |
std/event/group.zig+2-2| ... | ... | @@ -65,10 +65,10 @@ pub fn Group(comptime ReturnType: type) type { |
| 65 | 65 | const S = struct { |
| 66 | 66 | async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType { |
| 67 | 67 | // TODO this is a hack to make the memory following be inside the coro frame |
| 68 | suspend |p| { | |
| 68 | suspend { | |
| 69 | 69 | var my_node: Stack.Node = undefined; |
| 70 | 70 | node.* = &my_node; |
| 71 | resume p; | |
| 71 | resume @handle(); | |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | 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 | 92 | |
| 93 | 93 | pub async fn acquire(self: *Lock) Held { |
| 94 | 94 | // TODO explicitly put this memory in the coroutine frame #1194 |
| 95 | var my_handle: promise = undefined; | |
| 96 | suspend |p| { | |
| 97 | my_handle = p; | |
| 98 | resume p; | |
| 95 | suspend { | |
| 96 | resume @handle(); | |
| 99 | 97 | } |
| 100 | var my_tick_node = Loop.NextTickNode.init(my_handle); | |
| 98 | var my_tick_node = Loop.NextTickNode.init(@handle()); | |
| 101 | 99 | |
| 102 | 100 | errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire |
| 103 | 101 | suspend |_| { |
| ... | ... | @@ -110,35 +108,12 @@ pub const Lock = struct { |
| 110 | 108 | // will attempt to grab the lock. |
| 111 | 109 | _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); |
| 112 | 110 | |
| 113 | while (true) { | |
| 114 | const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | |
| 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. | |
| 111 | const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); | |
| 112 | if (old_bit == 0) { | |
| 121 | 113 | if (self.queue.get()) |node| { |
| 122 | 114 | // Whether this node is us or someone else, we tail resume it. |
| 123 | 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 | 143 | |
| 169 | 144 | async fn testLock(loop: *Loop, lock: *Lock) void { |
| 170 | 145 | // TODO explicitly put next tick node memory in the coroutine frame #1194 |
| 171 | suspend |p| { | |
| 172 | resume p; | |
| 146 | suspend { | |
| 147 | resume @handle(); | |
| 173 | 148 | } |
| 174 | 149 | const handle1 = async lockRunner(lock) catch @panic("out of memory"); |
| 175 | 150 | var tick_node1 = Loop.NextTickNode{ |
std/event/loop.zig+7-7| ... | ... | @@ -354,11 +354,11 @@ pub const Loop = struct { |
| 354 | 354 | |
| 355 | 355 | pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void { |
| 356 | 356 | defer self.linuxRemoveFd(fd); |
| 357 | suspend |p| { | |
| 357 | suspend { | |
| 358 | 358 | // TODO explicitly put this memory in the coroutine frame #1194 |
| 359 | 359 | var resume_node = ResumeNode{ |
| 360 | 360 | .id = ResumeNode.Id.Basic, |
| 361 | .handle = p, | |
| 361 | .handle = @handle(), | |
| 362 | 362 | }; |
| 363 | 363 | try self.linuxAddFd(fd, &resume_node, flags); |
| 364 | 364 | } |
| ... | ... | @@ -449,12 +449,12 @@ pub const Loop = struct { |
| 449 | 449 | pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) { |
| 450 | 450 | const S = struct { |
| 451 | 451 | async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType { |
| 452 | suspend |p| { | |
| 453 | handle.* = p; | |
| 452 | suspend { | |
| 453 | handle.* = @handle(); | |
| 454 | 454 | var my_tick_node = Loop.NextTickNode{ |
| 455 | 455 | .prev = undefined, |
| 456 | 456 | .next = undefined, |
| 457 | .data = p, | |
| 457 | .data = @handle(), | |
| 458 | 458 | }; |
| 459 | 459 | loop.onNextTick(&my_tick_node); |
| 460 | 460 | } |
| ... | ... | @@ -472,11 +472,11 @@ pub const Loop = struct { |
| 472 | 472 | /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O |
| 473 | 473 | /// is performed. |
| 474 | 474 | pub async fn yield(self: *Loop) void { |
| 475 | suspend |p| { | |
| 475 | suspend { | |
| 476 | 476 | var my_tick_node = Loop.NextTickNode{ |
| 477 | 477 | .prev = undefined, |
| 478 | 478 | .next = undefined, |
| 479 | .data = p, | |
| 479 | .data = @handle(), | |
| 480 | 480 | }; |
| 481 | 481 | self.onNextTick(&my_tick_node); |
| 482 | 482 | } |
std/event/tcp.zig+4-4| ... | ... | @@ -88,8 +88,8 @@ pub const Server = struct { |
| 88 | 88 | }, |
| 89 | 89 | error.ProcessFdQuotaExceeded => { |
| 90 | 90 | errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node); |
| 91 | suspend |p| { | |
| 92 | self.waiting_for_emfile_node = PromiseNode.init(p); | |
| 91 | suspend { | |
| 92 | self.waiting_for_emfile_node = PromiseNode.init( @handle() ); | |
| 93 | 93 | std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node); |
| 94 | 94 | } |
| 95 | 95 | continue; |
| ... | ... | @@ -141,8 +141,8 @@ test "listen on a port, send bytes, receive bytes" { |
| 141 | 141 | (await next_handler) catch |err| { |
| 142 | 142 | std.debug.panic("unable to handle connection: {}\n", err); |
| 143 | 143 | }; |
| 144 | suspend |p| { | |
| 145 | cancel p; | |
| 144 | suspend { | |
| 145 | cancel @handle(); | |
| 146 | 146 | } |
| 147 | 147 | } |
| 148 | 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 | 18 | OpenBrace, |
| 19 | 19 | CloseBrace, |
| 20 | 20 | FormatString, |
| 21 | Pointer, | |
| 21 | 22 | }; |
| 22 | 23 | |
| 23 | 24 | comptime var start_index = 0; |
| ... | ... | @@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 54 | 55 | state = State.Start; |
| 55 | 56 | start_index = i + 1; |
| 56 | 57 | }, |
| 58 | '*' => state = State.Pointer, | |
| 57 | 59 | else => { |
| 58 | 60 | state = State.FormatString; |
| 59 | 61 | }, |
| ... | ... | @@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), |
| 75 | 77 | }, |
| 76 | 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 | 93 | comptime { |
| ... | ... | @@ -235,6 +248,11 @@ pub fn formatIntValue( |
| 235 | 248 | return formatAsciiChar(value, context, Errors, output); |
| 236 | 249 | } |
| 237 | 250 | }, |
| 251 | 'b' => { | |
| 252 | radix = 2; | |
| 253 | uppercase = false; | |
| 254 | width = 0; | |
| 255 | }, | |
| 238 | 256 | 'd' => { |
| 239 | 257 | radix = 10; |
| 240 | 258 | uppercase = false; |
| ... | ... | @@ -861,6 +879,31 @@ test "fmt.format" { |
| 861 | 879 | const value: u8 = 'a'; |
| 862 | 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 | 907 | try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); |
| 865 | 908 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); |
| 866 | 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 | 120 | try posixRead(fd, buf); |
| 121 | 121 | }, |
| 122 | 122 | Os.windows => { |
| 123 | var hCryptProv: windows.HCRYPTPROV = undefined; | |
| 124 | if (windows.CryptAcquireContextA(&hCryptProv, null, null, windows.PROV_RSA_FULL, 0) == 0) { | |
| 125 | const err = windows.GetLastError(); | |
| 126 | return switch (err) { | |
| 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) { | |
| 123 | // Call RtlGenRandom() instead of CryptGetRandom() on Windows | |
| 124 | // https://github.com/rust-lang-nursery/rand/issues/111 | |
| 125 | // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 | |
| 126 | if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { | |
| 133 | 127 | const err = windows.GetLastError(); |
| 134 | 128 | return switch (err) { |
| 135 | 129 | else => unexpectedErrorWindows(err), |
| ... | ... | @@ -149,8 +143,14 @@ pub fn getRandomBytes(buf: []u8) !void { |
| 149 | 143 | } |
| 150 | 144 | |
| 151 | 145 | test "os.getRandomBytes" { |
| 152 | var buf: [50]u8 = undefined; | |
| 153 | try getRandomBytes(buf[0..]); | |
| 146 | var buf_a: [50]u8 = undefined; | |
| 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 | 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 | 2823 | builtin.Os.macosx => { |
| 2824 | 2824 | var count: c_int = undefined; |
| 2825 | 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 | 2827 | const err = posix.getErrno(rc); |
| 2828 | 2828 | switch (err) { |
| 2829 | 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 | 28 | |
| 29 | 29 | pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, |
| 30 | 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 | 166 | } |
| 167 | 167 | |
| 168 | 168 | test "InvalidDll" { |
| 169 | if (builtin.os != builtin.Os.windows) return; | |
| 169 | if (builtin.os != builtin.Os.windows) return error.SkipZigTest; | |
| 170 | 170 | |
| 171 | 171 | const DllName = "asdf.dll"; |
| 172 | 172 | const allocator = std.debug.global_allocator; |
std/zig/ast.zig-12| ... | ... | @@ -1778,19 +1778,12 @@ pub const Node = struct { |
| 1778 | 1778 | |
| 1779 | 1779 | pub const Suspend = struct { |
| 1780 | 1780 | base: Node, |
| 1781 | label: ?TokenIndex, | |
| 1782 | 1781 | suspend_token: TokenIndex, |
| 1783 | payload: ?*Node, | |
| 1784 | 1782 | body: ?*Node, |
| 1785 | 1783 | |
| 1786 | 1784 | pub fn iterate(self: *Suspend, index: usize) ?*Node { |
| 1787 | 1785 | var i = index; |
| 1788 | 1786 | |
| 1789 | if (self.payload) |payload| { | |
| 1790 | if (i < 1) return payload; | |
| 1791 | i -= 1; | |
| 1792 | } | |
| 1793 | ||
| 1794 | 1787 | if (self.body) |body| { |
| 1795 | 1788 | if (i < 1) return body; |
| 1796 | 1789 | i -= 1; |
| ... | ... | @@ -1800,7 +1793,6 @@ pub const Node = struct { |
| 1800 | 1793 | } |
| 1801 | 1794 | |
| 1802 | 1795 | pub fn firstToken(self: *Suspend) TokenIndex { |
| 1803 | if (self.label) |label| return label; | |
| 1804 | 1796 | return self.suspend_token; |
| 1805 | 1797 | } |
| 1806 | 1798 | |
| ... | ... | @@ -1809,10 +1801,6 @@ pub const Node = struct { |
| 1809 | 1801 | return body.lastToken(); |
| 1810 | 1802 | } |
| 1811 | 1803 | |
| 1812 | if (self.payload) |payload| { | |
| 1813 | return payload.lastToken(); | |
| 1814 | } | |
| 1815 | ||
| 1816 | 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 | 852 | }) catch unreachable; |
| 853 | 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 | 855 | Token.Id.Keyword_inline => { |
| 869 | 856 | stack.append(State{ |
| 870 | 857 | .Inline = InlineCtx{ |
| ... | ... | @@ -1415,10 +1402,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { |
| 1415 | 1402 | }, |
| 1416 | 1403 | |
| 1417 | 1404 | State.SuspendBody => |suspend_node| { |
| 1418 | if (suspend_node.payload != null) { | |
| 1419 | try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); | |
| 1405 | const token = nextToken(&tok_it, &tree); | |
| 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 | 1421 | State.AsyncAllocator => |async_node| { |
| 1424 | 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 | 3084 | Token.Id.Keyword_suspend => { |
| 3087 | 3085 | const node = try arena.create(ast.Node.Suspend{ |
| 3088 | 3086 | .base = ast.Node{ .id = ast.Node.Id.Suspend }, |
| 3089 | .label = null, | |
| 3090 | 3087 | .suspend_token = token_index, |
| 3091 | .payload = null, | |
| 3092 | 3088 | .body = null, |
| 3093 | 3089 | }); |
| 3094 | 3090 | ctx.store(&node.base); |
| 3095 | 3091 | |
| 3096 | 3092 | stack.append(State{ .SuspendBody = node }) catch unreachable; |
| 3097 | try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); | |
| 3098 | 3093 | return true; |
| 3099 | 3094 | }, |
| 3100 | 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 | 898 | ); |
| 899 | 899 | } |
| 900 | 900 | |
| 901 | test "zig fmt: labeled suspend" { | |
| 901 | test "zig fmt: resume from suspend block" { | |
| 902 | 902 | try testCanonical( |
| 903 | 903 | \\fn foo() void { |
| 904 | \\ s: suspend |p| { | |
| 905 | \\ break :s; | |
| 904 | \\ suspend { | |
| 905 | \\ resume @handle(); | |
| 906 | 906 | \\ } |
| 907 | 907 | \\} |
| 908 | 908 | \\ |
| ... | ... | @@ -1784,7 +1784,7 @@ test "zig fmt: coroutines" { |
| 1784 | 1784 | \\ x += 1; |
| 1785 | 1785 | \\ suspend; |
| 1786 | 1786 | \\ x += 1; |
| 1787 | \\ suspend |p| {} | |
| 1787 | \\ suspend; | |
| 1788 | 1788 | \\ const p: promise->void = async simpleAsyncFn() catch unreachable; |
| 1789 | 1789 | \\ await p; |
| 1790 | 1790 | \\} |
std/zig/render.zig+1-15| ... | ... | @@ -323,21 +323,7 @@ fn renderExpression( |
| 323 | 323 | ast.Node.Id.Suspend => { |
| 324 | 324 | const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); |
| 325 | 325 | |
| 326 | if (suspend_node.label) |label| { | |
| 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| { | |
| 326 | if (suspend_node.body) |body| { | |
| 341 | 327 | try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); |
| 342 | 328 | return renderExpression(allocator, stream, tree, indent, start_col, body, space); |
| 343 | 329 | } else { |
test/cases/cancel.zig+2-2| ... | ... | @@ -85,8 +85,8 @@ async fn b4() void { |
| 85 | 85 | defer { |
| 86 | 86 | defer_b4 = true; |
| 87 | 87 | } |
| 88 | suspend |p| { | |
| 89 | b4_handle = p; | |
| 88 | suspend { | |
| 89 | b4_handle = @handle(); | |
| 90 | 90 | } |
| 91 | 91 | suspend; |
| 92 | 92 | } |
test/cases/coroutine_await_struct.zig+2-2| ... | ... | @@ -30,9 +30,9 @@ async fn await_amain() void { |
| 30 | 30 | } |
| 31 | 31 | async fn await_another() Foo { |
| 32 | 32 | await_seq('c'); |
| 33 | suspend |p| { | |
| 33 | suspend { | |
| 34 | 34 | await_seq('d'); |
| 35 | await_a_promise = p; | |
| 35 | await_a_promise = @handle(); | |
| 36 | 36 | } |
| 37 | 37 | await_seq('g'); |
| 38 | 38 | return Foo{ .x = 1234 }; |
test/cases/coroutines.zig+12-7| ... | ... | @@ -62,10 +62,15 @@ test "coroutine suspend with block" { |
| 62 | 62 | var a_promise: promise = undefined; |
| 63 | 63 | var result = false; |
| 64 | 64 | async fn testSuspendBlock() void { |
| 65 | suspend |p| { | |
| 66 | comptime assert(@typeOf(p) == promise->void); | |
| 67 | a_promise = p; | |
| 65 | suspend { | |
| 66 | comptime assert(@typeOf(@handle()) == promise->void); | |
| 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 | 74 | result = true; |
| 70 | 75 | } |
| 71 | 76 | |
| ... | ... | @@ -93,9 +98,9 @@ async fn await_amain() void { |
| 93 | 98 | } |
| 94 | 99 | async fn await_another() i32 { |
| 95 | 100 | await_seq('c'); |
| 96 | suspend |p| { | |
| 101 | suspend { | |
| 97 | 102 | await_seq('d'); |
| 98 | await_a_promise = p; | |
| 103 | await_a_promise = @handle(); | |
| 99 | 104 | } |
| 100 | 105 | await_seq('g'); |
| 101 | 106 | return 1234; |
| ... | ... | @@ -244,8 +249,8 @@ test "break from suspend" { |
| 244 | 249 | std.debug.assert(my_result == 2); |
| 245 | 250 | } |
| 246 | 251 | async fn testBreakFromSuspend(my_result: *i32) void { |
| 247 | suspend |p| { | |
| 248 | resume p; | |
| 252 | suspend { | |
| 253 | resume @handle(); | |
| 249 | 254 | } |
| 250 | 255 | my_result.* += 1; |
| 251 | 256 | suspend; |
test/compile_errors.zig+23-2| ... | ... | @@ -1,6 +1,27 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 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 | 25 | cases.add( |
| 5 | 26 | "while loop body expression ignored", |
| 6 | 27 | \\fn returns() usize { |
| ... | ... | @@ -367,8 +388,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 367 | 388 | \\} |
| 368 | 389 | \\ |
| 369 | 390 | \\async fn foo() void { |
| 370 | \\ suspend |p| { | |
| 371 | \\ suspend |p1| { | |
| 391 | \\ suspend { | |
| 392 | \\ suspend { | |
| 372 | 393 | \\ } |
| 373 | 394 | \\ } |
| 374 | 395 | \\} |