authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-02 17:29:31-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-02 17:29:31-04:00
log65140b2fba4e55d713de506f2bed259ca9410cbf
treee52660c1726caabc730c8869499e5801f027f1b1
parent951124e1772c7013c2b1a674cf98a0b638c36262
parentfb05b96492f4fb1476106bf735788ac16f69c7ef

Merge remote-tracking branch 'origin/master' into async-fs


25 files changed, 314 insertions(+), 186 deletions(-)

doc/langref.html.in+70-8
......@@ -134,6 +134,58 @@ pub fn main() void {
134134 </p>
135135 {#see_also|Values|@import|Errors|Root Source File#}
136136 {#header_close#}
137 {#header_open|Comments#}
138 {#code_begin|test|comments#}
139const assert = @import("std").debug.assert;
140
141test "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).
166const 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#}
137189 {#header_open|Values#}
138190 {#code_begin|exe|values#}
139191const std = @import("std");
......@@ -4638,9 +4690,9 @@ test "coroutine suspend with block" {
46384690var a_promise: promise = undefined;
46394691var result = false;
46404692async 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();
46444696 }
46454697 result = true;
46464698}
......@@ -4681,8 +4733,8 @@ test "resume from suspend" {
46814733 std.debug.assert(my_result == 2);
46824734}
46834735async fn testResumeFromSuspend(my_result: *i32) void {
4684 suspend |p| {
4685 resume p;
4736 suspend {
4737 resume @handle();
46864738 }
46874739 my_result.* += 1;
46884740 suspend;
......@@ -4739,9 +4791,9 @@ async fn amain() void {
47394791}
47404792async fn another() i32 {
47414793 seq('c');
4742 suspend |p| {
4794 suspend {
47434795 seq('d');
4744 a_promise = p;
4796 a_promise = @handle();
47454797 }
47464798 seq('g');
47474799 return 1234;
......@@ -5331,6 +5383,16 @@ test "main" {
53315383 This function is only valid within function scope.
53325384 </p>
53335385 {#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#}
53345396 {#header_open|@import#}
53355397 <pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre>
53365398 <p>
......@@ -7336,7 +7398,7 @@ Defer(body) = ("defer" | "deferror") body
73367398
73377399IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
73387400
7339SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
7401SuspendExpression(body) = "suspend" option( body )
73407402
73417403IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
73427404
src/all_types.hpp+7-1
......@@ -899,7 +899,6 @@ struct AstNodeAwaitExpr {
899899
900900struct AstNodeSuspend {
901901 AstNode *block;
902 AstNode *promise_symbol;
903902};
904903
905904struct AstNodePromiseType {
......@@ -1358,6 +1357,7 @@ enum BuiltinFnId {
13581357 BuiltinFnIdBreakpoint,
13591358 BuiltinFnIdReturnAddress,
13601359 BuiltinFnIdFrameAddress,
1360 BuiltinFnIdHandle,
13611361 BuiltinFnIdEmbedFile,
13621362 BuiltinFnIdCmpxchgWeak,
13631363 BuiltinFnIdCmpxchgStrong,
......@@ -1716,6 +1716,7 @@ struct CodeGen {
17161716 LLVMValueRef coro_save_fn_val;
17171717 LLVMValueRef coro_promise_fn_val;
17181718 LLVMValueRef coro_alloc_helper_fn_val;
1719 LLVMValueRef coro_frame_fn_val;
17191720 LLVMValueRef merge_err_ret_traces_fn_val;
17201721 LLVMValueRef add_error_return_trace_addr_fn_val;
17211722 LLVMValueRef stacksave_fn_val;
......@@ -2076,6 +2077,7 @@ enum IrInstructionId {
20762077 IrInstructionIdBreakpoint,
20772078 IrInstructionIdReturnAddress,
20782079 IrInstructionIdFrameAddress,
2080 IrInstructionIdHandle,
20792081 IrInstructionIdAlignOf,
20802082 IrInstructionIdOverflowOp,
20812083 IrInstructionIdTestErr,
......@@ -2793,6 +2795,10 @@ struct IrInstructionFrameAddress {
27932795 IrInstruction base;
27942796};
27952797
2798struct IrInstructionHandle {
2799 IrInstruction base;
2800};
2801
27962802enum IrOverflowOp {
27972803 IrOverflowOpAdd,
27982804 IrOverflowOpSub,
src/ast_render.cpp-3
......@@ -1112,9 +1112,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
11121112 {
11131113 fprintf(ar->f, "suspend");
11141114 if (node->data.suspend.block != nullptr) {
1115 fprintf(ar->f, " |");
1116 render_node_grouped(ar, node->data.suspend.promise_symbol);
1117 fprintf(ar->f, "| ");
11181115 render_node_grouped(ar, node->data.suspend.block);
11191116 }
11201117 break;
src/codegen.cpp+24
......@@ -4146,6 +4146,26 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable
41464146 return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, "");
41474147}
41484148
4149static LLVMValueRef get_handle_fn_val(CodeGen *g) {
4150 if (g->coro_frame_fn_val)
4151 return g->coro_frame_fn_val;
4152
4153 LLVMTypeRef fn_type = LLVMFunctionType( LLVMPointerType(LLVMInt8Type(), 0)
4154 , nullptr, 0, false);
4155 Buf *name = buf_sprintf("llvm.coro.frame");
4156 g->coro_frame_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type);
4157 assert(LLVMGetIntrinsicID(g->coro_frame_fn_val));
4158
4159 return g->coro_frame_fn_val;
4160}
4161
4162static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable,
4163 IrInstructionHandle *instruction)
4164{
4165 LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_promise->type_ref);
4166 return LLVMBuildCall(g->builder, get_handle_fn_val(g), &zero, 0, "");
4167}
4168
41494169static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) {
41504170 TypeTableEntry *int_type = instruction->result_ptr_type;
41514171 assert(int_type->id == TypeTableEntryIdInt);
......@@ -4910,6 +4930,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
49104930 return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction);
49114931 case IrInstructionIdFrameAddress:
49124932 return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction);
4933 case IrInstructionIdHandle:
4934 return ir_render_handle(g, executable, (IrInstructionHandle *)instruction);
49134935 case IrInstructionIdOverflowOp:
49144936 return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);
49154937 case IrInstructionIdTestErr:
......@@ -6005,6 +6027,7 @@ static void do_code_gen(CodeGen *g) {
60056027 ir_render(g, fn_table_entry);
60066028
60076029 }
6030
60086031 assert(!g->errors.length);
60096032
60106033 if (buf_len(&g->global_asm) != 0) {
......@@ -6344,6 +6367,7 @@ static void define_builtin_fns(CodeGen *g) {
63446367 create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0);
63456368 create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0);
63466369 create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0);
6370 create_builtin_fn(g, BuiltinFnIdHandle, "handle", 0);
63476371 create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3);
63486372 create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3);
63496373 create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1);
src/ir.cpp+39-12
......@@ -580,6 +580,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *)
580580 return IrInstructionIdFrameAddress;
581581}
582582
583static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) {
584 return IrInstructionIdHandle;
585}
586
583587static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
584588 return IrInstructionIdAlignOf;
585589}
......@@ -2240,6 +2244,17 @@ static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction
22402244 return new_instruction;
22412245}
22422246
2247static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) {
2248 IrInstructionHandle *instruction = ir_build_instruction<IrInstructionHandle>(irb, scope, source_node);
2249 return &instruction->base;
2250}
2251
2252static IrInstruction *ir_build_handle_from(IrBuilder *irb, IrInstruction *old_instruction) {
2253 IrInstruction *new_instruction = ir_build_handle(irb, old_instruction->scope, old_instruction->source_node);
2254 ir_link_new_instruction(new_instruction, old_instruction);
2255 return new_instruction;
2256}
2257
22432258static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node,
22442259 IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,
22452260 IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)
......@@ -3843,6 +3858,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
38433858 return irb->codegen->invalid_instruction;
38443859 }
38453860
3861 bool is_async = exec_is_async(irb->exec);
3862
38463863 switch (builtin_fn->id) {
38473864 case BuiltinFnIdInvalid:
38483865 zig_unreachable();
......@@ -4475,6 +4492,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
44754492 return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval);
44764493 case BuiltinFnIdFrameAddress:
44774494 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);
44784505 case BuiltinFnIdAlignOf:
44794506 {
44804507 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
70697096 if (node->data.suspend.block == nullptr) {
70707097 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
70717098 } 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;
70757099 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);
70857101 suspend_scope->resume_block = resume_block;
70867102 child_scope = &suspend_scope->base;
70877103 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
1900719023 return u8_ptr_const;
1900819024}
1900919025
19026static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) {
19027 ir_build_handle_from(&ira->new_irb, &instruction->base);
19028
19029 FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
19030 assert(fn_entry != nullptr);
19031 return get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type);
19032}
19033
1901019034static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
1901119035 IrInstruction *type_value = instruction->type_value->other;
1901219036 if (type_is_invalid(type_value->value.type))
......@@ -20982,6 +21006,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
2098221006 return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);
2098321007 case IrInstructionIdFrameAddress:
2098421008 return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);
21009 case IrInstructionIdHandle:
21010 return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction);
2098521011 case IrInstructionIdAlignOf:
2098621012 return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);
2098721013 case IrInstructionIdOverflowOp:
......@@ -21274,6 +21300,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
2127421300 case IrInstructionIdAlignOf:
2127521301 case IrInstructionIdReturnAddress:
2127621302 case IrInstructionIdFrameAddress:
21303 case IrInstructionIdHandle:
2127721304 case IrInstructionIdTestErr:
2127821305 case IrInstructionIdUnwrapErrCode:
2127921306 case IrInstructionIdOptionalWrap:
src/ir_print.cpp+7
......@@ -791,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst
791791 fprintf(irp->f, "@frameAddress()");
792792}
793793
794static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) {
795 fprintf(irp->f, "@handle()");
796}
797
794798static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) {
795799 fprintf(irp->f, "@returnAddress()");
796800}
......@@ -1556,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
15561560 case IrInstructionIdFrameAddress:
15571561 ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction);
15581562 break;
1563 case IrInstructionIdHandle:
1564 ir_print_handle(irp, (IrInstructionHandle *)instruction);
1565 break;
15591566 case IrInstructionIdAlignOf:
15601567 ir_print_align_of(irp, (IrInstructionAlignOf *)instruction);
15611568 break;
src/parser.cpp+9-16
......@@ -648,12 +648,11 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
648648}
649649
650650/*
651SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
651SuspendExpression(body) = "suspend" option( body )
652652*/
653653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
654 size_t orig_token_index = *token_index;
655
656654 Token *suspend_token = &pc->tokens->at(*token_index);
655
657656 if (suspend_token->id == TokenIdKeywordSuspend) {
658657 *token_index += 1;
659658 } else if (mandatory) {
......@@ -663,23 +662,18 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
663662 return nullptr;
664663 }
665664
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;
669670 } else if (mandatory) {
670 ast_expect_token(pc, suspend_token, TokenIdBinOr);
671 ast_expect_token(pc, lbrace, TokenIdLBrace);
671672 zig_unreachable();
672673 } else {
673 *token_index = orig_token_index;
674 *token_index -= 1;
674675 return nullptr;
675676 }
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;
683677}
684678
685679/*
......@@ -3134,7 +3128,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
31343128 visit_field(&node->data.await_expr.expr, visit, context);
31353129 break;
31363130 case NodeTypeSuspend:
3137 visit_field(&node->data.suspend.promise_symbol, visit, context);
31383131 visit_field(&node->data.suspend.block, visit, context);
31393132 break;
31403133 }
std/event/channel.zig+12-18
......@@ -88,13 +88,11 @@ pub fn Channel(comptime T: type) type {
8888 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
8989 pub async fn put(self: *SelfChannel, data: T) void {
9090 // 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();
9593 }
9694
97 var my_tick_node = Loop.NextTickNode.init(my_handle);
95 var my_tick_node = Loop.NextTickNode.init(@handle());
9896 var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{
9997 .tick_node = &my_tick_node,
10098 .data = data,
......@@ -111,7 +109,7 @@ pub fn Channel(comptime T: type) type {
111109 self.dispatch();
112110 }
113111 }
114 suspend |handle| {
112 suspend {
115113 self.putters.put(&queue_node);
116114 _ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
117115
......@@ -123,16 +121,14 @@ pub fn Channel(comptime T: type) type {
123121 /// complete when the next item is put in the channel.
124122 pub async fn get(self: *SelfChannel) T {
125123 // 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();
130126 }
131127
132128 // TODO integrate this function with named return values
133129 // so we can get rid of this extra result copy
134130 var result: T = undefined;
135 var my_tick_node = Loop.NextTickNode.init(my_handle);
131 var my_tick_node = Loop.NextTickNode.init(@handle());
136132 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
137133 .tick_node = &my_tick_node,
138134 .data = GetNode.Data{
......@@ -152,7 +148,7 @@ pub fn Channel(comptime T: type) type {
152148 }
153149 }
154150
155 suspend |_| {
151 suspend {
156152 self.getters.put(&queue_node);
157153 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
158154
......@@ -176,16 +172,14 @@ pub fn Channel(comptime T: type) type {
176172 /// for data and will not wait for data to be available.
177173 pub async fn getOrNull(self: *SelfChannel) ?T {
178174 // 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();
183177 }
184178
185179 // TODO integrate this function with named return values
186180 // so we can get rid of this extra result copy
187181 var result: ?T = null;
188 var my_tick_node = Loop.NextTickNode.init(my_handle);
182 var my_tick_node = Loop.NextTickNode.init(@handle());
189183 var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined);
190184 var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
191185 .tick_node = &my_tick_node,
......@@ -211,7 +205,7 @@ pub fn Channel(comptime T: type) type {
211205 }
212206 }
213207
214 suspend |_| {
208 suspend {
215209 self.getters.put(&queue_node);
216210 _ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
217211 self.or_null_queue.put(&or_null_node);
std/event/future.zig+6-6
......@@ -100,8 +100,8 @@ test "std.event.Future" {
100100}
101101
102102async fn testFuture(loop: *Loop) void {
103 suspend |p| {
104 resume p;
103 suspend {
104 resume @handle();
105105 }
106106 var future = Future(i32).init(loop);
107107
......@@ -115,15 +115,15 @@ async fn testFuture(loop: *Loop) void {
115115}
116116
117117async fn waitOnFuture(future: *Future(i32)) i32 {
118 suspend |p| {
119 resume p;
118 suspend {
119 resume @handle();
120120 }
121121 return (await (async future.get() catch @panic("memory"))).*;
122122}
123123
124124async fn resolveFuture(future: *Future(i32)) void {
125 suspend |p| {
126 resume p;
125 suspend {
126 resume @handle();
127127 }
128128 future.data = 6;
129129 future.resolve();
std/event/group.zig+2-2
......@@ -65,10 +65,10 @@ pub fn Group(comptime ReturnType: type) type {
6565 const S = struct {
6666 async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType {
6767 // TODO this is a hack to make the memory following be inside the coro frame
68 suspend |p| {
68 suspend {
6969 var my_node: Stack.Node = undefined;
7070 node.* = &my_node;
71 resume p;
71 resume @handle();
7272 }
7373
7474 // 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 {
9292
9393 pub async fn acquire(self: *Lock) Held {
9494 // 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();
9997 }
100 var my_tick_node = Loop.NextTickNode.init(my_handle);
98 var my_tick_node = Loop.NextTickNode.init(@handle());
10199
102100 errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
103101 suspend |_| {
......@@ -110,35 +108,12 @@ pub const Lock = struct {
110108 // will attempt to grab the lock.
111109 _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
112110
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) {
121113 if (self.queue.get()) |node| {
122114 // Whether this node is us or someone else, we tail resume it.
123115 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 }
140116 }
141 unreachable;
142117 }
143118 }
144119
......@@ -168,8 +143,8 @@ test "std.event.Lock" {
168143
169144async fn testLock(loop: *Loop, lock: *Lock) void {
170145 // TODO explicitly put next tick node memory in the coroutine frame #1194
171 suspend |p| {
172 resume p;
146 suspend {
147 resume @handle();
173148 }
174149 const handle1 = async lockRunner(lock) catch @panic("out of memory");
175150 var tick_node1 = Loop.NextTickNode{
std/event/loop.zig+7-7
......@@ -354,11 +354,11 @@ pub const Loop = struct {
354354
355355 pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
356356 defer self.linuxRemoveFd(fd);
357 suspend |p| {
357 suspend {
358358 // TODO explicitly put this memory in the coroutine frame #1194
359359 var resume_node = ResumeNode{
360360 .id = ResumeNode.Id.Basic,
361 .handle = p,
361 .handle = @handle(),
362362 };
363363 try self.linuxAddFd(fd, &resume_node, flags);
364364 }
......@@ -449,12 +449,12 @@ pub const Loop = struct {
449449 pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) {
450450 const S = struct {
451451 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();
454454 var my_tick_node = Loop.NextTickNode{
455455 .prev = undefined,
456456 .next = undefined,
457 .data = p,
457 .data = @handle(),
458458 };
459459 loop.onNextTick(&my_tick_node);
460460 }
......@@ -472,11 +472,11 @@ pub const Loop = struct {
472472 /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O
473473 /// is performed.
474474 pub async fn yield(self: *Loop) void {
475 suspend |p| {
475 suspend {
476476 var my_tick_node = Loop.NextTickNode{
477477 .prev = undefined,
478478 .next = undefined,
479 .data = p,
479 .data = @handle(),
480480 };
481481 self.onNextTick(&my_tick_node);
482482 }
std/event/tcp.zig+4-4
......@@ -88,8 +88,8 @@ pub const Server = struct {
8888 },
8989 error.ProcessFdQuotaExceeded => {
9090 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() );
9393 std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
9494 }
9595 continue;
......@@ -141,8 +141,8 @@ test "listen on a port, send bytes, receive bytes" {
141141 (await next_handler) catch |err| {
142142 std.debug.panic("unable to handle connection: {}\n", err);
143143 };
144 suspend |p| {
145 cancel p;
144 suspend {
145 cancel @handle();
146146 }
147147 }
148148 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),
1818 OpenBrace,
1919 CloseBrace,
2020 FormatString,
21 Pointer,
2122 };
2223
2324 comptime var start_index = 0;
......@@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
5455 state = State.Start;
5556 start_index = i + 1;
5657 },
58 '*' => state = State.Pointer,
5759 else => {
5860 state = State.FormatString;
5961 },
......@@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
7577 },
7678 else => {},
7779 },
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 },
7891 }
7992 }
8093 comptime {
......@@ -235,6 +248,11 @@ pub fn formatIntValue(
235248 return formatAsciiChar(value, context, Errors, output);
236249 }
237250 },
251 'b' => {
252 radix = 2;
253 uppercase = false;
254 width = 0;
255 },
238256 'd' => {
239257 radix = 10;
240258 uppercase = false;
......@@ -861,6 +879,31 @@ test "fmt.format" {
861879 const value: u8 = 'a';
862880 try testFmt("u8: a\n", "u8: {c}\n", value);
863881 }
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 }
864907 try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
865908 try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
866909 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 {
120120 try posixRead(fd, buf);
121121 },
122122 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) {
133127 const err = windows.GetLastError();
134128 return switch (err) {
135129 else => unexpectedErrorWindows(err),
......@@ -149,8 +143,14 @@ pub fn getRandomBytes(buf: []u8) !void {
149143}
150144
151145test "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) );
154154}
155155
156156/// Raises a signal in the current kernel thread, ending its execution.
......@@ -2823,7 +2823,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize {
28232823 builtin.Os.macosx => {
28242824 var count: c_int = undefined;
28252825 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);
28272827 const err = posix.getErrno(rc);
28282828 switch (err) {
28292829 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,
2828
2929pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,
3030 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 */
34pub extern "advapi32" stdcallcc fn SystemFunction036(output: [*]u8, length: usize) BOOL;
35pub const RtlGenRandom = SystemFunction036;
std/os/windows/util.zig+1-1
......@@ -166,7 +166,7 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
166166}
167167
168168test "InvalidDll" {
169 if (builtin.os != builtin.Os.windows) return;
169 if (builtin.os != builtin.Os.windows) return error.SkipZigTest;
170170
171171 const DllName = "asdf.dll";
172172 const allocator = std.debug.global_allocator;
std/zig/ast.zig-12
......@@ -1778,19 +1778,12 @@ pub const Node = struct {
17781778
17791779 pub const Suspend = struct {
17801780 base: Node,
1781 label: ?TokenIndex,
17821781 suspend_token: TokenIndex,
1783 payload: ?*Node,
17841782 body: ?*Node,
17851783
17861784 pub fn iterate(self: *Suspend, index: usize) ?*Node {
17871785 var i = index;
17881786
1789 if (self.payload) |payload| {
1790 if (i < 1) return payload;
1791 i -= 1;
1792 }
1793
17941787 if (self.body) |body| {
17951788 if (i < 1) return body;
17961789 i -= 1;
......@@ -1800,7 +1793,6 @@ pub const Node = struct {
18001793 }
18011794
18021795 pub fn firstToken(self: *Suspend) TokenIndex {
1803 if (self.label) |label| return label;
18041796 return self.suspend_token;
18051797 }
18061798
......@@ -1809,10 +1801,6 @@ pub const Node = struct {
18091801 return body.lastToken();
18101802 }
18111803
1812 if (self.payload) |payload| {
1813 return payload.lastToken();
1814 }
1815
18161804 return self.suspend_token;
18171805 }
18181806 };
std/zig/parse.zig+14-19
......@@ -852,19 +852,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
852852 }) catch unreachable;
853853 continue;
854854 },
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 },
868855 Token.Id.Keyword_inline => {
869856 stack.append(State{
870857 .Inline = InlineCtx{
......@@ -1415,10 +1402,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
14151402 },
14161403
14171404 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 },
14201419 }
1421 continue;
14221420 },
14231421 State.AsyncAllocator => |async_node| {
14241422 if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) {
......@@ -3086,15 +3084,12 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con
30863084 Token.Id.Keyword_suspend => {
30873085 const node = try arena.create(ast.Node.Suspend{
30883086 .base = ast.Node{ .id = ast.Node.Id.Suspend },
3089 .label = null,
30903087 .suspend_token = token_index,
3091 .payload = null,
30923088 .body = null,
30933089 });
30943090 ctx.store(&node.base);
30953091
30963092 stack.append(State{ .SuspendBody = node }) catch unreachable;
3097 try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } });
30983093 return true;
30993094 },
31003095 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" {
898898 );
899899}
900900
901test "zig fmt: labeled suspend" {
901test "zig fmt: resume from suspend block" {
902902 try testCanonical(
903903 \\fn foo() void {
904 \\ s: suspend |p| {
905 \\ break :s;
904 \\ suspend {
905 \\ resume @handle();
906906 \\ }
907907 \\}
908908 \\
......@@ -1784,7 +1784,7 @@ test "zig fmt: coroutines" {
17841784 \\ x += 1;
17851785 \\ suspend;
17861786 \\ x += 1;
1787 \\ suspend |p| {}
1787 \\ suspend;
17881788 \\ const p: promise->void = async simpleAsyncFn() catch unreachable;
17891789 \\ await p;
17901790 \\}
std/zig/render.zig+1-15
......@@ -323,21 +323,7 @@ fn renderExpression(
323323 ast.Node.Id.Suspend => {
324324 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
325325
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| {
341327 try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space);
342328 return renderExpression(allocator, stream, tree, indent, start_col, body, space);
343329 } else {
test/cases/cancel.zig+2-2
......@@ -85,8 +85,8 @@ async fn b4() void {
8585 defer {
8686 defer_b4 = true;
8787 }
88 suspend |p| {
89 b4_handle = p;
88 suspend {
89 b4_handle = @handle();
9090 }
9191 suspend;
9292}
test/cases/coroutine_await_struct.zig+2-2
......@@ -30,9 +30,9 @@ async fn await_amain() void {
3030}
3131async fn await_another() Foo {
3232 await_seq('c');
33 suspend |p| {
33 suspend {
3434 await_seq('d');
35 await_a_promise = p;
35 await_a_promise = @handle();
3636 }
3737 await_seq('g');
3838 return Foo{ .x = 1234 };
test/cases/coroutines.zig+12-7
......@@ -62,10 +62,15 @@ test "coroutine suspend with block" {
6262var a_promise: promise = undefined;
6363var result = false;
6464async 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();
6868 }
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
6974 result = true;
7075}
7176
......@@ -93,9 +98,9 @@ async fn await_amain() void {
9398}
9499async fn await_another() i32 {
95100 await_seq('c');
96 suspend |p| {
101 suspend {
97102 await_seq('d');
98 await_a_promise = p;
103 await_a_promise = @handle();
99104 }
100105 await_seq('g');
101106 return 1234;
......@@ -244,8 +249,8 @@ test "break from suspend" {
244249 std.debug.assert(my_result == 2);
245250}
246251async fn testBreakFromSuspend(my_result: *i32) void {
247 suspend |p| {
248 resume p;
252 suspend {
253 resume @handle();
249254 }
250255 my_result.* += 1;
251256 suspend;
test/compile_errors.zig+23-2
......@@ -1,6 +1,27 @@
11const tests = @import("tests.zig");
22
33pub 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
425 cases.add(
526 "while loop body expression ignored",
627 \\fn returns() usize {
......@@ -367,8 +388,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
367388 \\}
368389 \\
369390 \\async fn foo() void {
370 \\ suspend |p| {
371 \\ suspend |p1| {
391 \\ suspend {
392 \\ suspend {
372393 \\ }
373394 \\ }
374395 \\}