From 0db33e9c86ff43d3fe7f7f8fb2e29333c2fad2af Mon Sep 17 00:00:00 2001 From: "Matthew D. Steele" Date: Mon, 30 Jul 2018 22:27:07 -0400 Subject: [PATCH 01/50] Add "Comments" section to language reference (#1309) The contents of this section come from the discussion on issue #1305. --- doc/langref.html.in | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/doc/langref.html.in b/doc/langref.html.in index 0499c632e..7fde55033 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -134,6 +134,58 @@ pub fn main() void {

{#see_also|Values|@import|Errors|Root Source File#} {#header_close#} + {#header_open|Comments#} + {#code_begin|test|comments#} +const assert = @import("std").debug.assert; + +test "comments" { + // Comments in Zig start with "//" and end at the next LF byte (end of line). + // The below line is a comment, and won't be executed. + + //assert(false); + + const x = true; // another comment + assert(x); +} + {#code_end#} +

+ There are no multiline comments in Zig (e.g. like /* */ + comments in C). This helps allow Zig to have the property that each line + of code can be tokenized out of context. +

+ {#header_open|Doc comments#} +

+ A doc comment is one that begins with exactly three slashes (i.e. + /// but not ////); + multiple doc comments in a row are merged together to form a multiline + doc comment. The doc comment documents whatever immediately follows it. +

+ {#code_begin|syntax|doc_comments#} +/// A structure for storing a timestamp, with nanosecond precision (this is a +/// multiline doc comment). +const Timestamp = struct { + /// The number of seconds since the epoch (this is also a doc comment). + seconds: i64, // signed so we can represent pre-1970 (not a doc comment) + /// The number of nanoseconds past the second (doc comment again). + nanos: u32, + + /// Returns a `Timestamp` struct representing the Unix epoch; that is, the + /// moment of 1970 Jan 1 00:00:00 UTC (this is a doc comment too). + pub fn unixEpoch() Timestamp { + return Timestamp{ + .seconds = 0, + .nanos = 0, + }; + } +}; + {#code_end#} +

+ Doc comments are only allowed in certain places; eventually, it will + become a compile error have a doc comment in an unexpected place, such as + in the middle of an expression, or just before a non-doc comment. +

+ {#header_close#} + {#header_close#} {#header_open|Values#} {#code_begin|exe|values#} const std = @import("std"); From 058bfb254c4c0e1cfb254791f771c88c74f299e8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 Jul 2018 11:34:42 -0400 Subject: [PATCH 02/50] std.fmt.format: add '*' for formatting things as pointers closes #1285 --- std/fmt/index.zig | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 2188cc580..8daec50f1 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -18,6 +18,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), OpenBrace, CloseBrace, FormatString, + Pointer, }; comptime var start_index = 0; @@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), state = State.Start; start_index = i + 1; }, + '*' => state = State.Pointer, else => { state = State.FormatString; }, @@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), }, else => {}, }, + State.Pointer => switch (c) { + '}' => { + try output(context, @typeName(@typeOf(args[next_arg]).Child)); + try output(context, "@"); + try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + else => @compileError("Unexpected format character after '*'"), + }, } } comptime { @@ -861,6 +874,27 @@ test "fmt.format" { const value: u8 = 'a'; try testFmt("u8: a\n", "u8: {c}\n", value); } + { + const value: [3]u8 = "abc"; + try testFmt("array: abc\n", "array: {}\n", value); + try testFmt("array: abc\n", "array: {}\n", &value); + + var buf: [100]u8 = undefined; + try testFmt( + try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)), + "array: {*}\n", + &value, + ); + } + { + const value: []const u8 = "abc"; + try testFmt("slice: abc\n", "slice: {}\n", value); + } + { + const value = @intToPtr(*i32, 0xdeadbeef); + try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); + try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value); + } try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); From de949b72c791477c29155ee09a1c5c3d4ab56033 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 Jul 2018 19:57:46 -0400 Subject: [PATCH 03/50] simpler std.event.Lock implementation --- std/event/lock.zig | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/std/event/lock.zig b/std/event/lock.zig index 2013b5595..0bd7183db 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -106,35 +106,12 @@ pub const Lock = struct { // will attempt to grab the lock. _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - while (true) { - const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - if (old_bit != 0) { - // We did not obtain the lock. Trust that our queue entry will resume us, and allow - // suspend to complete. - break; - } - // We got the lock. However we might have already been resumed from the queue. + const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); + if (old_bit == 0) { if (self.queue.get()) |node| { // Whether this node is us or someone else, we tail resume it. resume node.data; - break; - } else { - // We already got resumed, and there are none left in the queue, which means that - // we aren't even supposed to hold the lock right now. - _ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - _ = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); - - // There might be a queue item. If we know the queue is empty, we can be done, - // because the other actor will try to obtain the lock. - // But if there's a queue item, we are the actor which must loop and attempt - // to grab the lock again. - if (@atomicLoad(u8, &self.queue_empty_bit, AtomicOrder.SeqCst) == 1) { - break; - } else { - continue; - } } - unreachable; } } From e66f538972d1278ed95513aa1d97c196818a73c6 Mon Sep 17 00:00:00 2001 From: Marc Tiehuis Date: Thu, 2 Aug 2018 03:38:04 +1200 Subject: [PATCH 04/50] Add integer binary output format (#1313) --- std/fmt/index.zig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/std/fmt/index.zig b/std/fmt/index.zig index 8daec50f1..f4f9efee3 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -248,6 +248,11 @@ pub fn formatIntValue( return formatAsciiChar(value, context, Errors, output); } }, + 'b' => { + radix = 2; + uppercase = false; + width = 0; + }, 'd' => { radix = 10; uppercase = false; @@ -874,6 +879,10 @@ test "fmt.format" { const value: u8 = 'a'; try testFmt("u8: a\n", "u8: {c}\n", value); } + { + const value: u8 = 0b1100; + try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); + } { const value: [3]u8 = "abc"; try testFmt("array: abc\n", "array: {}\n", value); From e79c913cbcb03834fcc04e2258ed5da7d533c9db Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:16:31 +0900 Subject: [PATCH 05/50] src/all_types.hpp: add enums for Handle Builtin; Tracking Issue #1296 ; --- src/all_types.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/all_types.hpp b/src/all_types.hpp index 2f09e7030..f03a250ae 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1358,6 +1358,7 @@ enum BuiltinFnId { BuiltinFnIdBreakpoint, BuiltinFnIdReturnAddress, BuiltinFnIdFrameAddress, + BuiltinFnIdHandle, BuiltinFnIdEmbedFile, BuiltinFnIdCmpxchgWeak, BuiltinFnIdCmpxchgStrong, @@ -2076,6 +2077,7 @@ enum IrInstructionId { IrInstructionIdBreakpoint, IrInstructionIdReturnAddress, IrInstructionIdFrameAddress, + IrInstructionIdHandle, IrInstructionIdAlignOf, IrInstructionIdOverflowOp, IrInstructionIdTestErr, @@ -2793,6 +2795,10 @@ struct IrInstructionFrameAddress { IrInstruction base; }; +struct IrInstructionHandle { + IrInstruction base; +}; + enum IrOverflowOp { IrOverflowOpAdd, IrOverflowOpSub, From a9ea22d4f9816998cccfca4df2ef56d5069e1814 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:17:38 +0900 Subject: [PATCH 06/50] src/ir.cpp: wire-up IR for handle builtin; Tracking Issue #1296 ; --- src/ir.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 699baa152..50c8c7029 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -580,6 +580,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *) return IrInstructionIdFrameAddress; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) { + return IrInstructionIdHandle; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) { return IrInstructionIdAlignOf; } @@ -2240,6 +2244,17 @@ static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction return new_instruction; } +static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) { + IrInstructionHandle *instruction = ir_build_instruction(irb, scope, source_node); + return &instruction->base; +} + +static IrInstruction *ir_build_handle_from(IrBuilder *irb, IrInstruction *old_instruction) { + IrInstruction *new_instruction = ir_build_handle(irb, old_instruction->scope, old_instruction->source_node); + ir_link_new_instruction(new_instruction, old_instruction); + return new_instruction; +} + static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *result_ptr, TypeTableEntry *result_ptr_type) @@ -4475,6 +4490,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval); case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); + case BuiltinFnIdHandle: + return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval); case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -19007,6 +19024,13 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn return u8_ptr_const; } +static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) { + ir_build_handle_from(&ira->new_irb, &instruction->base); + + TypeTableEntry *promise_type = get_promise_type(ira->codegen, nullptr); + return promise_type; +} + static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { IrInstruction *type_value = instruction->type_value->other; if (type_is_invalid(type_value->value.type)) @@ -20982,6 +21006,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction); case IrInstructionIdFrameAddress: return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction); + case IrInstructionIdHandle: + return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction); case IrInstructionIdAlignOf: return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction); case IrInstructionIdOverflowOp: @@ -21274,6 +21300,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAlignOf: case IrInstructionIdReturnAddress: case IrInstructionIdFrameAddress: + case IrInstructionIdHandle: case IrInstructionIdTestErr: case IrInstructionIdUnwrapErrCode: case IrInstructionIdOptionalWrap: From cd18186715bf532715c03d8e67096606fe0f2bee Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:18:29 +0900 Subject: [PATCH 07/50] src/codegen.cpp: base handle builtin on `@frameAddress()`; Tracking Issue #1296 ; --- src/codegen.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/codegen.cpp b/src/codegen.cpp index 7420da979..336aded82 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4146,6 +4146,15 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, ""); } +static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, + IrInstructionHandle *instruction) +{ + // @andrewrk, not sure what to place here ? + // `get_promise_frame_type` ? + LLVMValueRef handle = LLVMConstNull(g->builtin_types.entry_promise->type_ref); + return LLVMBuildRet(g->builder, handle); +} + static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { TypeTableEntry *int_type = instruction->result_ptr_type; assert(int_type->id == TypeTableEntryIdInt); @@ -4910,6 +4919,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction); case IrInstructionIdFrameAddress: return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction); + case IrInstructionIdHandle: + return ir_render_handle(g, executable, (IrInstructionHandle *)instruction); case IrInstructionIdOverflowOp: return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction); case IrInstructionIdTestErr: @@ -6344,6 +6355,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0); create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0); create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0); + create_builtin_fn(g, BuiltinFnIdHandle, "handle", 0); create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3); create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3); create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1); From da5f3d5c4c79b1d0d0919a0226f5304081b2f049 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:19:16 +0900 Subject: [PATCH 08/50] src/ir_print.cpp: support `@handle()`; Tracking Issue #1296 ; --- src/ir_print.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 127afa94a..77c7ef47b 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -791,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst fprintf(irp->f, "@frameAddress()"); } +static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) { + fprintf(irp->f, "@handle()"); +} + static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) { fprintf(irp->f, "@returnAddress()"); } @@ -1556,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFrameAddress: ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction); break; + case IrInstructionIdHandle: + ir_print_handle(irp, (IrInstructionHandle *)instruction); + break; case IrInstructionIdAlignOf: ir_print_align_of(irp, (IrInstructionAlignOf *)instruction); break; From 9366a58bdd91a8b5e7bc7d4babb6f91b989769db Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 27 Jul 2018 19:19:47 +0900 Subject: [PATCH 09/50] test/cases/couroutines.zig: test @handle(); Tracking Issue #1296 ; --- test/cases/coroutines.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 72a4ed0b3..53c5c3f90 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -66,6 +66,11 @@ async fn testSuspendBlock() void { comptime assert(@typeOf(p) == promise->void); a_promise = p; } + + //Test to make sure that @handle() works as advertised (issue #1296) + //var our_handle: promise = @handle(); + assert( a_promise == @handle() ); + result = true; } From a2e5691228c61e5c56220fc8f5f72e47b0611000 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 11:46:31 +0900 Subject: [PATCH 10/50] src/codegen.cpp: return null if calling convention is not async; Tracking Issue #1296 ; --- src/codegen.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 336aded82..cbd195583 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4149,9 +4149,15 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - // @andrewrk, not sure what to place here ? - // `get_promise_frame_type` ? - LLVMValueRef handle = LLVMConstNull(g->builtin_types.entry_promise->type_ref); + + bool is_async = executable->fn_entry != nullptr && + executable->fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; + + if (!is_async || !executable->coro_handle) { + return LLVMConstNull(g->builtin_types.entry_promise->type_ref); + } + + LLVMValueRef handle = ir_llvm_value(g, executable->coro_handle); return LLVMBuildRet(g->builder, handle); } From 81f463626ad09dd09e525cda140fb63baf11bc73 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 14:13:26 +0900 Subject: [PATCH 11/50] src/codegen.cpp: add/throw error for @handle() in a non async context; Tracking Issue #1296 ; I removed/commented-out the assert checking for no errors since we now have some errors rendered. --- src/codegen.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index cbd195583..43e2a0b69 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4154,6 +4154,7 @@ static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, executable->fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; if (!is_async || !executable->coro_handle) { + add_node_error(g, instruction->base.source_node, buf_sprintf("@handle() in non-async function")); return LLVMConstNull(g->builtin_types.entry_promise->type_ref); } @@ -6022,7 +6023,8 @@ static void do_code_gen(CodeGen *g) { ir_render(g, fn_table_entry); } - assert(!g->errors.length); + + //assert(!g->errors.length); if (buf_len(&g->global_asm) != 0) { LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); From 0ee65025623c0440ed655d68b579f17e6d6e5f5c Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:20:56 +0900 Subject: [PATCH 12/50] src/codegen.cpp: remove `add_node_error` from `ir_render_handle`; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/codegen.cpp | 12 +----------- test/cases/coroutines.zig | 3 +++ 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 43e2a0b69..8a18a3f8d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4149,17 +4149,7 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - - bool is_async = executable->fn_entry != nullptr && - executable->fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; - - if (!is_async || !executable->coro_handle) { - add_node_error(g, instruction->base.source_node, buf_sprintf("@handle() in non-async function")); - return LLVMConstNull(g->builtin_types.entry_promise->type_ref); - } - - LLVMValueRef handle = ir_llvm_value(g, executable->coro_handle); - return LLVMBuildRet(g->builder, handle); + return LLVMConstNull(g->builtin_types.entry_promise->type_ref); } static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 53c5c3f90..deb4aa1b2 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -57,6 +57,9 @@ test "coroutine suspend with block" { resume a_promise; std.debug.assert(result); cancel p; + + assert( @handle() ); + } var a_promise: promise = undefined; From db362bec185d2eeb5a4dd018deb2a988479f0f1a Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:22:00 +0900 Subject: [PATCH 13/50] src/codegen.cpp: reassert that there are no generated errors in codegen; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/codegen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 8a18a3f8d..54effb948 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -6014,7 +6014,7 @@ static void do_code_gen(CodeGen *g) { } - //assert(!g->errors.length); + assert(!g->errors.length); if (buf_len(&g->global_asm) != 0) { LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); From c1a3b0cb0af3c0639ce09b2a17a3cc90977346fe Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:23:09 +0900 Subject: [PATCH 14/50] src/ir.cpp: add/throw error for @handle() in a non async context; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/ir.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 50c8c7029..1e9d1bdb7 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3858,6 +3858,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } + bool is_async = exec_is_async(irb->exec); + switch (builtin_fn->id) { case BuiltinFnIdInvalid: zig_unreachable(); @@ -4491,6 +4493,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); case BuiltinFnIdHandle: + if (!is_async) { + add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function")); + return irb->codegen->invalid_instruction; + } return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval); case BuiltinFnIdAlignOf: { From 1f0040dd92f64cebf77a5ce34bd72984d92b3f2f Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:37:23 +0900 Subject: [PATCH 15/50] test/cases/coroutines.zig: remove dummy assert used for testing; --- test/cases/coroutines.zig | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index deb4aa1b2..53c5c3f90 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -57,9 +57,6 @@ test "coroutine suspend with block" { resume a_promise; std.debug.assert(result); cancel p; - - assert( @handle() ); - } var a_promise: promise = undefined; From fcf53b31fc2899cba682c4c9a8cade40c6e0ab9e Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 15:38:22 +0900 Subject: [PATCH 16/50] src/ir.cpp: return promise->T instead of promise; Tracking Issue #1296 ; Thanks @andrewrk ; --- src/ir.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 1e9d1bdb7..9712cb0bc 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19033,8 +19033,9 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) { ir_build_handle_from(&ira->new_irb, &instruction->base); - TypeTableEntry *promise_type = get_promise_type(ira->codegen, nullptr); - return promise_type; + FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec); + assert(fn_entry != nullptr); + return get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type); } static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { From a8ea2360958354ba8310c7cea388351299e72e44 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 17:35:50 +0900 Subject: [PATCH 17/50] src/ir.cpp: don't allow `@handle()` outside of a function; Tracking Issue #1296 ; --- src/ir.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ir.cpp b/src/ir.cpp index 9712cb0bc..966d8e9f3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -4493,6 +4493,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval); case BuiltinFnIdHandle: + if (!irb->exec->fn_entry) { + add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition")); + return irb->codegen->invalid_instruction; + } if (!is_async) { add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function")); return irb->codegen->invalid_instruction; From 104bdb03d6b5906716efeb84045079a424bf650a Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 23:29:40 +0900 Subject: [PATCH 18/50] src/codegen.cpp: return promise instead of null promise; Tracking Issue #1296 ; --- src/codegen.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 54effb948..bd708e382 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4149,7 +4149,8 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - return LLVMConstNull(g->builtin_types.entry_promise->type_ref); + LLVMValueRef ptr = ir_llvm_value(g, executable->fn_entry->ir_executable.coro_handle->other); + return LLVMBuildBitCast(g->builder, ptr, g->builtin_types.entry_promise->type_ref, ""); } static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { From c546f750f14e63b80c01d707c5559524313edfe4 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 23:51:48 +0900 Subject: [PATCH 19/50] test/compile_errors.zig: @handle() called outside of function definition; Tracking Issue #1296 ; --- test/compile_errors.zig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2c4c9208e..c34b325a7 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4738,4 +4738,20 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", ); + + cases.add( + "@handle() called outside of function definition", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\ + \\var handle_undef: promise = undefined; + \\var handle_dummy: promise = @handle(); + \\ + \\pub fn main() void { + \\ if (handle_undef == handle_dummy) return 0; + \\} + , + ".tmp_source.zig:6:29: error: @handle() called outside of function definition", + ); } From 13ec5db2348a0f6a4464aa8a513dbf11b72dc3ae Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sat, 28 Jul 2018 23:52:12 +0900 Subject: [PATCH 20/50] test/compile_errors.zig: @handle() in non-async function Tracking Issue #1296 ; --- test/compile_errors.zig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index c34b325a7..241f3a47a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -4754,4 +4754,18 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:6:29: error: @handle() called outside of function definition", ); + + cases.add( + "@handle() in non-async function", + \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { + \\ @import("std").os.exit(126); + \\} + \\ + \\pub fn main() void { + \\ var handle_undef: promise = undefined; + \\ if (handle_undef == @handle()) return 0; + \\} + , + ".tmp_source.zig:7:25: error: @handle() in non-async function", + ); } From 92cb330e160388bca7ddd7ceecbd7157ce92247b Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 12:27:50 +0900 Subject: [PATCH 21/50] src/codegen.cpp: @handle(): replace hacky ref chain with llvm intrinsic; Tracking Issue #1296 ; --- src/all_types.hpp | 1 + src/codegen.cpp | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index f03a250ae..8d55a75f9 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1717,6 +1717,7 @@ struct CodeGen { LLVMValueRef coro_save_fn_val; LLVMValueRef coro_promise_fn_val; LLVMValueRef coro_alloc_helper_fn_val; + LLVMValueRef coro_frame_fn_val; LLVMValueRef merge_err_ret_traces_fn_val; LLVMValueRef add_error_return_trace_addr_fn_val; LLVMValueRef stacksave_fn_val; diff --git a/src/codegen.cpp b/src/codegen.cpp index bd708e382..539356ef2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4146,11 +4146,24 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, ""); } +static LLVMValueRef get_handle_fn_val(CodeGen *g) { + if (g->coro_frame_fn_val) + return g->coro_frame_fn_val; + + LLVMTypeRef fn_type = LLVMFunctionType( LLVMPointerType(LLVMInt8Type(), 0) + , nullptr, 0, false); + Buf *name = buf_sprintf("llvm.coro.frame"); + g->coro_frame_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type); + assert(LLVMGetIntrinsicID(g->coro_frame_fn_val)); + + return g->coro_frame_fn_val; +} + static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionHandle *instruction) { - LLVMValueRef ptr = ir_llvm_value(g, executable->fn_entry->ir_executable.coro_handle->other); - return LLVMBuildBitCast(g->builder, ptr, g->builtin_types.entry_promise->type_ref, ""); + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_promise->type_ref); + return LLVMBuildCall(g->builder, get_handle_fn_val(g), &zero, 0, ""); } static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { From ca1b3563372dbf4442b5c3ff0fecaa174aea06b7 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:09:00 +0900 Subject: [PATCH 22/50] src/all_types.hpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/all_types.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 8d55a75f9..b1e8a3746 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -899,7 +899,6 @@ struct AstNodeAwaitExpr { struct AstNodeSuspend { AstNode *block; - AstNode *promise_symbol; }; struct AstNodePromiseType { From 5e5685c1174350b00ebf3e9ac49e244fa38fc0ac Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:09:39 +0900 Subject: [PATCH 23/50] src/ast_render.cpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/ast_render.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 2ace00885..984b4230b 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -1112,9 +1112,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { { fprintf(ar->f, "suspend"); if (node->data.suspend.block != nullptr) { - fprintf(ar->f, " |"); - render_node_grouped(ar, node->data.suspend.promise_symbol); - fprintf(ar->f, "| "); render_node_grouped(ar, node->data.suspend.block); } break; From b3cd65d56e2efc3e0f67461dbad75747b2db3aa7 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:10:48 +0900 Subject: [PATCH 24/50] src/ir.cpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/ir.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 966d8e9f3..7d2881744 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7096,19 +7096,8 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod if (node->data.suspend.block == nullptr) { suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false); } else { - assert(node->data.suspend.promise_symbol != nullptr); - assert(node->data.suspend.promise_symbol->type == NodeTypeSymbol); - Buf *promise_symbol_name = node->data.suspend.promise_symbol->data.symbol_expr.symbol; Scope *child_scope; - if (!buf_eql_str(promise_symbol_name, "_")) { - VariableTableEntry *promise_var = ir_create_var(irb, node, parent_scope, promise_symbol_name, - true, true, false, const_bool_false); - ir_build_var_decl(irb, parent_scope, node, promise_var, nullptr, nullptr, irb->exec->coro_handle); - child_scope = promise_var->child_scope; - } else { - child_scope = parent_scope; - } - ScopeSuspend *suspend_scope = create_suspend_scope(node, child_scope); + ScopeSuspend *suspend_scope = create_suspend_scope(node, parent_scope); suspend_scope->resume_block = resume_block; child_scope = &suspend_scope->base; IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle); From d3f628907a6b9e5b863c9d5235e6dadf57f42ca2 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:11:43 +0900 Subject: [PATCH 25/50] src/parser.cpp: remove promise_symbol from suspend; Tracking Issue #1296 ; --- src/parser.cpp | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index a93d8de83..e2a818a56 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -648,35 +648,37 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m } /* -SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = "suspend" option( body ) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { size_t orig_token_index = *token_index; + Token *token = &pc->tokens->at(*token_index); + Token *suspend_token = nullptr; - Token *suspend_token = &pc->tokens->at(*token_index); if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; + suspend_token = token; + token = &pc->tokens->at(*token_index); } else if (mandatory) { - ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend); + ast_expect_token(pc, token, TokenIdKeywordSuspend); zig_unreachable(); } else { return nullptr; } - Token *bar_token = &pc->tokens->at(*token_index); - if (bar_token->id == TokenIdBinOr) { - *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, suspend_token, TokenIdBinOr); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; + //guessing that semicolon is checked elsewhere? + if (token->id != TokenIdLBrace) { + if (mandatory) { + ast_expect_token(pc, token, TokenIdLBrace); + zig_unreachable(); + } else { + *token_index = orig_token_index; + return nullptr; + } } + //Expect that we have a block; AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); - node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index); - ast_eat_token(pc, token_index, TokenIdBinOr); node->data.suspend.block = ast_parse_block(pc, token_index, true); return node; @@ -3134,7 +3136,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont visit_field(&node->data.await_expr.expr, visit, context); break; case NodeTypeSuspend: - visit_field(&node->data.suspend.promise_symbol, visit, context); visit_field(&node->data.suspend.block, visit, context); break; } From 29057e5511ed007ee8db8306e89b674384c35964 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:12:33 +0900 Subject: [PATCH 26/50] std/event/channel.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/channel.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/std/event/channel.zig b/std/event/channel.zig index 03a036042..71e97f6e7 100644 --- a/std/event/channel.zig +++ b/std/event/channel.zig @@ -71,10 +71,10 @@ pub fn Channel(comptime T: type) type { /// puts a data item in the channel. The promise completes when the value has been added to the /// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter. pub async fn put(self: *SelfChannel, data: T) void { - suspend |handle| { + suspend { var my_tick_node = Loop.NextTickNode{ .next = undefined, - .data = handle, + .data = @handle(), }; var queue_node = std.atomic.Queue(PutNode).Node{ .data = PutNode{ @@ -96,10 +96,10 @@ pub fn Channel(comptime T: type) type { // TODO integrate this function with named return values // so we can get rid of this extra result copy var result: T = undefined; - suspend |handle| { + suspend { var my_tick_node = Loop.NextTickNode{ .next = undefined, - .data = handle, + .data = @handle(), }; var queue_node = std.atomic.Queue(GetNode).Node{ .data = GetNode{ From 244a7fdafb97b215e0e9e3e8aaa23777eccebd14 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:12:52 +0900 Subject: [PATCH 27/50] std/event/future.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/future.zig | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/std/event/future.zig b/std/event/future.zig index f5d14d1ca..f9b9db86a 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -100,8 +100,9 @@ test "std.event.Future" { } async fn testFuture(loop: *Loop) void { - suspend |p| { - resume p; + suspend { + var h: promise = @handle(); + resume h; } var future = Future(i32).init(loop); @@ -115,15 +116,17 @@ async fn testFuture(loop: *Loop) void { } async fn waitOnFuture(future: *Future(i32)) i32 { - suspend |p| { - resume p; + suspend { + var h: promise = @handle(); + resume h; } return (await (async future.get() catch @panic("memory"))).*; } async fn resolveFuture(future: *Future(i32)) void { - suspend |p| { - resume p; + suspend { + var h: promise = @handle(); + resume h; } future.data = 6; future.resolve(); From b4ff464d39038fe840ed6fce3f73cd075fde25f2 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:13:16 +0900 Subject: [PATCH 28/50] std/event/group.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/group.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/std/event/group.zig b/std/event/group.zig index 26c098399..493913010 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -54,10 +54,11 @@ pub fn Group(comptime ReturnType: type) type { const S = struct { async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType { // TODO this is a hack to make the memory following be inside the coro frame - suspend |p| { + suspend { var my_node: Stack.Node = undefined; node.* = &my_node; - resume p; + var h: promise = @handle(); + resume h; } // TODO this allocation elision should be guaranteed because we await it in From efec3a0e342be01f7cf74cf7906dd50b98e5aa97 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:13:42 +0900 Subject: [PATCH 29/50] std/event/lock.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/lock.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/std/event/lock.zig b/std/event/lock.zig index 0bd7183db..2769a2153 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -90,10 +90,10 @@ pub const Lock = struct { } pub async fn acquire(self: *Lock) Held { - suspend |handle| { + suspend { // TODO explicitly put this memory in the coroutine frame #1194 var my_tick_node = Loop.NextTickNode{ - .data = handle, + .data = @handle(), .next = undefined, }; @@ -141,8 +141,9 @@ test "std.event.Lock" { async fn testLock(loop: *Loop, lock: *Lock) void { // TODO explicitly put next tick node memory in the coroutine frame #1194 - suspend |p| { - resume p; + suspend { + var h: promise = @handle(); + resume h; } const handle1 = async lockRunner(lock) catch @panic("out of memory"); var tick_node1 = Loop.NextTickNode{ From a3705b425134d8e9c22ce238e7de9568d3394a44 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:14:02 +0900 Subject: [PATCH 30/50] std/event/loop.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/loop.zig | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/std/event/loop.zig b/std/event/loop.zig index 4e219653b..8b1b2e53d 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -331,11 +331,11 @@ pub const Loop = struct { pub async fn waitFd(self: *Loop, fd: i32) !void { defer self.removeFd(fd); - suspend |p| { + suspend { // TODO explicitly put this memory in the coroutine frame #1194 var resume_node = ResumeNode{ .id = ResumeNode.Id.Basic, - .handle = p, + .handle = @handle(), }; try self.addFd(fd, &resume_node); } @@ -417,11 +417,11 @@ pub const Loop = struct { pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) { const S = struct { async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType { - suspend |p| { - handle.* = p; + suspend { + handle.* = @handle(); var my_tick_node = Loop.NextTickNode{ .next = undefined, - .data = p, + .data = @handle(), }; loop.onNextTick(&my_tick_node); } @@ -439,10 +439,10 @@ pub const Loop = struct { /// CPU bound tasks would be waiting in the event loop but never get started because no async I/O /// is performed. pub async fn yield(self: *Loop) void { - suspend |p| { + suspend { var my_tick_node = Loop.NextTickNode{ .next = undefined, - .data = p, + .data = @handle(), }; self.onNextTick(&my_tick_node); } From 9fe140abad11a15a8bb81b3599600ca1ec6f00d0 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:14:35 +0900 Subject: [PATCH 31/50] std/event/tcp.zig: remove promise_symbol from suspend and use @handle(); Tracking Issue #1296 ; --- std/event/tcp.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/std/event/tcp.zig b/std/event/tcp.zig index 416a8c07d..9a3c6f95c 100644 --- a/std/event/tcp.zig +++ b/std/event/tcp.zig @@ -88,8 +88,8 @@ pub const Server = struct { }, error.ProcessFdQuotaExceeded => { errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node); - suspend |p| { - self.waiting_for_emfile_node = PromiseNode.init(p); + suspend { + self.waiting_for_emfile_node = PromiseNode.init( @handle() ); std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node); } continue; @@ -141,8 +141,9 @@ test "listen on a port, send bytes, receive bytes" { (await next_handler) catch |err| { std.debug.panic("unable to handle connection: {}\n", err); }; - suspend |p| { - cancel p; + suspend { + var h: promise = @handle(); + cancel h; } } async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void { From bc032a89cc13e483a55c58bcba4593229dd7f3ed Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:16:36 +0900 Subject: [PATCH 32/50] std/zig/parser_test.zig: update test to reflect that the promise symbol is no in scope with suspend; Tracking Issue #1296 ; --- std/zig/parser_test.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 21259bec3..32cdc8121 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -1784,7 +1784,7 @@ test "zig fmt: coroutines" { \\ x += 1; \\ suspend; \\ x += 1; - \\ suspend |p| {} + \\ suspend; \\ const p: promise->void = async simpleAsyncFn() catch unreachable; \\ await p; \\} From 79792a32e19b5ef7a918655f09ac32dd94c8f19e Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:17:13 +0900 Subject: [PATCH 33/50] test/cases/coroutine_await_struct.zig: update test to reflect that the promise symbol is no in scope with suspend; Tracking Issue #1296 ; --- test/cases/coroutine_await_struct.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/coroutine_await_struct.zig b/test/cases/coroutine_await_struct.zig index 56c526092..79168715d 100644 --- a/test/cases/coroutine_await_struct.zig +++ b/test/cases/coroutine_await_struct.zig @@ -30,9 +30,9 @@ async fn await_amain() void { } async fn await_another() Foo { await_seq('c'); - suspend |p| { + suspend { await_seq('d'); - await_a_promise = p; + await_a_promise = @handle(); } await_seq('g'); return Foo{ .x = 1234 }; From 3241ada4682b6953431876edfb8fc7af0f346a34 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:17:48 +0900 Subject: [PATCH 34/50] test/cases/coroutines.zig: update test to reflect that the promise symbol is no in scope with suspend; Tracking Issue #1296 ; --- test/cases/coroutines.zig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 53c5c3f90..c2aeb5de8 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -62,9 +62,9 @@ test "coroutine suspend with block" { var a_promise: promise = undefined; var result = false; async fn testSuspendBlock() void { - suspend |p| { - comptime assert(@typeOf(p) == promise->void); - a_promise = p; + suspend { + comptime assert(@typeOf(@handle()) == promise->void); + a_promise = @handle(); } //Test to make sure that @handle() works as advertised (issue #1296) @@ -98,9 +98,9 @@ async fn await_amain() void { } async fn await_another() i32 { await_seq('c'); - suspend |p| { + suspend { await_seq('d'); - await_a_promise = p; + await_a_promise = @handle(); } await_seq('g'); return 1234; From 9b3cebcdb9bedfa2ef49ddfb0dffea0899ab558d Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:18:31 +0900 Subject: [PATCH 35/50] test/cases/coroutines.zig: test for immediate resume inside of suspend with @handle(); Tracking Issue #1296 ; --- test/cases/coroutines.zig | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index c2aeb5de8..4dc20b9cf 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -256,3 +256,19 @@ async fn testBreakFromSuspend(my_result: *i32) void { suspend; my_result.* += 1; } + +test "suspend resume @handle()" { + var buf: [500]u8 = undefined; + var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; + var my_result: i32 = 1; + const p = try async testBreakFromSuspend(&my_result); + std.debug.assert(my_result == 2); +} +async fn testSuspendResumeAtHandle() void { + suspend { + resume @handle(); + } + my_result.* += 1; + suspend; + my_result.* += 1; +} \ No newline at end of file From 51955a5ca2b6f3f005e28cd3758dc481c2eea0c3 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:18:54 +0900 Subject: [PATCH 36/50] test/compile_errors.zig: update test to reflect that the promise symbol is no in scope with suspend; Tracking Issue #1296 ; --- test/compile_errors.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 241f3a47a..f4b289f70 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -367,8 +367,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\ \\async fn foo() void { - \\ suspend |p| { - \\ suspend |p1| { + \\ suspend { + \\ suspend { \\ } \\ } \\} From ff4a03f35157c3c82d581a599bf98db86504ecc1 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Sun, 29 Jul 2018 17:19:36 +0900 Subject: [PATCH 37/50] doc/langref.html.in: update docs to reflect that the promise symbol is no in scope with suspend; Tracking Issue #1296 ; --- doc/langref.html.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 7fde55033..58b63f7f4 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4690,9 +4690,9 @@ test "coroutine suspend with block" { var a_promise: promise = undefined; var result = false; async fn testSuspendBlock() void { - suspend |p| { - comptime assert(@typeOf(p) == promise->void); - a_promise = p; + suspend { + comptime assert(@typeOf(@handle()) == promise->void); + a_promise = @handle(); } result = true; } @@ -4791,9 +4791,9 @@ async fn amain() void { } async fn another() i32 { seq('c'); - suspend |p| { + suspend { seq('d'); - a_promise = p; + a_promise = @handle(); } seq('g'); return 1234; @@ -7388,7 +7388,7 @@ Defer(body) = ("defer" | "deferror") body IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) -SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) +SuspendExpression(body) = "suspend" option( body ) IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) From 5de92425d57338ba6c94a90360f96eb2fa8efdda Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:11:37 +0900 Subject: [PATCH 38/50] src/parser.cpp: fix typo from rebase; --- src/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.cpp b/src/parser.cpp index e2a818a56..84ccdbeea 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -655,7 +655,7 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b Token *token = &pc->tokens->at(*token_index); Token *suspend_token = nullptr; - if (suspend_token->id == TokenIdKeywordSuspend) { + if (token->id == TokenIdKeywordSuspend) { *token_index += 1; suspend_token = token; token = &pc->tokens->at(*token_index); From 915e321a2334b32ae398ec669d059f43fc2a8774 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:45:35 +0900 Subject: [PATCH 39/50] doc/langref.html.in: update suspend example with @handle(); Tracking Issue #1296 ; --- doc/langref.html.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 58b63f7f4..92fae2347 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4733,8 +4733,8 @@ test "resume from suspend" { std.debug.assert(my_result == 2); } async fn testResumeFromSuspend(my_result: *i32) void { - suspend |p| { - resume p; + suspend { + resume @handle(); } my_result.* += 1; suspend; From 9bed23f8b77531b1699ff79bbdd8bc9281ddfe5d Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:46:41 +0900 Subject: [PATCH 40/50] test/cases/coroutines.zig: update suspend to use @handle(); Tracking Issue #1296 ; --- test/cases/coroutines.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index 4dc20b9cf..a955eeac3 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -249,8 +249,8 @@ test "break from suspend" { std.debug.assert(my_result == 2); } async fn testBreakFromSuspend(my_result: *i32) void { - suspend |p| { - resume p; + suspend { + resume @handle(); } my_result.* += 1; suspend; From 9b890d70675b105ba2810e6b08010098e198043c Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:47:03 +0900 Subject: [PATCH 41/50] test/cases/cancel.zig: update suspend to use @handle(); Tracking Issue #1296 ; --- test/cases/cancel.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/cancel.zig b/test/cases/cancel.zig index edf11d687..c0f74fd34 100644 --- a/test/cases/cancel.zig +++ b/test/cases/cancel.zig @@ -85,8 +85,8 @@ async fn b4() void { defer { defer_b4 = true; } - suspend |p| { - b4_handle = p; + suspend { + b4_handle = @handle(); } suspend; } From ac0a87d58d72a8c1fbb9b0af2d3818075729d85a Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:47:39 +0900 Subject: [PATCH 42/50] doc/langref.html.in: add builtin @handle() to docs; Tracking Issue #1296 ; --- doc/langref.html.in | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/langref.html.in b/doc/langref.html.in index 92fae2347..54677bc5b 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5383,6 +5383,16 @@ test "main" { This function is only valid within function scope.

{#header_close#} + {#header_open|@handle#} +
@handle()
+

+ This function returns a promise->T type, where T + is the return type of the async function in scope. +

+

+ This function is only valid within an async function scope. +

+ {#header_close#} {#header_open|@import#}
@import(comptime path: []u8) (namespace)

From 96a94e7da933dafec25356c435f5725c3cb0ce04 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 2 Aug 2018 17:52:40 +0900 Subject: [PATCH 43/50] std/event: directly return @handle(); Tracking Issue #1296 ; --- std/event/future.zig | 9 +++------ std/event/group.zig | 3 +-- std/event/lock.zig | 3 +-- std/event/tcp.zig | 3 +-- 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/std/event/future.zig b/std/event/future.zig index f9b9db86a..8abdce7d0 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -101,8 +101,7 @@ test "std.event.Future" { async fn testFuture(loop: *Loop) void { suspend { - var h: promise = @handle(); - resume h; + resume @handle(); } var future = Future(i32).init(loop); @@ -117,16 +116,14 @@ async fn testFuture(loop: *Loop) void { async fn waitOnFuture(future: *Future(i32)) i32 { suspend { - var h: promise = @handle(); - resume h; + resume @handle(); } return (await (async future.get() catch @panic("memory"))).*; } async fn resolveFuture(future: *Future(i32)) void { suspend { - var h: promise = @handle(); - resume h; + resume @handle(); } future.data = 6; future.resolve(); diff --git a/std/event/group.zig b/std/event/group.zig index 493913010..6c7fc6369 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -57,8 +57,7 @@ pub fn Group(comptime ReturnType: type) type { suspend { var my_node: Stack.Node = undefined; node.* = &my_node; - var h: promise = @handle(); - resume h; + resume @handle(); } // TODO this allocation elision should be guaranteed because we await it in diff --git a/std/event/lock.zig b/std/event/lock.zig index 2769a2153..c4cb1a3f0 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -142,8 +142,7 @@ test "std.event.Lock" { async fn testLock(loop: *Loop, lock: *Lock) void { // TODO explicitly put next tick node memory in the coroutine frame #1194 suspend { - var h: promise = @handle(); - resume h; + resume @handle(); } const handle1 = async lockRunner(lock) catch @panic("out of memory"); var tick_node1 = Loop.NextTickNode{ diff --git a/std/event/tcp.zig b/std/event/tcp.zig index 9a3c6f95c..ea803a932 100644 --- a/std/event/tcp.zig +++ b/std/event/tcp.zig @@ -142,8 +142,7 @@ test "listen on a port, send bytes, receive bytes" { std.debug.panic("unable to handle connection: {}\n", err); }; suspend { - var h: promise = @handle(); - cancel h; + cancel @handle(); } } async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void { From 432b7685bfa840a459b492a37894f7ffed870c7e Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 01:59:59 +0900 Subject: [PATCH 44/50] std/os/index.zig: use "hw.logicalcpu" instead of "hw.ncpu" in macOS; (#1317) Tracking Issue #1252 ; hw.ncpu was deprecated in macOS. Among 4 new options available (hw.{physicalcpu, physicalcpu_max, logicalcpu, logicalcpu_max}), hw.logicalcpu was chosen because it actually reflects the number of logical cores the OS sees. --- std/os/index.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/index.zig b/std/os/index.zig index 77fd2a78a..0a6f598a2 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -2790,7 +2790,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { builtin.Os.macosx => { var count: c_int = undefined; var count_len: usize = @sizeOf(c_int); - const rc = posix.sysctlbyname(c"hw.ncpu", @ptrCast(*c_void, &count), &count_len, null, 0); + const rc = posix.sysctlbyname(c"hw.logicalcpu", @ptrCast(*c_void, &count), &count_len, null, 0); const err = posix.getErrno(rc); switch (err) { 0 => return @intCast(usize, count), From 22fd359e2c601c9d0a009705bed60c88821f2b0f Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:14:06 +0900 Subject: [PATCH 45/50] std/os/windows/advapi32.zig: add SystemFunction036; Tracking Issue #1318 ; --- std/os/windows/advapi32.zig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/std/os/windows/advapi32.zig b/std/os/windows/advapi32.zig index dcb5a636e..64a820a3b 100644 --- a/std/os/windows/advapi32.zig +++ b/std/os/windows/advapi32.zig @@ -28,3 +28,8 @@ pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD, lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS; + +// RtlGenRandom is known as SystemFunction036 under advapi32 +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ +pub extern "advapi32" stdcallcc fn SystemFunction036(output: PVOID, length: ULONG_PTR) BOOL; +pub const RtlGenRandom = SystemFunction036; From c44653f40f37c93fc68a2e455d693bb11f1c11d3 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:14:52 +0900 Subject: [PATCH 46/50] std/os/index.zig: swap CryptGetRandom() with RtlGenRandom(); Tracking Issue #1318 ; --- std/os/index.zig | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 77fd2a78a..7042247a2 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -130,16 +130,10 @@ pub fn getRandomBytes(buf: []u8) !void { try posixRead(fd, buf); }, Os.windows => { - var hCryptProv: windows.HCRYPTPROV = undefined; - if (windows.CryptAcquireContextA(&hCryptProv, null, null, windows.PROV_RSA_FULL, 0) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => unexpectedErrorWindows(err), - }; - } - defer _ = windows.CryptReleaseContext(hCryptProv, 0); - - if (windows.CryptGenRandom(hCryptProv, @intCast(windows.DWORD, buf.len), buf.ptr) == 0) { + // Call RtlGenRandom() instead of CryptGetRandom() on Windows + // https://github.com/rust-lang-nursery/rand/issues/111 + // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 + if (!windows.RtlGenRandom(buf.ptr, buf.len)) { const err = windows.GetLastError(); return switch (err) { else => unexpectedErrorWindows(err), From dde7eb45c51dd96242a6b028eb1912ee6ffa2c55 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:16:19 +0900 Subject: [PATCH 47/50] std/os/index.zig: call getRandomBytes() twice and compare; Tracking Issue #1318 ; --- std/os/index.zig | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index 7042247a2..87edce3da 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -153,8 +153,14 @@ pub fn getRandomBytes(buf: []u8) !void { } test "os.getRandomBytes" { - var buf: [50]u8 = undefined; - try getRandomBytes(buf[0..]); + var buf_a: [50]u8 = undefined; + var buf_b: [50]u8 = undefined; + // Call Twice + try getRandomBytes(buf_a[0..]); + try getRandomBytes(buf_b[0..]); + + // Check if random (not 100% conclusive) + assert( !mem.eql(u8, buf_a, buf_b) ); } /// Raises a signal in the current kernel thread, ending its execution. From 782043e2e66ab5833cb20dc6e787b87eb84165f8 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Fri, 3 Aug 2018 02:16:49 +0900 Subject: [PATCH 48/50] std/os/windows/util.zig: SKIP instead of PASS on non-windows systems; Tracking Issue #1318 ; --- std/os/windows/util.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index dda9ce7a8..c9d2c3c3e 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -166,7 +166,7 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void { } test "InvalidDll" { - if (builtin.os != builtin.Os.windows) return; + if (builtin.os != builtin.Os.windows) return error.SkipZigTest; const DllName = "asdf.dll"; const allocator = std.debug.global_allocator; From 729f2aceb045e54b2b74a33fa5f64cb9802988c6 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Aug 2018 13:34:31 -0400 Subject: [PATCH 49/50] fix API of RtlGenRandom --- std/os/index.zig | 2 +- std/os/windows/advapi32.zig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/std/os/index.zig b/std/os/index.zig index b5e0129da..425a900a7 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -133,7 +133,7 @@ pub fn getRandomBytes(buf: []u8) !void { // Call RtlGenRandom() instead of CryptGetRandom() on Windows // https://github.com/rust-lang-nursery/rand/issues/111 // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 - if (!windows.RtlGenRandom(buf.ptr, buf.len)) { + if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { const err = windows.GetLastError(); return switch (err) { else => unexpectedErrorWindows(err), diff --git a/std/os/windows/advapi32.zig b/std/os/windows/advapi32.zig index 64a820a3b..2f3195475 100644 --- a/std/os/windows/advapi32.zig +++ b/std/os/windows/advapi32.zig @@ -31,5 +31,5 @@ pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPC // RtlGenRandom is known as SystemFunction036 under advapi32 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */ -pub extern "advapi32" stdcallcc fn SystemFunction036(output: PVOID, length: ULONG_PTR) BOOL; +pub extern "advapi32" stdcallcc fn SystemFunction036(output: [*]u8, length: usize) BOOL; pub const RtlGenRandom = SystemFunction036; From 895f262a55b9951647efef4528c17cf64d6b7c07 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 2 Aug 2018 14:15:31 -0400 Subject: [PATCH 50/50] pull request fixups * clean up parser code * fix stage2 parse and render code * remove redundant test * make stage1 compile tests leaner --- src/parser.cpp | 36 +++++++++++---------------- std/zig/ast.zig | 12 --------- std/zig/parse.zig | 33 +++++++++++-------------- std/zig/parser_test.zig | 6 ++--- std/zig/render.zig | 16 +----------- test/cases/coroutines.zig | 16 ------------ test/compile_errors.zig | 51 ++++++++++++++++----------------------- 7 files changed, 53 insertions(+), 117 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index 84ccdbeea..453ab7ce2 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -651,37 +651,29 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m SuspendExpression(body) = "suspend" option( body ) */ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { - size_t orig_token_index = *token_index; - Token *token = &pc->tokens->at(*token_index); - Token *suspend_token = nullptr; + Token *suspend_token = &pc->tokens->at(*token_index); - if (token->id == TokenIdKeywordSuspend) { + if (suspend_token->id == TokenIdKeywordSuspend) { *token_index += 1; - suspend_token = token; - token = &pc->tokens->at(*token_index); } else if (mandatory) { - ast_expect_token(pc, token, TokenIdKeywordSuspend); + ast_expect_token(pc, suspend_token, TokenIdKeywordSuspend); zig_unreachable(); } else { return nullptr; } - //guessing that semicolon is checked elsewhere? - if (token->id != TokenIdLBrace) { - if (mandatory) { - ast_expect_token(pc, token, TokenIdLBrace); - zig_unreachable(); - } else { - *token_index = orig_token_index; - return nullptr; - } + Token *lbrace = &pc->tokens->at(*token_index); + if (lbrace->id == TokenIdLBrace) { + AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); + node->data.suspend.block = ast_parse_block(pc, token_index, true); + return node; + } else if (mandatory) { + ast_expect_token(pc, lbrace, TokenIdLBrace); + zig_unreachable(); + } else { + *token_index -= 1; + return nullptr; } - - //Expect that we have a block; - AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token); - node->data.suspend.block = ast_parse_block(pc, token_index, true); - - return node; } /* diff --git a/std/zig/ast.zig b/std/zig/ast.zig index 004f9278b..95e899fb9 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1778,19 +1778,12 @@ pub const Node = struct { pub const Suspend = struct { base: Node, - label: ?TokenIndex, suspend_token: TokenIndex, - payload: ?*Node, body: ?*Node, pub fn iterate(self: *Suspend, index: usize) ?*Node { var i = index; - if (self.payload) |payload| { - if (i < 1) return payload; - i -= 1; - } - if (self.body) |body| { if (i < 1) return body; i -= 1; @@ -1800,7 +1793,6 @@ pub const Node = struct { } pub fn firstToken(self: *Suspend) TokenIndex { - if (self.label) |label| return label; return self.suspend_token; } @@ -1809,10 +1801,6 @@ pub const Node = struct { return body.lastToken(); } - if (self.payload) |payload| { - return payload.lastToken(); - } - return self.suspend_token; } }; diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 73d51e787..fb49d2a2b 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -852,19 +852,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }) catch unreachable; continue; }, - Token.Id.Keyword_suspend => { - const node = try arena.create(ast.Node.Suspend{ - .base = ast.Node{ .id = ast.Node.Id.Suspend }, - .label = ctx.label, - .suspend_token = token_index, - .payload = null, - .body = null, - }); - ctx.opt_ctx.store(&node.base); - stack.append(State{ .SuspendBody = node }) catch unreachable; - try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); - continue; - }, Token.Id.Keyword_inline => { stack.append(State{ .Inline = InlineCtx{ @@ -1415,10 +1402,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree { }, State.SuspendBody => |suspend_node| { - if (suspend_node.payload != null) { - try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); + const token = nextToken(&tok_it, &tree); + switch (token.ptr.id) { + Token.Id.Semicolon => { + prevToken(&tok_it, &tree); + continue; + }, + Token.Id.LBrace => { + prevToken(&tok_it, &tree); + try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } }); + continue; + }, + else => { + ((try tree.errors.addOne())).* = Error{ .InvalidToken = Error.InvalidToken{ .token = token.index } }; + }, } - continue; }, State.AsyncAllocator => |async_node| { if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) { @@ -3086,15 +3084,12 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con Token.Id.Keyword_suspend => { const node = try arena.create(ast.Node.Suspend{ .base = ast.Node{ .id = ast.Node.Id.Suspend }, - .label = null, .suspend_token = token_index, - .payload = null, .body = null, }); ctx.store(&node.base); stack.append(State{ .SuspendBody = node }) catch unreachable; - try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } }); return true; }, Token.Id.Keyword_if => { diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index 32cdc8121..582bffdf3 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -898,11 +898,11 @@ test "zig fmt: union(enum(u32)) with assigned enum values" { ); } -test "zig fmt: labeled suspend" { +test "zig fmt: resume from suspend block" { try testCanonical( \\fn foo() void { - \\ s: suspend |p| { - \\ break :s; + \\ suspend { + \\ resume @handle(); \\ } \\} \\ diff --git a/std/zig/render.zig b/std/zig/render.zig index bc45768fa..868902a0d 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -323,21 +323,7 @@ fn renderExpression( ast.Node.Id.Suspend => { const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base); - if (suspend_node.label) |label| { - try renderToken(tree, stream, label, indent, start_col, Space.None); - try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space); - } - - if (suspend_node.payload) |payload| { - if (suspend_node.body) |body| { - try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); - try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space); - return renderExpression(allocator, stream, tree, indent, start_col, body, space); - } else { - try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); - return renderExpression(allocator, stream, tree, indent, start_col, payload, space); - } - } else if (suspend_node.body) |body| { + if (suspend_node.body) |body| { try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space); return renderExpression(allocator, stream, tree, indent, start_col, body, space); } else { diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig index a955eeac3..bd6b6abf6 100644 --- a/test/cases/coroutines.zig +++ b/test/cases/coroutines.zig @@ -256,19 +256,3 @@ async fn testBreakFromSuspend(my_result: *i32) void { suspend; my_result.* += 1; } - -test "suspend resume @handle()" { - var buf: [500]u8 = undefined; - var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator; - var my_result: i32 = 1; - const p = try async testBreakFromSuspend(&my_result); - std.debug.assert(my_result == 2); -} -async fn testSuspendResumeAtHandle() void { - suspend { - resume @handle(); - } - my_result.* += 1; - suspend; - my_result.* += 1; -} \ No newline at end of file diff --git a/test/compile_errors.zig b/test/compile_errors.zig index f4b289f70..948d212e5 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,27 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "@handle() called outside of function definition", + \\var handle_undef: promise = undefined; + \\var handle_dummy: promise = @handle(); + \\export fn entry() bool { + \\ return handle_undef == handle_dummy; + \\} + , + ".tmp_source.zig:2:29: error: @handle() called outside of function definition", + ); + + cases.add( + "@handle() in non-async function", + \\export fn entry() bool { + \\ var handle_undef: promise = undefined; + \\ return handle_undef == @handle(); + \\} + , + ".tmp_source.zig:3:28: error: @handle() in non-async function", + ); + cases.add( "while loop body expression ignored", \\fn returns() usize { @@ -4738,34 +4759,4 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { , ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", ); - - cases.add( - "@handle() called outside of function definition", - \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { - \\ @import("std").os.exit(126); - \\} - \\ - \\var handle_undef: promise = undefined; - \\var handle_dummy: promise = @handle(); - \\ - \\pub fn main() void { - \\ if (handle_undef == handle_dummy) return 0; - \\} - , - ".tmp_source.zig:6:29: error: @handle() called outside of function definition", - ); - - cases.add( - "@handle() in non-async function", - \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { - \\ @import("std").os.exit(126); - \\} - \\ - \\pub fn main() void { - \\ var handle_undef: promise = undefined; - \\ if (handle_undef == @handle()) return 0; - \\} - , - ".tmp_source.zig:7:25: error: @handle() in non-async function", - ); }