authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-15 16:46:43-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-15 16:46:43-04:00
logd3672493cc6ad5085f202df1859b13b4ae4dec96
tree3bcd835cb800432959e53d5467105d24ada0f89e
parent55f5cee86b39bb2127a316f9b5d0abf532580cac
signature Commit is signed but in an unrecognized format.

basic docs for new async/await semantics


1 files changed, 308 insertions(+), 115 deletions(-)

doc/langref.html.in+308-115
...@@ -5970,54 +5970,25 @@ test "global assembly" {...@@ -5970,54 +5970,25 @@ test "global assembly" {
5970 {#header_close#}5970 {#header_close#}
5971 {#header_open|Async Functions#}5971 {#header_open|Async Functions#}
5972 <p>5972 <p>
5973 An async function is a function whose callsite is split into an {#syntax#}async{#endsyntax#} initiation,5973 When a function is called, a frame is pushed to the stack,
5974 followed by an {#syntax#}await{#endsyntax#} completion.5974 the function runs until it reaches a return statement, and then the frame is popped from the stack.
5975 </p>5975 At the callsite, the following code does not run until the function returns.
5976 <p>
5977 When you call a function, it creates a stack frame,
5978 and then the function runs until it reaches a return
5979 statement, and then the stack frame is destroyed.
5980 At the callsite, the next line of code does not run
5981 until the function returns.
5982 </p>
5983 <p>
5984 An async function is like a function, but it can be suspended
5985 and resumed any number of times, and then it must be
5986 explicitly destroyed. When an async function suspends, it
5987 returns to the resumer.
5988 </p>5976 </p>
5989 {#header_open|Minimal Async Function Example#}
5990 <p>5977 <p>
5991 Declare an async function with the {#syntax#}async{#endsyntax#} keyword.5978 An async function is a function whose callsite is split into an {#syntax#}async{#endsyntax#} initiation,
5992 The expression in angle brackets must evaluate to a struct5979 followed by an {#syntax#}await{#endsyntax#} completion. Its frame is
5993 which has these fields:5980 provided explicitly by the caller, and it can be suspended and resumed any number of times.
5994 </p>
5995 <ul>
5996 <li>{#syntax#}allocFn: fn (self: *Allocator, byte_count: usize, alignment: u29) Error![]u8{#endsyntax#} - where {#syntax#}Error{#endsyntax#} can be any error set.</li>
5997 <li>{#syntax#}freeFn: fn (self: *Allocator, old_mem: []u8) void{#endsyntax#}</li>
5998 </ul>
5999 <p>
6000 You may notice that this corresponds to the {#syntax#}std.mem.Allocator{#endsyntax#} interface.
6001 This makes it convenient to integrate with existing allocators. Note, however,
6002 that the language feature does not depend on the standard library, and any struct which
6003 has these fields is allowed.
6004 </p>
6005 <p>
6006 Omitting the angle bracket expression when defining an async function makes
6007 the function generic. Zig will infer the allocator type when the async function is called.
6008 </p>
6009 <p>
6010 Call an async function with the {#syntax#}async{#endsyntax#} keyword. Here, the expression in angle brackets
6011 is a pointer to the allocator struct that the async function expects.
6012 </p>5981 </p>
6013 <p>5982 <p>
6014 The result of an async function call is a {#syntax#}promise->T{#endsyntax#} type, where {#syntax#}T{#endsyntax#}5983 Zig infers that a function is {#syntax#}async{#endsyntax#} when it observes that the function contains
6015 is the return type of the async function. Once a promise has been created, it must be5984 a <strong>suspension point</strong>. Async functions can be called the same as normal functions. A
6016 consumed with {#syntax#}await{#endsyntax#}:5985 function call of an async function is a suspend point.
6017 </p>5986 </p>
5987 {#header_open|Suspend and Resume#}
6018 <p>5988 <p>
6019 Async functions start executing when created, so in the following example, the entire5989 At any point, a function may suspend itself. This causes control flow to
6020 TODO5990 return to the callsite (in the case of the first suspension),
5991 or resumer (in the case of subsequent suspensions).
6021 </p>5992 </p>
6022 {#code_begin|test#}5993 {#code_begin|test#}
6023const std = @import("std");5994const std = @import("std");
...@@ -6025,32 +5996,25 @@ const assert = std.debug.assert;...@@ -6025,32 +5996,25 @@ const assert = std.debug.assert;
60255996
6026var x: i32 = 1;5997var x: i32 = 1;
60275998
6028test "call an async function" {5999test "suspend with no resume" {
6029 var frame = async simpleAsyncFn();6000 var frame = async func();
6030 comptime assert(@typeOf(frame) == @Frame(simpleAsyncFn));
6031 assert(x == 2);6001 assert(x == 2);
6032}6002}
6033fn simpleAsyncFn() void {6003
6004fn func() void {
6034 x += 1;6005 x += 1;
6035 suspend;6006 suspend;
6007 // This line is never reached because the suspend has no matching resume.
6036 x += 1;6008 x += 1;
6037}6009}
6038 {#code_end#}6010 {#code_end#}
6039 {#header_close#}
6040 {#header_open|Suspend and Resume#}
6041 <p>6011 <p>
6042 At any point, an async function may suspend itself. This causes control flow to6012 In the same way that each allocation should have a corresponding free,
6043 return to the caller or resumer. The following code demonstrates where control flow6013 Each {#syntax#}suspend{#endsyntax#} should have a corresponding {#syntax#}resume{#endsyntax#}.
6044 goes:6014 A <strong>suspend block</strong> allows a function to put a pointer to its own
6045 </p>6015 frame somewhere, for example into an event loop, even if that action will perform a
6046 <p>6016 {#syntax#}resume{#endsyntax#} operation on a different thread.
6047 TODO another test example here6017 {#link|@frame#} provides access to the async function frame pointer.
6048 </p>
6049 <p>
6050 When an async function suspends itself, it must be sure that it will be
6051 resumed somehow, for example by registering its promise handle
6052 in an event loop. Use a suspend capture block to gain access to the
6053 promise (TODO this is outdated):
6054 </p>6018 </p>
6055 {#code_begin|test#}6019 {#code_begin|test#}
6056const std = @import("std");6020const std = @import("std");
...@@ -6061,9 +6025,9 @@ var result = false;...@@ -6061,9 +6025,9 @@ var result = false;
60616025
6062test "async function suspend with block" {6026test "async function suspend with block" {
6063 _ = async testSuspendBlock();6027 _ = async testSuspendBlock();
6064 std.debug.assert(!result);6028 assert(!result);
6065 resume the_frame;6029 resume the_frame;
6066 std.debug.assert(result);6030 assert(result);
6067}6031}
60686032
6069fn testSuspendBlock() void {6033fn testSuspendBlock() void {
...@@ -6075,19 +6039,15 @@ fn testSuspendBlock() void {...@@ -6075,19 +6039,15 @@ fn testSuspendBlock() void {
6075}6039}
6076 {#code_end#}6040 {#code_end#}
6077 <p>6041 <p>
6078 Every suspend point in an async function represents a point at which the async function6042 {#syntax#}suspend{#endsyntax#} causes a function to be {#syntax#}async{#endsyntax#}.
6079 could be destroyed. If that happens, {#syntax#}defer{#endsyntax#} expressions that are in
6080 scope are run, as well as {#syntax#}errdefer{#endsyntax#} expressions.
6081 </p>
6082 <p>
6083 {#link|Await#} counts as a suspend point.
6084 </p>6043 </p>
6044
6085 {#header_open|Resuming from Suspend Blocks#}6045 {#header_open|Resuming from Suspend Blocks#}
6086 <p>6046 <p>
6087 Upon entering a {#syntax#}suspend{#endsyntax#} block, the async function is already considered6047 Upon entering a {#syntax#}suspend{#endsyntax#} block, the async function is already considered
6088 suspended, and can be resumed. For example, if you started another kernel thread,6048 suspended, and can be resumed. For example, if you started another kernel thread,
6089 and had that thread call {#syntax#}resume{#endsyntax#} on the promise handle provided by the6049 and had that thread call {#syntax#}resume{#endsyntax#} on the frame pointer provided by the
6090 {#syntax#}suspend{#endsyntax#} block, the new thread would begin executing after the suspend6050 {#link|@frame#}, the new thread would begin executing after the suspend
6091 block, while the old thread continued executing the suspend block.6051 block, while the old thread continued executing the suspend block.
6092 </p>6052 </p>
6093 <p>6053 <p>
...@@ -6103,7 +6063,7 @@ test "resume from suspend" {...@@ -6103,7 +6063,7 @@ test "resume from suspend" {
6103 _ = async testResumeFromSuspend(&my_result);6063 _ = async testResumeFromSuspend(&my_result);
6104 std.debug.assert(my_result == 2);6064 std.debug.assert(my_result == 2);
6105}6065}
6106async fn testResumeFromSuspend(my_result: *i32) void {6066fn testResumeFromSuspend(my_result: *i32) void {
6107 suspend {6067 suspend {
6108 resume @frame();6068 resume @frame();
6109 }6069 }
...@@ -6113,32 +6073,59 @@ async fn testResumeFromSuspend(my_result: *i32) void {...@@ -6113,32 +6073,59 @@ async fn testResumeFromSuspend(my_result: *i32) void {
6113}6073}
6114 {#code_end#}6074 {#code_end#}
6115 <p>6075 <p>
6116 This is guaranteed to be a tail call, and therefore will not cause a new stack frame.6076 This is guaranteed to tail call, and therefore will not cause a new stack frame.
6117 </p>6077 </p>
6118 {#header_close#}6078 {#header_close#}
6119 {#header_close#}6079 {#header_close#}
6120 {#header_open|Await#}6080
6081 {#header_open|Async and Await#}
6121 <p>6082 <p>
6122 The {#syntax#}await{#endsyntax#} keyword is used to coordinate with an async function's6083 In the same way that every {#syntax#}suspend{#endsyntax#} has a matching
6123 {#syntax#}return{#endsyntax#} statement.6084 {#syntax#}resume{#endsyntax#}, every {#syntax#}async{#endsyntax#} has a matching {#syntax#}await{#endsyntax#}.
6124 </p>6085 </p>
6086 {#code_begin|test#}
6087const std = @import("std");
6088const assert = std.debug.assert;
6089
6090test "async and await" {
6091 // Here we have an exception where we do not match an async
6092 // with an await. The test block is not async and so cannot
6093 // have a suspend point in it.
6094 // This is well-defined behavior, and everything is OK here.
6095 // Note however that there would be no way to collect the
6096 // return value of amain, if it were something other than void.
6097 _ = async amain();
6098}
6099
6100fn amain() void {
6101 var frame = async func();
6102 comptime assert(@typeOf(frame) == @Frame(func));
6103
6104 const ptr: anyframe->void = &frame;
6105 const any_ptr: anyframe = ptr;
6106
6107 resume any_ptr;
6108 await ptr;
6109}
6110
6111fn func() void {
6112 suspend;
6113}
6114 {#code_end#}
6125 <p>6115 <p>
6126 {#syntax#}await{#endsyntax#} is valid only in an {#syntax#}async{#endsyntax#} function, and it takes6116 The {#syntax#}await{#endsyntax#} keyword is used to coordinate with an async function's
6127 as an operand a promise handle.6117 {#syntax#}return{#endsyntax#} statement.
6128 If the async function associated with the promise handle has already returned,
6129 then {#syntax#}await{#endsyntax#} destroys the target async function, and gives the return value.
6130 Otherwise, {#syntax#}await{#endsyntax#} suspends the current async function, registering its
6131 promise handle with the target async function. It becomes the target async function's responsibility
6132 to have ensured that it will be resumed or destroyed. When the target async function reaches
6133 its return statement, it gives the return value to the awaiter, destroys itself, and then
6134 resumes the awaiter.
6135 </p>6118 </p>
6136 <p>6119 <p>
6137 A frame handle must be consumed exactly once after it is created with {#syntax#}await{#endsyntax#}.6120 {#syntax#}await{#endsyntax#} is a suspend point, and takes as an operand anything that
6121 implicitly casts to {#syntax#}anyframe->T{#endsyntax#}.
6138 </p>6122 </p>
6139 <p>6123 <p>
6140 {#syntax#}await{#endsyntax#} counts as a suspend point, and therefore at every {#syntax#}await{#endsyntax#},6124 There is a common misconception that {#syntax#}await{#endsyntax#} resumes the target function.
6141 a async function can be potentially destroyed, which would run {#syntax#}defer{#endsyntax#} and {#syntax#}errdefer{#endsyntax#} expressions.6125 It is the other way around: it suspends until the target function completes.
6126 In the event that the target function has already completed, {#syntax#}await{#endsyntax#}
6127 does not suspend; instead it copies the
6128 return value directly from the target function's frame.
6142 </p>6129 </p>
6143 {#code_begin|test#}6130 {#code_begin|test#}
6144const std = @import("std");6131const std = @import("std");
...@@ -6156,14 +6143,14 @@ test "async function await" {...@@ -6156,14 +6143,14 @@ test "async function await" {
6156 assert(final_result == 1234);6143 assert(final_result == 1234);
6157 assert(std.mem.eql(u8, seq_points, "abcdefghi"));6144 assert(std.mem.eql(u8, seq_points, "abcdefghi"));
6158}6145}
6159async fn amain() void {6146fn amain() void {
6160 seq('b');6147 seq('b');
6161 var f = async another();6148 var f = async another();
6162 seq('e');6149 seq('e');
6163 final_result = await f;6150 final_result = await f;
6164 seq('h');6151 seq('h');
6165}6152}
6166async fn another() i32 {6153fn another() i32 {
6167 seq('c');6154 seq('c');
6168 suspend {6155 suspend {
6169 seq('d');6156 seq('d');
...@@ -6183,31 +6170,156 @@ fn seq(c: u8) void {...@@ -6183,31 +6170,156 @@ fn seq(c: u8) void {
6183 {#code_end#}6170 {#code_end#}
6184 <p>6171 <p>
6185 In general, {#syntax#}suspend{#endsyntax#} is lower level than {#syntax#}await{#endsyntax#}. Most application6172 In general, {#syntax#}suspend{#endsyntax#} is lower level than {#syntax#}await{#endsyntax#}. Most application
6186 code will use only {#syntax#}async{#endsyntax#} and {#syntax#}await{#endsyntax#}, but event loop6173 code will use only {#syntax#}async{#endsyntax#} and {#syntax#}await{#endsyntax#}, but event loop
6187 implementations will make use of {#syntax#}suspend{#endsyntax#} internally.6174 implementations will make use of {#syntax#}suspend{#endsyntax#} internally.
6188 </p>6175 </p>
6189 {#header_close#}6176 {#header_close#}
6190 {#header_open|Open Issues#}6177
6178 {#header_open|Async Function Example#}
6191 <p>6179 <p>
6192 There are a few issues with async function that are considered unresolved. Best be aware of them,6180 Putting all of this together, here is an example of typical
6193 as the situation is likely to change before 1.0.0:6181 {#syntax#}async{#endsyntax#}/{#syntax#}await{#endsyntax#} usage:
6182 </p>
6183 {#code_begin|exe|async#}
6184const std = @import("std");
6185const Allocator = std.mem.Allocator;
6186
6187pub fn main() void {
6188 _ = async amainWrap();
6189
6190 // Typically we would use an event loop to manage resuming async functions,
6191 // but in this example we hard code what the event loop would do,
6192 // to make things deterministic.
6193 resume global_file_frame;
6194 resume global_download_frame;
6195}
6196
6197fn amainWrap() void {
6198 amain() catch |e| {
6199 std.debug.warn("{}\n", e);
6200 if (@errorReturnTrace()) |trace| {
6201 std.debug.dumpStackTrace(trace.*);
6202 }
6203 std.process.exit(1);
6204 };
6205}
6206
6207fn amain() !void {
6208 const allocator = std.heap.direct_allocator;
6209 var download_frame = async fetchUrl(allocator, "https://example.com/");
6210 var awaited_download_frame = false;
6211 errdefer if (!awaited_download_frame) {
6212 if (await download_frame) |r| allocator.free(r) else |_| {}
6213 };
6214
6215 var file_frame = async readFile(allocator, "something.txt");
6216 var awaited_file_frame = false;
6217 errdefer if (!awaited_file_frame) {
6218 if (await file_frame) |r| allocator.free(r) else |_| {}
6219 };
6220
6221 awaited_file_frame = true;
6222 const file_text = try await file_frame;
6223 defer allocator.free(file_text);
6224
6225 awaited_download_frame = true;
6226 const download_text = try await download_frame;
6227 defer allocator.free(download_text);
6228
6229 std.debug.warn("download_text: {}\n", download_text);
6230 std.debug.warn("file_text: {}\n", file_text);
6231}
6232
6233var global_download_frame: anyframe = undefined;
6234fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 {
6235 const result = try std.mem.dupe(allocator, u8, "this is the downloaded url contents");
6236 errdefer allocator.free(result);
6237 suspend {
6238 global_download_frame = @frame();
6239 }
6240 std.debug.warn("fetchUrl returning\n");
6241 return result;
6242}
6243
6244var global_file_frame: anyframe = undefined;
6245fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
6246 const result = try std.mem.dupe(allocator, u8, "this is the file contents");
6247 errdefer allocator.free(result);
6248 suspend {
6249 global_file_frame = @frame();
6250 }
6251 std.debug.warn("readFile returning\n");
6252 return result;
6253}
6254 {#code_end#}
6255 <p>
6256 Now we remove the {#syntax#}suspend{#endsyntax#} and {#syntax#}resume{#endsyntax#} code, and
6257 observe the same behavior, with one tiny difference:
6258 </p>
6259 {#code_begin|exe|blocking#}
6260const std = @import("std");
6261const Allocator = std.mem.Allocator;
6262
6263pub fn main() void {
6264 _ = async amainWrap();
6265}
6266
6267fn amainWrap() void {
6268 amain() catch |e| {
6269 std.debug.warn("{}\n", e);
6270 if (@errorReturnTrace()) |trace| {
6271 std.debug.dumpStackTrace(trace.*);
6272 }
6273 std.process.exit(1);
6274 };
6275}
6276
6277fn amain() !void {
6278 const allocator = std.heap.direct_allocator;
6279 var download_frame = async fetchUrl(allocator, "https://example.com/");
6280 var awaited_download_frame = false;
6281 errdefer if (!awaited_download_frame) {
6282 if (await download_frame) |r| allocator.free(r) else |_| {}
6283 };
6284
6285 var file_frame = async readFile(allocator, "something.txt");
6286 var awaited_file_frame = false;
6287 errdefer if (!awaited_file_frame) {
6288 if (await file_frame) |r| allocator.free(r) else |_| {}
6289 };
6290
6291 awaited_file_frame = true;
6292 const file_text = try await file_frame;
6293 defer allocator.free(file_text);
6294
6295 awaited_download_frame = true;
6296 const download_text = try await download_frame;
6297 defer allocator.free(download_text);
6298
6299 std.debug.warn("download_text: {}\n", download_text);
6300 std.debug.warn("file_text: {}\n", file_text);
6301}
6302
6303fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 {
6304 const result = try std.mem.dupe(allocator, u8, "this is the downloaded url contents");
6305 errdefer allocator.free(result);
6306 std.debug.warn("fetchUrl returning\n");
6307 return result;
6308}
6309
6310fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
6311 const result = try std.mem.dupe(allocator, u8, "this is the file contents");
6312 errdefer allocator.free(result);
6313 std.debug.warn("readFile returning\n");
6314 return result;
6315}
6316 {#code_end#}
6317 <p>
6318 Previously, the {#syntax#}fetchUrl{#endsyntax#} and {#syntax#}readFile{#endsyntax#} functions suspended,
6319 and were resumed in an order determined by the {#syntax#}main{#endsyntax#} function. Now,
6320 since there are no suspend points, the order of the printed "... returning" messages
6321 is determined by the order of {#syntax#}async{#endsyntax#} callsites.
6194 </p>6322 </p>
6195 <ul>
6196 <li>Async functions have optimizations disabled - even in release modes - due to an
6197 <a href="https://github.com/ziglang/zig/issues/802">LLVM bug</a>.
6198 </li>
6199 <li>
6200 There are some situations where we can know statically that there will not be
6201 memory allocation failure, but Zig still forces us to handle it.
6202 TODO file an issue for this and link it here.
6203 </li>
6204 <li>
6205 Zig does not take advantage of LLVM's allocation elision optimization for
6206 async function. It crashed LLVM when I tried to do it the first time. This is
6207 related to the other 2 bullet points here. See
6208 <a href="https://github.com/ziglang/zig/issues/802">#802</a>.
6209 </li>
6210 </ul>
6211 {#header_close#}6323 {#header_close#}
62126324
6213 {#header_close#}6325 {#header_close#}
...@@ -6265,6 +6377,49 @@ comptime {...@@ -6265,6 +6377,49 @@ comptime {
6265 Note: This function is deprecated. Use {#link|@typeInfo#} instead.6377 Note: This function is deprecated. Use {#link|@typeInfo#} instead.
6266 </p>6378 </p>
6267 {#header_close#}6379 {#header_close#}
6380
6381 {#header_open|@asyncCall#}
6382 <pre>{#syntax#}@asyncCall(frame_buffer: []u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}</pre>
6383 <p>
6384 {#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,
6385 which may or may not be an {#link|async function|Async Functions#}.
6386 </p>
6387 <p>
6388 The provided {#syntax#}frame_buffer{#endsyntax#} must be large enough to fit the entire function frame.
6389 This size can be determined with {#link|@frameSize#}. To provide a too-small buffer
6390 invokes safety-checked {#link|Undefined Behavior#}.
6391 </p>
6392 <p>
6393 {#syntax#}result_ptr{#endsyntax#} is optional ({#link|null#} may be provided). If provided,
6394 the function call will write its result directly to the result pointer, which will be available to
6395 read after {#link|await|Async and Await#} completes. Any result location provided to
6396 {#syntax#}await{#endsyntax#} will copy the result from {#syntax#}result_ptr{#endsyntax#}.
6397 </p>
6398 {#code_begin|test#}
6399const std = @import("std");
6400const assert = std.debug.assert;
6401
6402test "async fn pointer in a struct field" {
6403 var data: i32 = 1;
6404 const Foo = struct {
6405 bar: async fn (*i32) void,
6406 };
6407 var foo = Foo{ .bar = func };
6408 var bytes: [64]u8 = undefined;
6409 const f = @asyncCall(&bytes, {}, foo.bar, &data);
6410 assert(data == 2);
6411 resume f;
6412 assert(data == 4);
6413}
6414
6415async fn func(y: *i32) void {
6416 defer y.* += 2;
6417 y.* += 1;
6418 suspend;
6419}
6420 {#code_end#}
6421 {#header_close#}
6422
6268 {#header_open|@atomicLoad#}6423 {#header_open|@atomicLoad#}
6269 <pre>{#syntax#}@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre>6424 <pre>{#syntax#}@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre>
6270 <p>6425 <p>
...@@ -6855,6 +7010,44 @@ export fn @"A function name that is a complete sentence."() void {}...@@ -6855,6 +7010,44 @@ export fn @"A function name that is a complete sentence."() void {}
6855 {#see_also|@intToFloat#}7010 {#see_also|@intToFloat#}
6856 {#header_close#}7011 {#header_close#}
68577012
7013 {#header_open|@frame#}
7014 <pre>{#syntax#}@frame() *@Frame(func){#endsyntax#}</pre>
7015 <p>
7016 This function returns a pointer to the frame for a given function. This type
7017 can be {#link|implicitly cast|Implicit Casts#} to {#syntax#}anyframe->T{#endsyntax#} and
7018 to {#syntax#}anyframe{#endsyntax#}, where {#syntax#}T{#endsyntax#} is the return type
7019 of the function in scope.
7020 </p>
7021 <p>
7022 This function does not mark a suspension point, but it does cause the function in scope
7023 to become an {#link|async function|Async Functions#}.
7024 </p>
7025 {#header_close#}
7026
7027 {#header_open|@Frame#}
7028 <pre>{#syntax#}@Frame(func: var) type{#endsyntax#}</pre>
7029 <p>
7030 This function returns the frame type of a function. This works for {#link|Async Functions#}
7031 as well as any function without a specific calling convention.
7032 </p>
7033 <p>
7034 This type is suitable to be used as the return type of {#link|async|Async and Await#} which
7035 allows one to, for example, heap-allocate an async function frame:
7036 </p>
7037 {#code_begin|test#}
7038const std = @import("std");
7039
7040test "heap allocated frame" {
7041 const frame = try std.heap.direct_allocator.create(@Frame(func));
7042 frame.* = async func();
7043}
7044
7045fn func() void {
7046 suspend;
7047}
7048 {#code_end#}
7049 {#header_close#}
7050
6858 {#header_open|@frameAddress#}7051 {#header_open|@frameAddress#}
6859 <pre>{#syntax#}@frameAddress() usize{#endsyntax#}</pre>7052 <pre>{#syntax#}@frameAddress() usize{#endsyntax#}</pre>
6860 <p>7053 <p>
...@@ -6870,14 +7063,14 @@ export fn @"A function name that is a complete sentence."() void {}...@@ -6870,14 +7063,14 @@ export fn @"A function name that is a complete sentence."() void {}
6870 </p>7063 </p>
6871 {#header_close#}7064 {#header_close#}
68727065
6873 {#header_open|@handle#}7066 {#header_open|@frameSize#}
6874 <pre>{#syntax#}@handle(){#endsyntax#}</pre>7067 <pre>{#syntax#}@frameSize() usize{#endsyntax#}</pre>
6875 <p>7068 <p>
6876 This function returns a {#syntax#}promise->T{#endsyntax#} type, where {#syntax#}T{#endsyntax#}7069 This is the same as {#syntax#}@sizeOf(@Frame(func)){#endsyntax#}, where {#syntax#}func{#endsyntax#}
6877 is the return type of the async function in scope.7070 may be runtime-known.
6878 </p>7071 </p>
6879 <p>7072 <p>
6880 This function is only valid within an async function scope.7073 This function is typically used in conjunction with {#link|@asyncCall#}.
6881 </p>7074 </p>
6882 {#header_close#}7075 {#header_close#}
68837076