authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-04 15:19:03-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-08-04 15:19:03-04:00
logb48948d6e805cdee7f33243098af9a862c152df1
tree8b1b4b37670f7444e8fe12748a8db99ac7498cce
parentf804310d9f953c9d78a4271ba8d75133341840e6
parent9bd8b01650f9cf21e601117951711b21aa5fd216

Merge branch 'master' into llvm7


32 files changed, 366 insertions(+), 176 deletions(-)

.gitignore+11
...@@ -1,3 +1,14 @@...@@ -1,3 +1,14 @@
1# This file is for zig-specific build artifacts.
2# If you have OS-specific or editor-specific files to ignore,
3# such as *.swp or .DS_Store, put those in your global
4# ~/.gitignore and put this in your ~/.gitconfig:
5#
6# [core]
7# excludesfile = ~/.gitignore
8#
9# Cheers!
10# -andrewrk
11
1zig-cache/12zig-cache/
2build/13build/
3build-*/14build-*/
doc/langref.html.in+18-8
...@@ -4690,9 +4690,9 @@ test "coroutine suspend with block" {...@@ -4690,9 +4690,9 @@ test "coroutine suspend with block" {
4690var a_promise: promise = undefined;4690var a_promise: promise = undefined;
4691var result = false;4691var result = false;
4692async fn testSuspendBlock() void {4692async fn testSuspendBlock() void {
4693 suspend |p| {4693 suspend {
4694 comptime assert(@typeOf(p) == promise->void);4694 comptime assert(@typeOf(@handle()) == promise->void);
4695 a_promise = p;4695 a_promise = @handle();
4696 }4696 }
4697 result = true;4697 result = true;
4698}4698}
...@@ -4733,8 +4733,8 @@ test "resume from suspend" {...@@ -4733,8 +4733,8 @@ test "resume from suspend" {
4733 std.debug.assert(my_result == 2);4733 std.debug.assert(my_result == 2);
4734}4734}
4735async fn testResumeFromSuspend(my_result: *i32) void {4735async fn testResumeFromSuspend(my_result: *i32) void {
4736 suspend |p| {4736 suspend {
4737 resume p;4737 resume @handle();
4738 }4738 }
4739 my_result.* += 1;4739 my_result.* += 1;
4740 suspend;4740 suspend;
...@@ -4791,9 +4791,9 @@ async fn amain() void {...@@ -4791,9 +4791,9 @@ async fn amain() void {
4791}4791}
4792async fn another() i32 {4792async fn another() i32 {
4793 seq('c');4793 seq('c');
4794 suspend |p| {4794 suspend {
4795 seq('d');4795 seq('d');
4796 a_promise = p;4796 a_promise = @handle();
4797 }4797 }
4798 seq('g');4798 seq('g');
4799 return 1234;4799 return 1234;
...@@ -5383,6 +5383,16 @@ test "main" {...@@ -5383,6 +5383,16 @@ test "main" {
5383 This function is only valid within function scope.5383 This function is only valid within function scope.
5384 </p>5384 </p>
5385 {#header_close#}5385 {#header_close#}
5386 {#header_open|@handle#}
5387 <pre><code class="zig">@handle()</code></pre>
5388 <p>
5389 This function returns a <code>promise->T</code> type, where <code>T</code>
5390 is the return type of the async function in scope.
5391 </p>
5392 <p>
5393 This function is only valid within an async function scope.
5394 </p>
5395 {#header_close#}
5386 {#header_open|@import#}5396 {#header_open|@import#}
5387 <pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre>5397 <pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre>
5388 <p>5398 <p>
...@@ -7388,7 +7398,7 @@ Defer(body) = ("defer" | "deferror") body...@@ -7388,7 +7398,7 @@ Defer(body) = ("defer" | "deferror") body
73887398
7389IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))7399IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
73907400
7391SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))7401SuspendExpression(body) = "suspend" option( body )
73927402
7393IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)7403IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
73947404
src/all_types.hpp+7-1
...@@ -899,7 +899,6 @@ struct AstNodeAwaitExpr {...@@ -899,7 +899,6 @@ struct AstNodeAwaitExpr {
899899
900struct AstNodeSuspend {900struct AstNodeSuspend {
901 AstNode *block;901 AstNode *block;
902 AstNode *promise_symbol;
903};902};
904903
905struct AstNodePromiseType {904struct AstNodePromiseType {
...@@ -1358,6 +1357,7 @@ enum BuiltinFnId {...@@ -1358,6 +1357,7 @@ enum BuiltinFnId {
1358 BuiltinFnIdBreakpoint,1357 BuiltinFnIdBreakpoint,
1359 BuiltinFnIdReturnAddress,1358 BuiltinFnIdReturnAddress,
1360 BuiltinFnIdFrameAddress,1359 BuiltinFnIdFrameAddress,
1360 BuiltinFnIdHandle,
1361 BuiltinFnIdEmbedFile,1361 BuiltinFnIdEmbedFile,
1362 BuiltinFnIdCmpxchgWeak,1362 BuiltinFnIdCmpxchgWeak,
1363 BuiltinFnIdCmpxchgStrong,1363 BuiltinFnIdCmpxchgStrong,
...@@ -1714,6 +1714,7 @@ struct CodeGen {...@@ -1714,6 +1714,7 @@ struct CodeGen {
1714 LLVMValueRef coro_save_fn_val;1714 LLVMValueRef coro_save_fn_val;
1715 LLVMValueRef coro_promise_fn_val;1715 LLVMValueRef coro_promise_fn_val;
1716 LLVMValueRef coro_alloc_helper_fn_val;1716 LLVMValueRef coro_alloc_helper_fn_val;
1717 LLVMValueRef coro_frame_fn_val;
1717 LLVMValueRef merge_err_ret_traces_fn_val;1718 LLVMValueRef merge_err_ret_traces_fn_val;
1718 LLVMValueRef add_error_return_trace_addr_fn_val;1719 LLVMValueRef add_error_return_trace_addr_fn_val;
1719 LLVMValueRef stacksave_fn_val;1720 LLVMValueRef stacksave_fn_val;
...@@ -2074,6 +2075,7 @@ enum IrInstructionId {...@@ -2074,6 +2075,7 @@ enum IrInstructionId {
2074 IrInstructionIdBreakpoint,2075 IrInstructionIdBreakpoint,
2075 IrInstructionIdReturnAddress,2076 IrInstructionIdReturnAddress,
2076 IrInstructionIdFrameAddress,2077 IrInstructionIdFrameAddress,
2078 IrInstructionIdHandle,
2077 IrInstructionIdAlignOf,2079 IrInstructionIdAlignOf,
2078 IrInstructionIdOverflowOp,2080 IrInstructionIdOverflowOp,
2079 IrInstructionIdTestErr,2081 IrInstructionIdTestErr,
...@@ -2791,6 +2793,10 @@ struct IrInstructionFrameAddress {...@@ -2791,6 +2793,10 @@ struct IrInstructionFrameAddress {
2791 IrInstruction base;2793 IrInstruction base;
2792};2794};
27932795
2796struct IrInstructionHandle {
2797 IrInstruction base;
2798};
2799
2794enum IrOverflowOp {2800enum IrOverflowOp {
2795 IrOverflowOpAdd,2801 IrOverflowOpAdd,
2796 IrOverflowOpSub,2802 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
...@@ -4057,6 +4057,26 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable...@@ -4057,6 +4057,26 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable
4057 return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, "");4057 return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, "");
4058}4058}
40594059
4060static LLVMValueRef get_handle_fn_val(CodeGen *g) {
4061 if (g->coro_frame_fn_val)
4062 return g->coro_frame_fn_val;
4063
4064 LLVMTypeRef fn_type = LLVMFunctionType( LLVMPointerType(LLVMInt8Type(), 0)
4065 , nullptr, 0, false);
4066 Buf *name = buf_sprintf("llvm.coro.frame");
4067 g->coro_frame_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type);
4068 assert(LLVMGetIntrinsicID(g->coro_frame_fn_val));
4069
4070 return g->coro_frame_fn_val;
4071}
4072
4073static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable,
4074 IrInstructionHandle *instruction)
4075{
4076 LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_promise->type_ref);
4077 return LLVMBuildCall(g->builder, get_handle_fn_val(g), &zero, 0, "");
4078}
4079
4060static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) {4080static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) {
4061 TypeTableEntry *int_type = instruction->result_ptr_type;4081 TypeTableEntry *int_type = instruction->result_ptr_type;
4062 assert(int_type->id == TypeTableEntryIdInt);4082 assert(int_type->id == TypeTableEntryIdInt);
...@@ -4821,6 +4841,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4821,6 +4841,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4821 return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction);4841 return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction);
4822 case IrInstructionIdFrameAddress:4842 case IrInstructionIdFrameAddress:
4823 return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction);4843 return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction);
4844 case IrInstructionIdHandle:
4845 return ir_render_handle(g, executable, (IrInstructionHandle *)instruction);
4824 case IrInstructionIdOverflowOp:4846 case IrInstructionIdOverflowOp:
4825 return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);4847 return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);
4826 case IrInstructionIdTestErr:4848 case IrInstructionIdTestErr:
...@@ -5916,6 +5938,7 @@ static void do_code_gen(CodeGen *g) {...@@ -5916,6 +5938,7 @@ static void do_code_gen(CodeGen *g) {
5916 ir_render(g, fn_table_entry);5938 ir_render(g, fn_table_entry);
59175939
5918 }5940 }
5941
5919 assert(!g->errors.length);5942 assert(!g->errors.length);
59205943
5921 if (buf_len(&g->global_asm) != 0) {5944 if (buf_len(&g->global_asm) != 0) {
...@@ -6255,6 +6278,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -6255,6 +6278,7 @@ static void define_builtin_fns(CodeGen *g) {
6255 create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0);6278 create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0);
6256 create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0);6279 create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0);
6257 create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0);6280 create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0);
6281 create_builtin_fn(g, BuiltinFnIdHandle, "handle", 0);
6258 create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3);6282 create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3);
6259 create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3);6283 create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3);
6260 create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1);6284 create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1);
src/ir.cpp+64-14
...@@ -580,6 +580,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *)...@@ -580,6 +580,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *)
580 return IrInstructionIdFrameAddress;580 return IrInstructionIdFrameAddress;
581}581}
582582
583static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) {
584 return IrInstructionIdHandle;
585}
586
583static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {587static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
584 return IrInstructionIdAlignOf;588 return IrInstructionIdAlignOf;
585}589}
...@@ -2240,6 +2244,17 @@ static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction...@@ -2240,6 +2244,17 @@ static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction
2240 return new_instruction;2244 return new_instruction;
2241}2245}
22422246
2247static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) {
2248 IrInstructionHandle *instruction = ir_build_instruction<IrInstructionHandle>(irb, scope, source_node);
2249 return &instruction->base;
2250}
2251
2252static IrInstruction *ir_build_handle_from(IrBuilder *irb, IrInstruction *old_instruction) {
2253 IrInstruction *new_instruction = ir_build_handle(irb, old_instruction->scope, old_instruction->source_node);
2254 ir_link_new_instruction(new_instruction, old_instruction);
2255 return new_instruction;
2256}
2257
2243static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node,2258static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node,
2244 IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,2259 IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,
2245 IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)2260 IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)
...@@ -3317,7 +3332,15 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco...@@ -3317,7 +3332,15 @@ static VariableTableEntry *create_local_var(CodeGen *codegen, AstNode *node, Sco
3317static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name,3332static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *scope, Buf *name,
3318 bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime)3333 bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstruction *is_comptime)
3319{3334{
3320 VariableTableEntry *var = create_local_var(irb->codegen, node, scope, name, src_is_const, gen_is_const, is_shadowable, is_comptime);3335 bool is_underscored = name ? buf_eql_str(name, "_") : false;
3336 VariableTableEntry *var = create_local_var( irb->codegen
3337 , node
3338 , scope
3339 , (is_underscored ? nullptr : name)
3340 , src_is_const
3341 , gen_is_const
3342 , (is_underscored ? true : is_shadowable)
3343 , is_comptime );
3321 if (is_comptime != nullptr || gen_is_const) {3344 if (is_comptime != nullptr || gen_is_const) {
3322 var->mem_slot_index = exec_next_mem_slot(irb->exec);3345 var->mem_slot_index = exec_next_mem_slot(irb->exec);
3323 var->owner_exec = irb->exec;3346 var->owner_exec = irb->exec;
...@@ -3843,6 +3866,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3843,6 +3866,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3843 return irb->codegen->invalid_instruction;3866 return irb->codegen->invalid_instruction;
3844 }3867 }
38453868
3869 bool is_async = exec_is_async(irb->exec);
3870
3846 switch (builtin_fn->id) {3871 switch (builtin_fn->id) {
3847 case BuiltinFnIdInvalid:3872 case BuiltinFnIdInvalid:
3848 zig_unreachable();3873 zig_unreachable();
...@@ -4475,6 +4500,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4475,6 +4500,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);4500 return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval);
4476 case BuiltinFnIdFrameAddress:4501 case BuiltinFnIdFrameAddress:
4477 return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval);4502 return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval);
4503 case BuiltinFnIdHandle:
4504 if (!irb->exec->fn_entry) {
4505 add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition"));
4506 return irb->codegen->invalid_instruction;
4507 }
4508 if (!is_async) {
4509 add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function"));
4510 return irb->codegen->invalid_instruction;
4511 }
4512 return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval);
4478 case BuiltinFnIdAlignOf:4513 case BuiltinFnIdAlignOf:
4479 {4514 {
4480 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4515 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -5159,6 +5194,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod...@@ -5159,6 +5194,11 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
51595194
5160 AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;5195 AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
51615196
5197 if (buf_eql_str(variable_declaration->symbol, "_")) {
5198 add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol"));
5199 return irb->codegen->invalid_instruction;
5200 }
5201
5162 IrInstruction *type_instruction;5202 IrInstruction *type_instruction;
5163 if (variable_declaration->type != nullptr) {5203 if (variable_declaration->type != nullptr) {
5164 type_instruction = ir_gen_node(irb, variable_declaration->type, scope);5204 type_instruction = ir_gen_node(irb, variable_declaration->type, scope);
...@@ -5171,6 +5211,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod...@@ -5171,6 +5211,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
5171 bool is_shadowable = false;5211 bool is_shadowable = false;
5172 bool is_const = variable_declaration->is_const;5212 bool is_const = variable_declaration->is_const;
5173 bool is_extern = variable_declaration->is_extern;5213 bool is_extern = variable_declaration->is_extern;
5214
5174 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node,5215 IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node,
5175 ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime);5216 ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime);
5176 VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol,5217 VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol,
...@@ -7069,19 +7110,8 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -7069,19 +7110,8 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
7069 if (node->data.suspend.block == nullptr) {7110 if (node->data.suspend.block == nullptr) {
7070 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);7111 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
7071 } else {7112 } 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;7113 Scope *child_scope;
7076 if (!buf_eql_str(promise_symbol_name, "_")) {7114 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;7115 suspend_scope->resume_block = resume_block;
7086 child_scope = &suspend_scope->base;7116 child_scope = &suspend_scope->base;
7087 IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);7117 IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);
...@@ -9598,6 +9628,9 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un...@@ -9598,6 +9628,9 @@ static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value, Un
9598 case ConstValSpecialStatic:9628 case ConstValSpecialStatic:
9599 return &value->value;9629 return &value->value;
9600 case ConstValSpecialRuntime:9630 case ConstValSpecialRuntime:
9631 if (!type_has_bits(value->value.type)) {
9632 return &value->value;
9633 }
9601 ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));9634 ir_add_error(ira, value, buf_sprintf("unable to evaluate constant expression"));
9602 return nullptr;9635 return nullptr;
9603 case ConstValSpecialUndef:9636 case ConstValSpecialUndef:
...@@ -16099,8 +16132,14 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir...@@ -16099,8 +16132,14 @@ static TypeTableEntry *ir_analyze_container_init_fields_union(IrAnalyze *ira, Ir
16099 if (casted_field_value == ira->codegen->invalid_instruction)16132 if (casted_field_value == ira->codegen->invalid_instruction)
16100 return ira->codegen->builtin_types.entry_invalid;16133 return ira->codegen->builtin_types.entry_invalid;
1610116134
16135 type_ensure_zero_bits_known(ira->codegen, casted_field_value->value.type);
16136 if (type_is_invalid(casted_field_value->value.type))
16137 return ira->codegen->builtin_types.entry_invalid;
16138
16102 bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);16139 bool is_comptime = ir_should_inline(ira->new_irb.exec, instruction->scope);
16103 if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime) {16140 if (is_comptime || casted_field_value->value.special != ConstValSpecialRuntime ||
16141 !type_has_bits(casted_field_value->value.type))
16142 {
16104 ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);16143 ConstExprValue *field_val = ir_resolve_const(ira, casted_field_value, UndefOk);
16105 if (!field_val)16144 if (!field_val)
16106 return ira->codegen->builtin_types.entry_invalid;16145 return ira->codegen->builtin_types.entry_invalid;
...@@ -19007,6 +19046,14 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn...@@ -19007,6 +19046,14 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn
19007 return u8_ptr_const;19046 return u8_ptr_const;
19008}19047}
1900919048
19049static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) {
19050 ir_build_handle_from(&ira->new_irb, &instruction->base);
19051
19052 FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
19053 assert(fn_entry != nullptr);
19054 return get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type);
19055}
19056
19010static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {19057static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
19011 IrInstruction *type_value = instruction->type_value->other;19058 IrInstruction *type_value = instruction->type_value->other;
19012 if (type_is_invalid(type_value->value.type))19059 if (type_is_invalid(type_value->value.type))
...@@ -20982,6 +21029,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -20982,6 +21029,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
20982 return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);21029 return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);
20983 case IrInstructionIdFrameAddress:21030 case IrInstructionIdFrameAddress:
20984 return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);21031 return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);
21032 case IrInstructionIdHandle:
21033 return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction);
20985 case IrInstructionIdAlignOf:21034 case IrInstructionIdAlignOf:
20986 return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);21035 return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);
20987 case IrInstructionIdOverflowOp:21036 case IrInstructionIdOverflowOp:
...@@ -21274,6 +21323,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -21274,6 +21323,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
21274 case IrInstructionIdAlignOf:21323 case IrInstructionIdAlignOf:
21275 case IrInstructionIdReturnAddress:21324 case IrInstructionIdReturnAddress:
21276 case IrInstructionIdFrameAddress:21325 case IrInstructionIdFrameAddress:
21326 case IrInstructionIdHandle:
21277 case IrInstructionIdTestErr:21327 case IrInstructionIdTestErr:
21278 case IrInstructionIdUnwrapErrCode:21328 case IrInstructionIdUnwrapErrCode:
21279 case IrInstructionIdOptionalWrap:21329 case IrInstructionIdOptionalWrap:
src/ir_print.cpp+7
...@@ -791,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst...@@ -791,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst
791 fprintf(irp->f, "@frameAddress()");791 fprintf(irp->f, "@frameAddress()");
792}792}
793793
794static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) {
795 fprintf(irp->f, "@handle()");
796}
797
794static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) {798static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) {
795 fprintf(irp->f, "@returnAddress()");799 fprintf(irp->f, "@returnAddress()");
796}800}
...@@ -1556,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1556,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1556 case IrInstructionIdFrameAddress:1560 case IrInstructionIdFrameAddress:
1557 ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction);1561 ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction);
1558 break;1562 break;
1563 case IrInstructionIdHandle:
1564 ir_print_handle(irp, (IrInstructionHandle *)instruction);
1565 break;
1559 case IrInstructionIdAlignOf:1566 case IrInstructionIdAlignOf:
1560 ir_print_align_of(irp, (IrInstructionAlignOf *)instruction);1567 ir_print_align_of(irp, (IrInstructionAlignOf *)instruction);
1561 break;1568 break;
src/parser.cpp+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}
649649
650/*650/*
651SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))651SuspendExpression(body) = "suspend" option( body )
652*/652*/
653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
654 size_t orig_token_index = *token_index;
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 }
665664
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}
684678
685/*679/*
...@@ -3134,7 +3128,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont...@@ -3134,7 +3128,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
3134 visit_field(&node->data.await_expr.expr, visit, context);3128 visit_field(&node->data.await_expr.expr, visit, context);
3135 break;3129 break;
3136 case NodeTypeSuspend:3130 case NodeTypeSuspend:
3137 visit_field(&node->data.suspend.promise_symbol, visit, context);
3138 visit_field(&node->data.suspend.block, visit, context);3131 visit_field(&node->data.suspend.block, visit, context);
3139 break;3132 break;
3140 }3133 }
std/event/channel.zig+4-4
...@@ -71,10 +71,10 @@ pub fn Channel(comptime T: type) type {...@@ -71,10 +71,10 @@ pub fn Channel(comptime T: type) type {
71 /// puts a data item in the channel. The promise completes when the value has been added to the71 /// puts a data item in the channel. The promise completes when the value has been added to the
72 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.72 /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
73 pub async fn put(self: *SelfChannel, data: T) void {73 pub async fn put(self: *SelfChannel, data: T) void {
74 suspend |handle| {74 suspend {
75 var my_tick_node = Loop.NextTickNode{75 var my_tick_node = Loop.NextTickNode{
76 .next = undefined,76 .next = undefined,
77 .data = handle,77 .data = @handle(),
78 };78 };
79 var queue_node = std.atomic.Queue(PutNode).Node{79 var queue_node = std.atomic.Queue(PutNode).Node{
80 .data = PutNode{80 .data = PutNode{
...@@ -96,10 +96,10 @@ pub fn Channel(comptime T: type) type {...@@ -96,10 +96,10 @@ pub fn Channel(comptime T: type) type {
96 // TODO integrate this function with named return values96 // TODO integrate this function with named return values
97 // so we can get rid of this extra result copy97 // so we can get rid of this extra result copy
98 var result: T = undefined;98 var result: T = undefined;
99 suspend |handle| {99 suspend {
100 var my_tick_node = Loop.NextTickNode{100 var my_tick_node = Loop.NextTickNode{
101 .next = undefined,101 .next = undefined,
102 .data = handle,102 .data = @handle(),
103 };103 };
104 var queue_node = std.atomic.Queue(GetNode).Node{104 var queue_node = std.atomic.Queue(GetNode).Node{
105 .data = GetNode{105 .data = GetNode{
std/event/future.zig+6-6
...@@ -100,8 +100,8 @@ test "std.event.Future" {...@@ -100,8 +100,8 @@ test "std.event.Future" {
100}100}
101101
102async fn testFuture(loop: *Loop) void {102async fn testFuture(loop: *Loop) void {
103 suspend |p| {103 suspend {
104 resume p;104 resume @handle();
105 }105 }
106 var future = Future(i32).init(loop);106 var future = Future(i32).init(loop);
107107
...@@ -115,15 +115,15 @@ async fn testFuture(loop: *Loop) void {...@@ -115,15 +115,15 @@ async fn testFuture(loop: *Loop) void {
115}115}
116116
117async fn waitOnFuture(future: *Future(i32)) i32 {117async fn waitOnFuture(future: *Future(i32)) i32 {
118 suspend |p| {118 suspend {
119 resume p;119 resume @handle();
120 }120 }
121 return (await (async future.get() catch @panic("memory"))).*;121 return (await (async future.get() catch @panic("memory"))).*;
122}122}
123123
124async fn resolveFuture(future: *Future(i32)) void {124async fn resolveFuture(future: *Future(i32)) void {
125 suspend |p| {125 suspend {
126 resume p;126 resume @handle();
127 }127 }
128 future.data = 6;128 future.data = 6;
129 future.resolve();129 future.resolve();
std/event/group.zig+2-2
...@@ -54,10 +54,10 @@ pub fn Group(comptime ReturnType: type) type {...@@ -54,10 +54,10 @@ pub fn Group(comptime ReturnType: type) type {
54 const S = struct {54 const S = struct {
55 async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType {55 async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType {
56 // TODO this is a hack to make the memory following be inside the coro frame56 // TODO this is a hack to make the memory following be inside the coro frame
57 suspend |p| {57 suspend {
58 var my_node: Stack.Node = undefined;58 var my_node: Stack.Node = undefined;
59 node.* = &my_node;59 node.* = &my_node;
60 resume p;60 resume @handle();
61 }61 }
6262
63 // TODO this allocation elision should be guaranteed because we await it in63 // TODO this allocation elision should be guaranteed because we await it in
std/event/lock.zig+6-29
...@@ -90,10 +90,10 @@ pub const Lock = struct {...@@ -90,10 +90,10 @@ pub const Lock = struct {
90 }90 }
9191
92 pub async fn acquire(self: *Lock) Held {92 pub async fn acquire(self: *Lock) Held {
93 suspend |handle| {93 suspend {
94 // TODO explicitly put this memory in the coroutine frame #119494 // TODO explicitly put this memory in the coroutine frame #1194
95 var my_tick_node = Loop.NextTickNode{95 var my_tick_node = Loop.NextTickNode{
96 .data = handle,96 .data = @handle(),
97 .next = undefined,97 .next = undefined,
98 };98 };
9999
...@@ -106,35 +106,12 @@ pub const Lock = struct {...@@ -106,35 +106,12 @@ pub const Lock = struct {
106 // will attempt to grab the lock.106 // will attempt to grab the lock.
107 _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);107 _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
108108
109 while (true) {109 const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
110 const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);110 if (old_bit == 0) {
111 if (old_bit != 0) {
112 // We did not obtain the lock. Trust that our queue entry will resume us, and allow
113 // suspend to complete.
114 break;
115 }
116 // We got the lock. However we might have already been resumed from the queue.
117 if (self.queue.get()) |node| {111 if (self.queue.get()) |node| {
118 // Whether this node is us or someone else, we tail resume it.112 // Whether this node is us or someone else, we tail resume it.
119 resume node.data;113 resume node.data;
120 break;
121 } else {
122 // We already got resumed, and there are none left in the queue, which means that
123 // we aren't even supposed to hold the lock right now.
124 _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
125 _ = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
126
127 // There might be a queue item. If we know the queue is empty, we can be done,
128 // because the other actor will try to obtain the lock.
129 // But if there's a queue item, we are the actor which must loop and attempt
130 // to grab the lock again.
131 if (@atomicLoad(u8, &self.queue_empty_bit, AtomicOrder.SeqCst) == 1) {
132 break;
133 } else {
134 continue;
135 }
136 }114 }
137 unreachable;
138 }115 }
139 }116 }
140117
...@@ -164,8 +141,8 @@ test "std.event.Lock" {...@@ -164,8 +141,8 @@ test "std.event.Lock" {
164141
165async fn testLock(loop: *Loop, lock: *Lock) void {142async fn testLock(loop: *Loop, lock: *Lock) void {
166 // TODO explicitly put next tick node memory in the coroutine frame #1194143 // TODO explicitly put next tick node memory in the coroutine frame #1194
167 suspend |p| {144 suspend {
168 resume p;145 resume @handle();
169 }146 }
170 const handle1 = async lockRunner(lock) catch @panic("out of memory");147 const handle1 = async lockRunner(lock) catch @panic("out of memory");
171 var tick_node1 = Loop.NextTickNode{148 var tick_node1 = Loop.NextTickNode{
std/event/loop.zig+7-7
...@@ -331,11 +331,11 @@ pub const Loop = struct {...@@ -331,11 +331,11 @@ pub const Loop = struct {
331331
332 pub async fn waitFd(self: *Loop, fd: i32) !void {332 pub async fn waitFd(self: *Loop, fd: i32) !void {
333 defer self.removeFd(fd);333 defer self.removeFd(fd);
334 suspend |p| {334 suspend {
335 // TODO explicitly put this memory in the coroutine frame #1194335 // TODO explicitly put this memory in the coroutine frame #1194
336 var resume_node = ResumeNode{336 var resume_node = ResumeNode{
337 .id = ResumeNode.Id.Basic,337 .id = ResumeNode.Id.Basic,
338 .handle = p,338 .handle = @handle(),
339 };339 };
340 try self.addFd(fd, &resume_node);340 try self.addFd(fd, &resume_node);
341 }341 }
...@@ -417,11 +417,11 @@ pub const Loop = struct {...@@ -417,11 +417,11 @@ pub const Loop = struct {
417 pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) {417 pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) {
418 const S = struct {418 const S = struct {
419 async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType {419 async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType {
420 suspend |p| {420 suspend {
421 handle.* = p;421 handle.* = @handle();
422 var my_tick_node = Loop.NextTickNode{422 var my_tick_node = Loop.NextTickNode{
423 .next = undefined,423 .next = undefined,
424 .data = p,424 .data = @handle(),
425 };425 };
426 loop.onNextTick(&my_tick_node);426 loop.onNextTick(&my_tick_node);
427 }427 }
...@@ -439,10 +439,10 @@ pub const Loop = struct {...@@ -439,10 +439,10 @@ pub const Loop = struct {
439 /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O439 /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O
440 /// is performed.440 /// is performed.
441 pub async fn yield(self: *Loop) void {441 pub async fn yield(self: *Loop) void {
442 suspend |p| {442 suspend {
443 var my_tick_node = Loop.NextTickNode{443 var my_tick_node = Loop.NextTickNode{
444 .next = undefined,444 .next = undefined,
445 .data = p,445 .data = @handle(),
446 };446 };
447 self.onNextTick(&my_tick_node);447 self.onNextTick(&my_tick_node);
448 }448 }
std/event/tcp.zig+4-4
...@@ -88,8 +88,8 @@ pub const Server = struct {...@@ -88,8 +88,8 @@ pub const Server = struct {
88 },88 },
89 error.ProcessFdQuotaExceeded => {89 error.ProcessFdQuotaExceeded => {
90 errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);90 errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
91 suspend |p| {91 suspend {
92 self.waiting_for_emfile_node = PromiseNode.init(p);92 self.waiting_for_emfile_node = PromiseNode.init( @handle() );
93 std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);93 std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
94 }94 }
95 continue;95 continue;
...@@ -141,8 +141,8 @@ test "listen on a port, send bytes, receive bytes" {...@@ -141,8 +141,8 @@ test "listen on a port, send bytes, receive bytes" {
141 (await next_handler) catch |err| {141 (await next_handler) catch |err| {
142 std.debug.panic("unable to handle connection: {}\n", err);142 std.debug.panic("unable to handle connection: {}\n", err);
143 };143 };
144 suspend |p| {144 suspend {
145 cancel p;145 cancel @handle();
146 }146 }
147 }147 }
148 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {148 async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {
std/fmt/index.zig+9
...@@ -248,6 +248,11 @@ pub fn formatIntValue(...@@ -248,6 +248,11 @@ pub fn formatIntValue(
248 return formatAsciiChar(value, context, Errors, output);248 return formatAsciiChar(value, context, Errors, output);
249 }249 }
250 },250 },
251 'b' => {
252 radix = 2;
253 uppercase = false;
254 width = 0;
255 },
251 'd' => {256 'd' => {
252 radix = 10;257 radix = 10;
253 uppercase = false;258 uppercase = false;
...@@ -874,6 +879,10 @@ test "fmt.format" {...@@ -874,6 +879,10 @@ test "fmt.format" {
874 const value: u8 = 'a';879 const value: u8 = 'a';
875 try testFmt("u8: a\n", "u8: {c}\n", value);880 try testFmt("u8: a\n", "u8: {c}\n", value);
876 }881 }
882 {
883 const value: u8 = 0b1100;
884 try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value);
885 }
877 {886 {
878 const value: [3]u8 = "abc";887 const value: [3]u8 = "abc";
879 try testFmt("array: abc\n", "array: {}\n", value);888 try testFmt("array: abc\n", "array: {}\n", value);
std/os/index.zig+13-13
...@@ -130,16 +130,10 @@ pub fn getRandomBytes(buf: []u8) !void {...@@ -130,16 +130,10 @@ pub fn getRandomBytes(buf: []u8) !void {
130 try posixRead(fd, buf);130 try posixRead(fd, buf);
131 },131 },
132 Os.windows => {132 Os.windows => {
133 var hCryptProv: windows.HCRYPTPROV = undefined;133 // Call RtlGenRandom() instead of CryptGetRandom() on Windows
134 if (windows.CryptAcquireContextA(&hCryptProv, null, null, windows.PROV_RSA_FULL, 0) == 0) {134 // https://github.com/rust-lang-nursery/rand/issues/111
135 const err = windows.GetLastError();135 // https://bugzilla.mozilla.org/show_bug.cgi?id=504270
136 return switch (err) {136 if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) {
137 else => unexpectedErrorWindows(err),
138 };
139 }
140 defer _ = windows.CryptReleaseContext(hCryptProv, 0);
141
142 if (windows.CryptGenRandom(hCryptProv, @intCast(windows.DWORD, buf.len), buf.ptr) == 0) {
143 const err = windows.GetLastError();137 const err = windows.GetLastError();
144 return switch (err) {138 return switch (err) {
145 else => unexpectedErrorWindows(err),139 else => unexpectedErrorWindows(err),
...@@ -159,8 +153,14 @@ pub fn getRandomBytes(buf: []u8) !void {...@@ -159,8 +153,14 @@ pub fn getRandomBytes(buf: []u8) !void {
159}153}
160154
161test "os.getRandomBytes" {155test "os.getRandomBytes" {
162 var buf: [50]u8 = undefined;156 var buf_a: [50]u8 = undefined;
163 try getRandomBytes(buf[0..]);157 var buf_b: [50]u8 = undefined;
158 // Call Twice
159 try getRandomBytes(buf_a[0..]);
160 try getRandomBytes(buf_b[0..]);
161
162 // Check if random (not 100% conclusive)
163 assert( !mem.eql(u8, buf_a, buf_b) );
164}164}
165165
166/// Raises a signal in the current kernel thread, ending its execution.166/// Raises a signal in the current kernel thread, ending its execution.
...@@ -2790,7 +2790,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize {...@@ -2790,7 +2790,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize {
2790 builtin.Os.macosx => {2790 builtin.Os.macosx => {
2791 var count: c_int = undefined;2791 var count: c_int = undefined;
2792 var count_len: usize = @sizeOf(c_int);2792 var count_len: usize = @sizeOf(c_int);
2793 const rc = posix.sysctlbyname(c"hw.ncpu", @ptrCast(*c_void, &count), &count_len, null, 0);2793 const rc = posix.sysctlbyname(c"hw.logicalcpu", @ptrCast(*c_void, &count), &count_len, null, 0);
2794 const err = posix.getErrno(rc);2794 const err = posix.getErrno(rc);
2795 switch (err) {2795 switch (err) {
2796 0 => return @intCast(usize, count),2796 0 => return @intCast(usize, count),
std/os/linux/index.zig+1-1
...@@ -944,7 +944,7 @@ pub fn setgroups(size: usize, list: *const u32) usize {...@@ -944,7 +944,7 @@ pub fn setgroups(size: usize, list: *const u32) usize {
944}944}
945945
946pub fn getpid() i32 {946pub fn getpid() i32 {
947 return @bitCast(i32, u32(syscall0(SYS_getpid)));947 return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid)));
948}948}
949949
950pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize {950pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize {
std/os/linux/test.zig+4
...@@ -3,6 +3,10 @@ const builtin = @import("builtin");...@@ -3,6 +3,10 @@ const builtin = @import("builtin");
3const linux = std.os.linux;3const linux = std.os.linux;
4const assert = std.debug.assert;4const assert = std.debug.assert;
55
6test "getpid" {
7 assert(linux.getpid() != 0);
8}
9
6test "timer" {10test "timer" {
7 const epoll_fd = linux.epoll_create();11 const epoll_fd = linux.epoll_create();
8 var err = linux.getErrno(epoll_fd);12 var err = linux.getErrno(epoll_fd);
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,
2828
29pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,29pub 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 */
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 {...@@ -166,7 +166,7 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
166}166}
167167
168test "InvalidDll" {168test "InvalidDll" {
169 if (builtin.os != builtin.Os.windows) return;169 if (builtin.os != builtin.Os.windows) return error.SkipZigTest;
170170
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/rand/index.zig+5-4
...@@ -30,7 +30,7 @@ pub const DefaultCsprng = Isaac64;...@@ -30,7 +30,7 @@ pub const DefaultCsprng = Isaac64;
30pub const Random = struct {30pub const Random = struct {
31 fillFn: fn (r: *Random, buf: []u8) void,31 fillFn: fn (r: *Random, buf: []u8) void,
3232
33 /// Read random bytes into the specified buffer until fill.33 /// Read random bytes into the specified buffer until full.
34 pub fn bytes(r: *Random, buf: []u8) void {34 pub fn bytes(r: *Random, buf: []u8) void {
35 r.fillFn(r, buf);35 r.fillFn(r, buf);
36 }36 }
...@@ -48,10 +48,10 @@ pub const Random = struct {...@@ -48,10 +48,10 @@ pub const Random = struct {
48 }48 }
49 }49 }
5050
51 /// Get a random unsigned integer with even distribution between `start`51 /// Return a random integer with even distribution between `start`
52 /// inclusive and `end` exclusive.52 /// inclusive and `end` exclusive. `start` must be less than `end`.
53 pub fn range(r: *Random, comptime T: type, start: T, end: T) T {53 pub fn range(r: *Random, comptime T: type, start: T, end: T) T {
54 assert(start <= end);54 assert(start < end);
55 if (T.is_signed) {55 if (T.is_signed) {
56 const uint = @IntType(false, T.bit_count);56 const uint = @IntType(false, T.bit_count);
57 if (start >= 0 and end >= 0) {57 if (start >= 0 and end >= 0) {
...@@ -664,6 +664,7 @@ test "Random range" {...@@ -664,6 +664,7 @@ test "Random range" {
664 testRange(&prng.random, -4, 3);664 testRange(&prng.random, -4, 3);
665 testRange(&prng.random, -4, -1);665 testRange(&prng.random, -4, -1);
666 testRange(&prng.random, 10, 14);666 testRange(&prng.random, 10, 14);
667 // TODO: test that prng.random.range(1, 1) causes an assertion error
667}668}
668669
669fn testRange(r: *Random, start: i32, end: i32) void {670fn testRange(r: *Random, start: i32, end: i32) void {
std/zig/ast.zig-12
...@@ -1778,19 +1778,12 @@ pub const Node = struct {...@@ -1778,19 +1778,12 @@ pub const Node = struct {
17781778
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,
17851783
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;
17881786
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 }
18011794
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 }
18061798
...@@ -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 }
18111803
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 },
14161403
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);
30953091
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}
900900
901test "zig fmt: labeled suspend" {901test "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);
325325
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/behavior.zig+1
...@@ -60,6 +60,7 @@ comptime {...@@ -60,6 +60,7 @@ comptime {
60 _ = @import("cases/try.zig");60 _ = @import("cases/try.zig");
61 _ = @import("cases/type_info.zig");61 _ = @import("cases/type_info.zig");
62 _ = @import("cases/undefined.zig");62 _ = @import("cases/undefined.zig");
63 _ = @import("cases/underscore.zig");
63 _ = @import("cases/union.zig");64 _ = @import("cases/union.zig");
64 _ = @import("cases/var_args.zig");65 _ = @import("cases/var_args.zig");
65 _ = @import("cases/void.zig");66 _ = @import("cases/void.zig");
test/cases/cancel.zig+2-2
...@@ -85,8 +85,8 @@ async fn b4() void {...@@ -85,8 +85,8 @@ async fn b4() void {
85 defer {85 defer {
86 defer_b4 = true;86 defer_b4 = true;
87 }87 }
88 suspend |p| {88 suspend {
89 b4_handle = p;89 b4_handle = @handle();
90 }90 }
91 suspend;91 suspend;
92}92}
test/cases/coroutine_await_struct.zig+2-2
...@@ -30,9 +30,9 @@ async fn await_amain() void {...@@ -30,9 +30,9 @@ async fn await_amain() void {
30}30}
31async fn await_another() Foo {31async fn await_another() Foo {
32 await_seq('c');32 await_seq('c');
33 suspend |p| {33 suspend {
34 await_seq('d');34 await_seq('d');
35 await_a_promise = p;35 await_a_promise = @handle();
36 }36 }
37 await_seq('g');37 await_seq('g');
38 return Foo{ .x = 1234 };38 return Foo{ .x = 1234 };
test/cases/coroutines.zig+12-7
...@@ -62,10 +62,15 @@ test "coroutine suspend with block" {...@@ -62,10 +62,15 @@ test "coroutine suspend with block" {
62var a_promise: promise = undefined;62var a_promise: promise = undefined;
63var result = false;63var result = false;
64async fn testSuspendBlock() void {64async fn testSuspendBlock() void {
65 suspend |p| {65 suspend {
66 comptime assert(@typeOf(p) == promise->void);66 comptime assert(@typeOf(@handle()) == promise->void);
67 a_promise = p;67 a_promise = @handle();
68 }68 }
69
70 //Test to make sure that @handle() works as advertised (issue #1296)
71 //var our_handle: promise = @handle();
72 assert( a_promise == @handle() );
73
69 result = true;74 result = true;
70}75}
7176
...@@ -93,9 +98,9 @@ async fn await_amain() void {...@@ -93,9 +98,9 @@ async fn await_amain() void {
93}98}
94async fn await_another() i32 {99async fn await_another() i32 {
95 await_seq('c');100 await_seq('c');
96 suspend |p| {101 suspend {
97 await_seq('d');102 await_seq('d');
98 await_a_promise = p;103 await_a_promise = @handle();
99 }104 }
100 await_seq('g');105 await_seq('g');
101 return 1234;106 return 1234;
...@@ -244,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}
246async fn testBreakFromSuspend(my_result: *i32) void {251async fn testBreakFromSuspend(my_result: *i32) void {
247 suspend |p| {252 suspend {
248 resume p;253 resume @handle();
249 }254 }
250 my_result.* += 1;255 my_result.* += 1;
251 suspend;256 suspend;
test/cases/underscore.zig created+28
...@@ -0,0 +1,28 @@
1const std = @import("std");
2const assert = std.debug.assert;
3
4test "ignore lval with underscore" {
5 _ = false;
6}
7
8test "ignore lval with underscore (for loop)" {
9 for ([]void{}) |_, i| {
10 for ([]void{}) |_, j| {
11 break;
12 }
13 break;
14 }
15}
16
17test "ignore lval with underscore (while loop)" {
18 while (optionalReturnError()) |_| {
19 while (optionalReturnError()) |_| {
20 break;
21 } else |_| { }
22 break;
23 } else |_| { }
24}
25
26fn optionalReturnError() !?u32 {
27 return error.optionalReturnError;
28}
test/cases/union.zig+14
...@@ -297,3 +297,17 @@ test "access a member of tagged union with conflicting enum tag name" {...@@ -297,3 +297,17 @@ test "access a member of tagged union with conflicting enum tag name" {
297297
298 comptime assert(Bar.A == u8);298 comptime assert(Bar.A == u8);
299}299}
300
301test "tagged union initialization with runtime void" {
302 assert(testTaggedUnionInit({}));
303}
304
305const TaggedUnionWithAVoid = union(enum) {
306 A,
307 B: i32,
308};
309
310fn testTaggedUnionInit(x: var) bool {
311 const y = TaggedUnionWithAVoid{ .A = x };
312 return @TagType(TaggedUnionWithAVoid)(y) == TaggedUnionWithAVoid.A;
313}
test/compile_errors.zig+81-2
...@@ -1,6 +1,85 @@...@@ -1,6 +1,85 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompileErrorContext) void {3pub 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
25 cases.add(
26 "`_` is not a declarable symbol",
27 \\export fn f1() usize {
28 \\ var _: usize = 2;
29 \\ return _;
30 \\}
31 ,
32 ".tmp_source.zig:2:5: error: `_` is not a declarable symbol",
33 ".tmp_source.zig:3:12: error: use of undeclared identifier '_'",
34 );
35
36 cases.add(
37 "`_` should not be usable inside for",
38 \\export fn returns() void {
39 \\ for ([]void{}) |_, i| {
40 \\ for ([]void{}) |_, j| {
41 \\ return _;
42 \\ }
43 \\ }
44 \\}
45 ,
46 ".tmp_source.zig:4:20: error: use of undeclared identifier '_'",
47 );
48
49 cases.add(
50 "`_` should not be usable inside while",
51 \\export fn returns() void {
52 \\ while (optionalReturn()) |_| {
53 \\ while (optionalReturn()) |_| {
54 \\ return _;
55 \\ }
56 \\ }
57 \\}
58 \\fn optionalReturn() ?u32 {
59 \\ return 1;
60 \\}
61 ,
62 ".tmp_source.zig:4:20: error: use of undeclared identifier '_'",
63 );
64
65 cases.add(
66 "`_` should not be usable inside while else",
67 \\export fn returns() void {
68 \\ while (optionalReturnError()) |_| {
69 \\ while (optionalReturnError()) |_| {
70 \\ return;
71 \\ } else |_| {
72 \\ if (_ == error.optionalReturnError) return;
73 \\ }
74 \\ }
75 \\}
76 \\fn optionalReturnError() !?u32 {
77 \\ return error.optionalReturnError;
78 \\}
79 ,
80 ".tmp_source.zig:6:17: error: use of undeclared identifier '_'",
81 );
82
4 cases.add(83 cases.add(
5 "while loop body expression ignored",84 "while loop body expression ignored",
6 \\fn returns() usize {85 \\fn returns() usize {
...@@ -367,8 +446,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -367,8 +446,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
367 \\}446 \\}
368 \\447 \\
369 \\async fn foo() void {448 \\async fn foo() void {
370 \\ suspend |p| {449 \\ suspend {
371 \\ suspend |p1| {450 \\ suspend {
372 \\ }451 \\ }
373 \\ }452 \\ }
374 \\}453 \\}