authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-12-09 12:25:57-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-12-09 15:27:26-05:00
logf205d23e650019dd66120cf122ffb449267f619b
tree1d7ab12881bcf381ab46dfdf21653fc690ee44fc
parent69b587c1d389808e55846cadd8eec5b9fd4bc64a
signaturelock-open Commit is signed but in an unrecognized format.

implement async function call with `@call`

this removes the last usage of var args in zig std lib

9 files changed, 57 insertions(+), 29 deletions(-)

doc/langref.html.in+3
...@@ -6880,6 +6880,9 @@ pub const CallOptions = struct {...@@ -6880,6 +6880,9 @@ pub const CallOptions = struct {
6880 /// Equivalent to function call syntax.6880 /// Equivalent to function call syntax.
6881 auto,6881 auto,
68826882
6883 /// Equivalent to async keyword used with function call syntax.
6884 async_kw,
6885
6883 /// Prevents tail call optimization. This guarantees that the return6886 /// Prevents tail call optimization. This guarantees that the return
6884 /// address will point to the callsite, as opposed to the callsite's6887 /// address will point to the callsite, as opposed to the callsite's
6885 /// callsite. If the call is otherwise required to be tail-called6888 /// callsite. If the call is otherwise required to be tail-called
lib/std/builtin.zig+3
...@@ -382,6 +382,9 @@ pub const CallOptions = struct {...@@ -382,6 +382,9 @@ pub const CallOptions = struct {
382 /// Equivalent to function call syntax.382 /// Equivalent to function call syntax.
383 auto,383 auto,
384384
385 /// Equivalent to async keyword used with function call syntax.
386 async_kw,
387
385 /// Prevents tail call optimization. This guarantees that the return388 /// Prevents tail call optimization. This guarantees that the return
386 /// address will point to the callsite, as opposed to the callsite's389 /// address will point to the callsite, as opposed to the callsite's
387 /// callsite. If the call is otherwise required to be tail-called390 /// callsite. If the call is otherwise required to be tail-called
lib/std/event/group.zig+6-3
...@@ -60,16 +60,19 @@ pub fn Group(comptime ReturnType: type) type {...@@ -60,16 +60,19 @@ pub fn Group(comptime ReturnType: type) type {
60 /// allocated by the group and freed by `wait`.60 /// allocated by the group and freed by `wait`.
61 /// `func` must be async and have return type `ReturnType`.61 /// `func` must be async and have return type `ReturnType`.
62 /// Thread-safe.62 /// Thread-safe.
63 pub fn call(self: *Self, comptime func: var, args: ...) error{OutOfMemory}!void {63 pub fn call(self: *Self, comptime func: var, args: var) error{OutOfMemory}!void {
64 var frame = try self.allocator.create(@Frame(func));64 var frame = try self.allocator.create(@typeOf(@call(.{ .modifier = .async_kw }, func, args)));
65 errdefer self.allocator.destroy(frame);
65 const node = try self.allocator.create(AllocStack.Node);66 const node = try self.allocator.create(AllocStack.Node);
67 errdefer self.allocator.destroy(node);
66 node.* = AllocStack.Node{68 node.* = AllocStack.Node{
67 .next = undefined,69 .next = undefined,
68 .data = Node{70 .data = Node{
69 .handle = @asyncCall(frame, {}, func, args),71 .handle = frame,
70 .bytes = std.mem.asBytes(frame),72 .bytes = std.mem.asBytes(frame),
71 },73 },
72 };74 };
75 frame.* = @call(.{ .modifier = .async_kw }, func, args);
73 self.alloc_stack.push(node);76 self.alloc_stack.push(node);
74 }77 }
7578
src-self-hosted/compilation.zig+11-11
...@@ -778,7 +778,7 @@ pub const Compilation = struct {...@@ -778,7 +778,7 @@ pub const Compilation = struct {
778 continue;778 continue;
779 };779 };
780 const root_scope = ev.data;780 const root_scope = ev.data;
781 group.call(rebuildFile, self, root_scope) catch |err| {781 group.call(rebuildFile, .{ self, root_scope }) catch |err| {
782 build_result = err;782 build_result = err;
783 continue;783 continue;
784 };784 };
...@@ -787,7 +787,7 @@ pub const Compilation = struct {...@@ -787,7 +787,7 @@ pub const Compilation = struct {
787 while (self.fs_watch.channel.getOrNull()) |ev_or_err| {787 while (self.fs_watch.channel.getOrNull()) |ev_or_err| {
788 if (ev_or_err) |ev| {788 if (ev_or_err) |ev| {
789 const root_scope = ev.data;789 const root_scope = ev.data;
790 group.call(rebuildFile, self, root_scope) catch |err| {790 group.call(rebuildFile, .{ self, root_scope }) catch |err| {
791 build_result = err;791 build_result = err;
792 continue;792 continue;
793 };793 };
...@@ -868,7 +868,7 @@ pub const Compilation = struct {...@@ -868,7 +868,7 @@ pub const Compilation = struct {
868868
869 // TODO connect existing comptime decls to updated source files869 // TODO connect existing comptime decls to updated source files
870870
871 try self.prelink_group.call(addCompTimeBlock, self, tree_scope, &decl_scope.base, comptime_node);871 try self.prelink_group.call(addCompTimeBlock, .{ self, tree_scope, &decl_scope.base, comptime_node });
872 },872 },
873 .VarDecl => @panic("TODO"),873 .VarDecl => @panic("TODO"),
874 .FnProto => {874 .FnProto => {
...@@ -921,7 +921,7 @@ pub const Compilation = struct {...@@ -921,7 +921,7 @@ pub const Compilation = struct {
921 tree_scope.base.ref();921 tree_scope.base.ref();
922 errdefer self.gpa().destroy(fn_decl);922 errdefer self.gpa().destroy(fn_decl);
923923
924 try group.call(addTopLevelDecl, self, &fn_decl.base, locked_table);924 try group.call(addTopLevelDecl, .{ self, &fn_decl.base, locked_table });
925 }925 }
926 },926 },
927 .TestDecl => @panic("TODO"),927 .TestDecl => @panic("TODO"),
...@@ -1042,8 +1042,8 @@ pub const Compilation = struct {...@@ -1042,8 +1042,8 @@ pub const Compilation = struct {
1042 const is_export = decl.isExported(decl.tree_scope.tree);1042 const is_export = decl.isExported(decl.tree_scope.tree);
10431043
1044 if (is_export) {1044 if (is_export) {
1045 try self.prelink_group.call(verifyUniqueSymbol, self, decl);1045 try self.prelink_group.call(verifyUniqueSymbol, .{ self, decl });
1046 try self.prelink_group.call(resolveDecl, self, decl);1046 try self.prelink_group.call(resolveDecl, .{ self, decl });
1047 }1047 }
10481048
1049 const gop = try locked_table.getOrPut(decl.name);1049 const gop = try locked_table.getOrPut(decl.name);
...@@ -1062,7 +1062,7 @@ pub const Compilation = struct {...@@ -1062,7 +1062,7 @@ pub const Compilation = struct {
1062 const msg = try Msg.createFromScope(self, tree_scope, span, text);1062 const msg = try Msg.createFromScope(self, tree_scope, span, text);
1063 errdefer msg.destroy();1063 errdefer msg.destroy();
10641064
1065 try self.prelink_group.call(addCompileErrorAsync, self, msg);1065 try self.prelink_group.call(addCompileErrorAsync, .{ self, msg });
1066 }1066 }
10671067
1068 fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: var) !void {1068 fn addCompileErrorCli(self: *Compilation, realpath: []const u8, comptime fmt: []const u8, args: var) !void {
...@@ -1072,7 +1072,7 @@ pub const Compilation = struct {...@@ -1072,7 +1072,7 @@ pub const Compilation = struct {
1072 const msg = try Msg.createFromCli(self, realpath, text);1072 const msg = try Msg.createFromCli(self, realpath, text);
1073 errdefer msg.destroy();1073 errdefer msg.destroy();
10741074
1075 try self.prelink_group.call(addCompileErrorAsync, self, msg);1075 try self.prelink_group.call(addCompileErrorAsync, .{ self, msg });
1076 }1076 }
10771077
1078 async fn addCompileErrorAsync(1078 async fn addCompileErrorAsync(
...@@ -1131,7 +1131,7 @@ pub const Compilation = struct {...@@ -1131,7 +1131,7 @@ pub const Compilation = struct {
11311131
1132 // get a head start on looking for the native libc1132 // get a head start on looking for the native libc
1133 if (self.target == Target.Native and self.override_libc == null) {1133 if (self.target == Target.Native and self.override_libc == null) {
1134 try self.deinit_group.call(startFindingNativeLibC, self);1134 try self.deinit_group.call(startFindingNativeLibC, .{self});
1135 }1135 }
1136 }1136 }
1137 return link_lib;1137 return link_lib;
...@@ -1339,8 +1339,8 @@ fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {...@@ -1339,8 +1339,8 @@ fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
13391339
1340 // Kick off rendering to LLVM module, but it doesn't block the fn decl1340 // Kick off rendering to LLVM module, but it doesn't block the fn decl
1341 // analysis from being complete.1341 // analysis from being complete.
1342 try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);1342 try comp.prelink_group.call(codegen.renderToLlvm, .{ comp, fn_val, analyzed_code });
1343 try comp.prelink_group.call(addFnToLinkSet, comp, fn_val);1343 try comp.prelink_group.call(addFnToLinkSet, .{ comp, fn_val });
1344}1344}
13451345
1346async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) Compilation.BuildError!void {1346async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) Compilation.BuildError!void {
src-self-hosted/libc_installation.zig+8-8
...@@ -158,9 +158,9 @@ pub const LibCInstallation = struct {...@@ -158,9 +158,9 @@ pub const LibCInstallation = struct {
158 if (sdk.msvc_lib_dir_ptr != 0) {158 if (sdk.msvc_lib_dir_ptr != 0) {
159 self.msvc_lib_dir = try std.mem.dupe(allocator, u8, sdk.msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);159 self.msvc_lib_dir = try std.mem.dupe(allocator, u8, sdk.msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);
160 }160 }
161 try group.call(findNativeKernel32LibDir, allocator, self, sdk);161 try group.call(findNativeKernel32LibDir, .{ allocator, self, sdk });
162 try group.call(findNativeIncludeDirWindows, self, allocator, sdk);162 try group.call(findNativeIncludeDirWindows, .{ self, allocator, sdk });
163 try group.call(findNativeLibDirWindows, self, allocator, sdk);163 try group.call(findNativeLibDirWindows, .{ self, allocator, sdk });
164 },164 },
165 c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory,165 c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory,
166 c.ZigFindWindowsSdkError.NotFound => return error.NotFound,166 c.ZigFindWindowsSdkError.NotFound => return error.NotFound,
...@@ -168,10 +168,10 @@ pub const LibCInstallation = struct {...@@ -168,10 +168,10 @@ pub const LibCInstallation = struct {
168 }168 }
169 },169 },
170 .linux => {170 .linux => {
171 try group.call(findNativeIncludeDirLinux, self, allocator);171 try group.call(findNativeIncludeDirLinux, .{ self, allocator });
172 try group.call(findNativeLibDirLinux, self, allocator);172 try group.call(findNativeLibDirLinux, .{ self, allocator });
173 try group.call(findNativeStaticLibDir, self, allocator);173 try group.call(findNativeStaticLibDir, .{ self, allocator });
174 try group.call(findNativeDynamicLinker, self, allocator);174 try group.call(findNativeDynamicLinker, .{ self, allocator });
175 },175 },
176 .macosx, .freebsd, .netbsd => {176 .macosx, .freebsd, .netbsd => {
177 self.include_dir = try std.mem.dupe(allocator, u8, "/usr/include");177 self.include_dir = try std.mem.dupe(allocator, u8, "/usr/include");
...@@ -322,7 +322,7 @@ pub const LibCInstallation = struct {...@@ -322,7 +322,7 @@ pub const LibCInstallation = struct {
322 var group = event.Group(FindError!void).init(allocator);322 var group = event.Group(FindError!void).init(allocator);
323 errdefer group.wait() catch {};323 errdefer group.wait() catch {};
324 for (dyn_tests) |*dyn_test| {324 for (dyn_tests) |*dyn_test| {
325 try group.call(testNativeDynamicLinker, self, allocator, dyn_test);325 try group.call(testNativeDynamicLinker, .{ self, allocator, dyn_test });
326 }326 }
327 try group.wait();327 try group.wait();
328 for (dyn_tests) |*dyn_test| {328 for (dyn_tests) |*dyn_test| {
src-self-hosted/main.zig+2-2
...@@ -654,7 +654,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {...@@ -654,7 +654,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
654654
655 var group = event.Group(FmtError!void).init(allocator);655 var group = event.Group(FmtError!void).init(allocator);
656 for (flags.positionals.toSliceConst()) |file_path| {656 for (flags.positionals.toSliceConst()) |file_path| {
657 try group.call(fmtPath, &fmt, file_path, check_mode);657 try group.call(fmtPath, .{ &fmt, file_path, check_mode });
658 }658 }
659 try group.wait();659 try group.wait();
660 if (fmt.any_error) {660 if (fmt.any_error) {
...@@ -710,7 +710,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro...@@ -710,7 +710,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
710 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {710 if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
711 const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name });711 const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name });
712 @panic("TODO https://github.com/ziglang/zig/issues/3777");712 @panic("TODO https://github.com/ziglang/zig/issues/3777");
713 // try group.call(fmtPath, fmt, full_path, check_mode);713 // try group.call(fmtPath, .{fmt, full_path, check_mode});
714 }714 }
715 }715 }
716 return group.wait();716 return group.wait();
src/all_types.hpp+1-1
...@@ -782,6 +782,7 @@ struct AstNodeUnwrapOptional {...@@ -782,6 +782,7 @@ struct AstNodeUnwrapOptional {
782// Must be synchronized with std.builtin.CallOptions.Modifier782// Must be synchronized with std.builtin.CallOptions.Modifier
783enum CallModifier {783enum CallModifier {
784 CallModifierNone,784 CallModifierNone,
785 CallModifierAsync,
785 CallModifierNeverTail,786 CallModifierNeverTail,
786 CallModifierNeverInline,787 CallModifierNeverInline,
787 CallModifierNoAsync,788 CallModifierNoAsync,
...@@ -791,7 +792,6 @@ enum CallModifier {...@@ -791,7 +792,6 @@ enum CallModifier {
791792
792 // These are additional tags in the compiler, but not exposed in the std lib.793 // These are additional tags in the compiler, but not exposed in the std lib.
793 CallModifierBuiltin,794 CallModifierBuiltin,
794 CallModifierAsync,
795};795};
796796
797struct AstNodeFnCallExpr {797struct AstNodeFnCallExpr {
src/ir.cpp+1-4
...@@ -18330,10 +18330,7 @@ static IrInstruction *ir_analyze_call_extra(IrAnalyze *ira, IrInstruction *sourc...@@ -18330,10 +18330,7 @@ static IrInstruction *ir_analyze_call_extra(IrAnalyze *ira, IrInstruction *sourc
18330 if (modifier_val == nullptr)18330 if (modifier_val == nullptr)
18331 return ira->codegen->invalid_instruction;18331 return ira->codegen->invalid_instruction;
18332 CallModifier modifier = (CallModifier)bigint_as_u32(&modifier_val->data.x_enum_tag);18332 CallModifier modifier = (CallModifier)bigint_as_u32(&modifier_val->data.x_enum_tag);
18333 if (modifier == CallModifierAsync) {18333
18334 ir_add_error(ira, source_instr, buf_sprintf("TODO: @call with async modifier"));
18335 return ira->codegen->invalid_instruction;
18336 }
18337 if (ir_should_inline(ira->new_irb.exec, source_instr->scope)) {18334 if (ir_should_inline(ira->new_irb.exec, source_instr->scope)) {
18338 switch (modifier) {18335 switch (modifier) {
18339 case CallModifierBuiltin:18336 case CallModifierBuiltin:
test/stage1/behavior/async_fn.zig+22
...@@ -1271,3 +1271,25 @@ test "spill target expr in a for loop, with a var decl in the loop body" {...@@ -1271,3 +1271,25 @@ test "spill target expr in a for loop, with a var decl in the loop body" {
1271 resume S.global_frame;1271 resume S.global_frame;
1272 resume S.global_frame;1272 resume S.global_frame;
1273}1273}
1274
1275test "async call with @call" {
1276 const S = struct {
1277 var global_frame: anyframe = undefined;
1278 fn doTheTest() void {
1279 _ = @call(.{ .modifier = .async_kw }, atest, .{});
1280 resume global_frame;
1281 }
1282 fn atest() void {
1283 var frame = @call(.{ .modifier = .async_kw }, afoo, .{});
1284 const res = await frame;
1285 expect(res == 42);
1286 }
1287 fn afoo() i32 {
1288 suspend {
1289 global_frame = @frame();
1290 }
1291 return 42;
1292 }
1293 };
1294 S.doTheTest();
1295}