| ... | @@ -1179,33 +1179,8 @@ test "this will be skipped" { | ... | @@ -1179,33 +1179,8 @@ test "this will be skipped" { |
| 1179 | return error.SkipZigTest; | 1179 | return error.SkipZigTest; |
| 1180 | } | 1180 | } |
| 1181 | {#code_end#} | 1181 | {#code_end#} |
| 1182 | <p> | | |
| 1183 | The default test runner skips tests containing a {#link|suspend point|Async Functions#} while the | | |
| 1184 | test is running using the default, blocking IO mode. | | |
| 1185 | (The evented IO mode is enabled using the <kbd>--test-evented-io</kbd> command line parameter.) | | |
| 1186 | </p> | | |
| 1187 | {#code_begin|test|async_skip#} | | |
| 1188 | {#backend_stage1#} | | |
| 1189 | const std = @import("std"); | | |
| 1190 | | | |
| 1191 | test "async skip test" { | | |
| 1192 | var frame = async func(); | | |
| 1193 | const result = await frame; | | |
| 1194 | try std.testing.expect(result == 1); | | |
| 1195 | } | | |
| 1196 | | | |
| 1197 | fn func() i32 { | | |
| 1198 | suspend { | | |
| 1199 | resume @frame(); | | |
| 1200 | } | | |
| 1201 | return 1; | | |
| 1202 | } | | |
| 1203 | {#code_end#} | | |
| 1204 | <p> | | |
| 1205 | In the code sample above, the test would not be skipped in blocking IO mode if the {#syntax#}nosuspend{#endsyntax#} | | |
| 1206 | keyword was used (see {#link|Async and Await#}). | | |
| 1207 | </p> | | |
| 1208 | {#header_close#} | 1182 | {#header_close#} |
| | 1183 | |
| 1209 | {#header_open|Report Memory Leaks#} | 1184 | {#header_open|Report Memory Leaks#} |
| 1210 | <p> | 1185 | <p> |
| 1211 | When code allocates {#link|Memory#} using the {#link|Zig Standard Library#}'s testing allocator, | 1186 | When code allocates {#link|Memory#} using the {#link|Zig Standard Library#}'s testing allocator, |
| ... | @@ -6288,7 +6263,6 @@ test "float widening" { | ... | @@ -6288,7 +6263,6 @@ test "float widening" { |
| 6288 | <li>Cast {#syntax#}5{#endsyntax#} to {#syntax#}comptime_float{#endsyntax#} resulting in {#syntax#}@as(comptime_float, 10.8){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10.8){#endsyntax#}</li> | 6263 | <li>Cast {#syntax#}5{#endsyntax#} to {#syntax#}comptime_float{#endsyntax#} resulting in {#syntax#}@as(comptime_float, 10.8){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10.8){#endsyntax#}</li> |
| 6289 | </ul> | 6264 | </ul> |
| 6290 | {#code_begin|test_err#} | 6265 | {#code_begin|test_err#} |
| 6291 | {#backend_stage1#} | | |
| 6292 | // Compile time coercion of float to int | 6266 | // Compile time coercion of float to int |
| 6293 | test "implicit cast to comptime_int" { | 6267 | test "implicit cast to comptime_int" { |
| 6294 | var f: f32 = 54.0 / 5; | 6268 | var f: f32 = 54.0 / 5; |
| ... | @@ -7400,7 +7374,6 @@ pub fn main() void { | ... | @@ -7400,7 +7374,6 @@ pub fn main() void { |
| 7400 | </p> | 7374 | </p> |
| 7401 | {#code_begin|exe#} | 7375 | {#code_begin|exe#} |
| 7402 | {#target_linux_x86_64#} | 7376 | {#target_linux_x86_64#} |
| 7403 | {#backend_stage1#} | | |
| 7404 | pub fn main() noreturn { | 7377 | pub fn main() noreturn { |
| 7405 | const msg = "hello world\n"; | 7378 | const msg = "hello world\n"; |
| 7406 | _ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(msg), msg.len); | 7379 | _ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(msg), msg.len); |
| ... | @@ -7588,388 +7561,15 @@ test "global assembly" { | ... | @@ -7588,388 +7561,15 @@ test "global assembly" { |
| 7588 | <p>TODO: @atomic rmw</p> | 7561 | <p>TODO: @atomic rmw</p> |
| 7589 | <p>TODO: builtin atomic memory ordering enum</p> | 7562 | <p>TODO: builtin atomic memory ordering enum</p> |
| 7590 | {#header_close#} | 7563 | {#header_close#} |
| 7591 | {#header_open|Async Functions#} | | |
| 7592 | <p> | | |
| 7593 | When a function is called, a frame is pushed to the stack, | | |
| 7594 | the function runs until it reaches a return statement, and then the frame is popped from the stack. | | |
| 7595 | The code following the callsite does not run until the function returns. | | |
| 7596 | </p> | | |
| 7597 | <p> | | |
| 7598 | An async function is a function whose execution is split into an {#syntax#}async{#endsyntax#} initiation, | | |
| 7599 | followed by an {#syntax#}await{#endsyntax#} completion. Its frame is | | |
| 7600 | provided explicitly by the caller, and it can be suspended and resumed any number of times. | | |
| 7601 | </p> | | |
| 7602 | <p> | | |
| 7603 | The code following the {#syntax#}async{#endsyntax#} callsite runs immediately after the async | | |
| 7604 | function first suspends. When the return value of the async function is needed, | | |
| 7605 | the calling code can {#syntax#}await{#endsyntax#} on the async function frame. | | |
| 7606 | This will suspend the calling code until the async function completes, at which point | | |
| 7607 | execution resumes just after the {#syntax#}await{#endsyntax#} callsite. | | |
| 7608 | </p> | | |
| 7609 | <p> | | |
| 7610 | Zig infers that a function is {#syntax#}async{#endsyntax#} when it observes that the function contains | | |
| 7611 | a <strong>suspension point</strong>. Async functions can be called the same as normal functions. A | | |
| 7612 | function call of an async function is a suspend point. | | |
| 7613 | </p> | | |
| 7614 | {#header_open|Suspend and Resume#} | | |
| 7615 | <p> | | |
| 7616 | At any point, a function may suspend itself. This causes control flow to | | |
| 7617 | return to the callsite (in the case of the first suspension), | | |
| 7618 | or resumer (in the case of subsequent suspensions). | | |
| 7619 | </p> | | |
| 7620 | {#code_begin|test|suspend_no_resume#} | | |
| 7621 | {#backend_stage1#} | | |
| 7622 | const std = @import("std"); | | |
| 7623 | const expect = std.testing.expect; | | |
| 7624 | | | |
| 7625 | var x: i32 = 1; | | |
| 7626 | | | |
| 7627 | test "suspend with no resume" { | | |
| 7628 | var frame = async func(); | | |
| 7629 | try expect(x == 2); | | |
| 7630 | _ = frame; | | |
| 7631 | } | | |
| 7632 | | | |
| 7633 | fn func() void { | | |
| 7634 | x += 1; | | |
| 7635 | suspend {} | | |
| 7636 | // This line is never reached because the suspend has no matching resume. | | |
| 7637 | x += 1; | | |
| 7638 | } | | |
| 7639 | {#code_end#} | | |
| 7640 | <p> | | |
| 7641 | In the same way that each allocation should have a corresponding free, | | |
| 7642 | Each {#syntax#}suspend{#endsyntax#} should have a corresponding {#syntax#}resume{#endsyntax#}. | | |
| 7643 | A <strong>suspend block</strong> allows a function to put a pointer to its own | | |
| 7644 | frame somewhere, for example into an event loop, even if that action will perform a | | |
| 7645 | {#syntax#}resume{#endsyntax#} operation on a different thread. | | |
| 7646 | {#link|@frame#} provides access to the async function frame pointer. | | |
| 7647 | </p> | | |
| 7648 | {#code_begin|test|async_suspend_block#} | | |
| 7649 | {#backend_stage1#} | | |
| 7650 | const std = @import("std"); | | |
| 7651 | const expect = std.testing.expect; | | |
| 7652 | | | |
| 7653 | var the_frame: anyframe = undefined; | | |
| 7654 | var result = false; | | |
| 7655 | | | |
| 7656 | test "async function suspend with block" { | | |
| 7657 | _ = async testSuspendBlock(); | | |
| 7658 | try expect(!result); | | |
| 7659 | resume the_frame; | | |
| 7660 | try expect(result); | | |
| 7661 | } | | |
| 7662 | | | |
| 7663 | fn testSuspendBlock() void { | | |
| 7664 | suspend { | | |
| 7665 | comptime try expect(@TypeOf(@frame()) == *@Frame(testSuspendBlock)); | | |
| 7666 | the_frame = @frame(); | | |
| 7667 | } | | |
| 7668 | result = true; | | |
| 7669 | } | | |
| 7670 | {#code_end#} | | |
| 7671 | <p> | | |
| 7672 | {#syntax#}suspend{#endsyntax#} causes a function to be {#syntax#}async{#endsyntax#}. | | |
| 7673 | </p> | | |
| 7674 | | | |
| 7675 | {#header_open|Resuming from Suspend Blocks#} | | |
| 7676 | <p> | | |
| 7677 | Upon entering a {#syntax#}suspend{#endsyntax#} block, the async function is already considered | | |
| 7678 | suspended, and can be resumed. For example, if you started another kernel thread, | | |
| 7679 | and had that thread call {#syntax#}resume{#endsyntax#} on the frame pointer provided by the | | |
| 7680 | {#link|@frame#}, the new thread would begin executing after the suspend | | |
| 7681 | block, while the old thread continued executing the suspend block. | | |
| 7682 | </p> | | |
| 7683 | <p> | | |
| 7684 | However, the async function can be directly resumed from the suspend block, in which case it | | |
| 7685 | never returns to its resumer and continues executing. | | |
| 7686 | </p> | | |
| 7687 | {#code_begin|test|resume_from_suspend#} | | |
| 7688 | {#backend_stage1#} | | |
| 7689 | const std = @import("std"); | | |
| 7690 | const expect = std.testing.expect; | | |
| 7691 | | | |
| 7692 | test "resume from suspend" { | | |
| 7693 | var my_result: i32 = 1; | | |
| 7694 | _ = async testResumeFromSuspend(&my_result); | | |
| 7695 | try std.testing.expect(my_result == 2); | | |
| 7696 | } | | |
| 7697 | fn testResumeFromSuspend(my_result: *i32) void { | | |
| 7698 | suspend { | | |
| 7699 | resume @frame(); | | |
| 7700 | } | | |
| 7701 | my_result.* += 1; | | |
| 7702 | suspend {} | | |
| 7703 | my_result.* += 1; | | |
| 7704 | } | | |
| 7705 | {#code_end#} | | |
| 7706 | <p> | | |
| 7707 | This is guaranteed to tail call, and therefore will not cause a new stack frame. | | |
| 7708 | </p> | | |
| 7709 | {#header_close#} | | |
| 7710 | {#header_close#} | | |
| 7711 | | | |
| 7712 | {#header_open|Async and Await#} | | |
| 7713 | <p> | | |
| 7714 | In the same way that every {#syntax#}suspend{#endsyntax#} has a matching | | |
| 7715 | {#syntax#}resume{#endsyntax#}, every {#syntax#}async{#endsyntax#} has a matching {#syntax#}await{#endsyntax#} | | |
| 7716 | in standard code. | | |
| 7717 | </p> | | |
| 7718 | <p> | | |
| 7719 | However, it is possible to have an {#syntax#}async{#endsyntax#} call | | |
| 7720 | without a matching {#syntax#}await{#endsyntax#}. Upon completion of the async function, | | |
| 7721 | execution would continue at the most recent {#syntax#}async{#endsyntax#} callsite or {#syntax#}resume{#endsyntax#} callsite, | | |
| 7722 | and the return value of the async function would be lost. | | |
| 7723 | </p> | | |
| 7724 | {#code_begin|test|async_await#} | | |
| 7725 | {#backend_stage1#} | | |
| 7726 | const std = @import("std"); | | |
| 7727 | const expect = std.testing.expect; | | |
| 7728 | | | |
| 7729 | test "async and await" { | | |
| 7730 | // The test block is not async and so cannot have a suspend | | |
| 7731 | // point in it. By using the nosuspend keyword, we promise that | | |
| 7732 | // the code in amain will finish executing without suspending | | |
| 7733 | // back to the test block. | | |
| 7734 | nosuspend amain(); | | |
| 7735 | } | | |
| 7736 | | | |
| 7737 | fn amain() void { | | |
| 7738 | var frame = async func(); | | |
| 7739 | comptime try expect(@TypeOf(frame) == @Frame(func)); | | |
| 7740 | | | |
| 7741 | const ptr: anyframe->void = &frame; | | |
| 7742 | const any_ptr: anyframe = ptr; | | |
| 7743 | | | |
| 7744 | resume any_ptr; | | |
| 7745 | await ptr; | | |
| 7746 | } | | |
| 7747 | | | |
| 7748 | fn func() void { | | |
| 7749 | suspend {} | | |
| 7750 | } | | |
| 7751 | {#code_end#} | | |
| 7752 | <p> | | |
| 7753 | The {#syntax#}await{#endsyntax#} keyword is used to coordinate with an async function's | | |
| 7754 | {#syntax#}return{#endsyntax#} statement. | | |
| 7755 | </p> | | |
| 7756 | <p> | | |
| 7757 | {#syntax#}await{#endsyntax#} is a suspend point, and takes as an operand anything that | | |
| 7758 | coerces to {#syntax#}anyframe->T{#endsyntax#}. Calling {#syntax#}await{#endsyntax#} on | | |
| 7759 | the frame of an async function will cause execution to continue at the | | |
| 7760 | {#syntax#}await{#endsyntax#} callsite once the target function completes. | | |
| 7761 | </p> | | |
| 7762 | <p> | | |
| 7763 | There is a common misconception that {#syntax#}await{#endsyntax#} resumes the target function. | | |
| 7764 | It is the other way around: it suspends until the target function completes. | | |
| 7765 | In the event that the target function has already completed, {#syntax#}await{#endsyntax#} | | |
| 7766 | does not suspend; instead it copies the | | |
| 7767 | return value directly from the target function's frame. | | |
| 7768 | </p> | | |
| 7769 | {#code_begin|test|async_await_sequence#} | | |
| 7770 | {#backend_stage1#} | | |
| 7771 | const std = @import("std"); | | |
| 7772 | const expect = std.testing.expect; | | |
| 7773 | | | |
| 7774 | var the_frame: anyframe = undefined; | | |
| 7775 | var final_result: i32 = 0; | | |
| 7776 | | | |
| 7777 | test "async function await" { | | |
| 7778 | seq('a'); | | |
| 7779 | _ = async amain(); | | |
| 7780 | seq('f'); | | |
| 7781 | resume the_frame; | | |
| 7782 | seq('i'); | | |
| 7783 | try expect(final_result == 1234); | | |
| 7784 | try expect(std.mem.eql(u8, &seq_points, "abcdefghi")); | | |
| 7785 | } | | |
| 7786 | fn amain() void { | | |
| 7787 | seq('b'); | | |
| 7788 | var f = async another(); | | |
| 7789 | seq('e'); | | |
| 7790 | final_result = await f; | | |
| 7791 | seq('h'); | | |
| 7792 | } | | |
| 7793 | fn another() i32 { | | |
| 7794 | seq('c'); | | |
| 7795 | suspend { | | |
| 7796 | seq('d'); | | |
| 7797 | the_frame = @frame(); | | |
| 7798 | } | | |
| 7799 | seq('g'); | | |
| 7800 | return 1234; | | |
| 7801 | } | | |
| 7802 | | | |
| 7803 | var seq_points = [_]u8{0} ** "abcdefghi".len; | | |
| 7804 | var seq_index: usize = 0; | | |
| 7805 | | | |
| 7806 | fn seq(c: u8) void { | | |
| 7807 | seq_points[seq_index] = c; | | |
| 7808 | seq_index += 1; | | |
| 7809 | } | | |
| 7810 | {#code_end#} | | |
| 7811 | <p> | | |
| 7812 | In general, {#syntax#}suspend{#endsyntax#} is lower level than {#syntax#}await{#endsyntax#}. Most application | | |
| 7813 | code will use only {#syntax#}async{#endsyntax#} and {#syntax#}await{#endsyntax#}, but event loop | | |
| 7814 | implementations will make use of {#syntax#}suspend{#endsyntax#} internally. | | |
| 7815 | </p> | | |
| 7816 | {#header_close#} | | |
| 7817 | | | |
| 7818 | {#header_open|Async Function Example#} | | |
| 7819 | <p> | | |
| 7820 | Putting all of this together, here is an example of typical | | |
| 7821 | {#syntax#}async{#endsyntax#}/{#syntax#}await{#endsyntax#} usage: | | |
| 7822 | </p> | | |
| 7823 | {#code_begin|exe|async#} | | |
| 7824 | {#backend_stage1#} | | |
| 7825 | const std = @import("std"); | | |
| 7826 | const Allocator = std.mem.Allocator; | | |
| 7827 | | | |
| 7828 | pub fn main() void { | | |
| 7829 | _ = async amainWrap(); | | |
| 7830 | | | |
| 7831 | // Typically we would use an event loop to manage resuming async functions, | | |
| 7832 | // but in this example we hard code what the event loop would do, | | |
| 7833 | // to make things deterministic. | | |
| 7834 | resume global_file_frame; | | |
| 7835 | resume global_download_frame; | | |
| 7836 | } | | |
| 7837 | | | |
| 7838 | fn amainWrap() void { | | |
| 7839 | amain() catch |e| { | | |
| 7840 | std.debug.print("{}\n", .{e}); | | |
| 7841 | if (@errorReturnTrace()) |trace| { | | |
| 7842 | std.debug.dumpStackTrace(trace.*); | | |
| 7843 | } | | |
| 7844 | std.process.exit(1); | | |
| 7845 | }; | | |
| 7846 | } | | |
| 7847 | | | |
| 7848 | fn amain() !void { | | |
| 7849 | const allocator = std.heap.page_allocator; | | |
| 7850 | var download_frame = async fetchUrl(allocator, "https://example.com/"); | | |
| 7851 | var awaited_download_frame = false; | | |
| 7852 | errdefer if (!awaited_download_frame) { | | |
| 7853 | if (await download_frame) |r| allocator.free(r) else |_| {} | | |
| 7854 | }; | | |
| 7855 | | 7564 | |
| 7856 | var file_frame = async readFile(allocator, "something.txt"); | 7565 | {#header_open|Async Functions#} |
| 7857 | var awaited_file_frame = false; | 7566 | <p>Async functions are being temporarily regressed and will be |
| 7858 | errdefer if (!awaited_file_frame) { | 7567 | <a href="https://github.com/ziglang/zig/issues/6025">restored before Zig |
| 7859 | if (await file_frame) |r| allocator.free(r) else |_| {} | 7568 | 0.11.0 is tagged</a>. I apologize for the instability. Please use Zig 0.10.0 with |
| 7860 | }; | 7569 | the <code>-fstage1</code> flag for now if you need this feature.</p> |
| 7861 | | | |
| 7862 | awaited_file_frame = true; | | |
| 7863 | const file_text = try await file_frame; | | |
| 7864 | defer allocator.free(file_text); | | |
| 7865 | | | |
| 7866 | awaited_download_frame = true; | | |
| 7867 | const download_text = try await download_frame; | | |
| 7868 | defer allocator.free(download_text); | | |
| 7869 | | | |
| 7870 | std.debug.print("download_text: {s}\n", .{download_text}); | | |
| 7871 | std.debug.print("file_text: {s}\n", .{file_text}); | | |
| 7872 | } | | |
| 7873 | | | |
| 7874 | var global_download_frame: anyframe = undefined; | | |
| 7875 | fn fetchUrl(allocator: Allocator, url: []const u8) ![]u8 { | | |
| 7876 | _ = url; // this is just an example, we don't actually do it! | | |
| 7877 | const result = try allocator.dupe(u8, "this is the downloaded url contents"); | | |
| 7878 | errdefer allocator.free(result); | | |
| 7879 | suspend { | | |
| 7880 | global_download_frame = @frame(); | | |
| 7881 | } | | |
| 7882 | std.debug.print("fetchUrl returning\n", .{}); | | |
| 7883 | return result; | | |
| 7884 | } | | |
| 7885 | | | |
| 7886 | var global_file_frame: anyframe = undefined; | | |
| 7887 | fn readFile(allocator: Allocator, filename: []const u8) ![]u8 { | | |
| 7888 | _ = filename; // this is just an example, we don't actually do it! | | |
| 7889 | const result = try allocator.dupe(u8, "this is the file contents"); | | |
| 7890 | errdefer allocator.free(result); | | |
| 7891 | suspend { | | |
| 7892 | global_file_frame = @frame(); | | |
| 7893 | } | | |
| 7894 | std.debug.print("readFile returning\n", .{}); | | |
| 7895 | return result; | | |
| 7896 | } | | |
| 7897 | {#code_end#} | | |
| 7898 | <p> | | |
| 7899 | Now we remove the {#syntax#}suspend{#endsyntax#} and {#syntax#}resume{#endsyntax#} code, and | | |
| 7900 | observe the same behavior, with one tiny difference: | | |
| 7901 | </p> | | |
| 7902 | {#code_begin|exe|blocking#} | | |
| 7903 | {#backend_stage1#} | | |
| 7904 | const std = @import("std"); | | |
| 7905 | const Allocator = std.mem.Allocator; | | |
| 7906 | | | |
| 7907 | pub fn main() void { | | |
| 7908 | _ = async amainWrap(); | | |
| 7909 | } | | |
| 7910 | | | |
| 7911 | fn amainWrap() void { | | |
| 7912 | amain() catch |e| { | | |
| 7913 | std.debug.print("{}\n", .{e}); | | |
| 7914 | if (@errorReturnTrace()) |trace| { | | |
| 7915 | std.debug.dumpStackTrace(trace.*); | | |
| 7916 | } | | |
| 7917 | std.process.exit(1); | | |
| 7918 | }; | | |
| 7919 | } | | |
| 7920 | | | |
| 7921 | fn amain() !void { | | |
| 7922 | const allocator = std.heap.page_allocator; | | |
| 7923 | var download_frame = async fetchUrl(allocator, "https://example.com/"); | | |
| 7924 | var awaited_download_frame = false; | | |
| 7925 | errdefer if (!awaited_download_frame) { | | |
| 7926 | if (await download_frame) |r| allocator.free(r) else |_| {} | | |
| 7927 | }; | | |
| 7928 | | | |
| 7929 | var file_frame = async readFile(allocator, "something.txt"); | | |
| 7930 | var awaited_file_frame = false; | | |
| 7931 | errdefer if (!awaited_file_frame) { | | |
| 7932 | if (await file_frame) |r| allocator.free(r) else |_| {} | | |
| 7933 | }; | | |
| 7934 | | | |
| 7935 | awaited_file_frame = true; | | |
| 7936 | const file_text = try await file_frame; | | |
| 7937 | defer allocator.free(file_text); | | |
| 7938 | | | |
| 7939 | awaited_download_frame = true; | | |
| 7940 | const download_text = try await download_frame; | | |
| 7941 | defer allocator.free(download_text); | | |
| 7942 | | | |
| 7943 | std.debug.print("download_text: {s}\n", .{download_text}); | | |
| 7944 | std.debug.print("file_text: {s}\n", .{file_text}); | | |
| 7945 | } | | |
| 7946 | | | |
| 7947 | fn fetchUrl(allocator: Allocator, url: []const u8) ![]u8 { | | |
| 7948 | _ = url; // this is just an example, we don't actually do it! | | |
| 7949 | const result = try allocator.dupe(u8, "this is the downloaded url contents"); | | |
| 7950 | errdefer allocator.free(result); | | |
| 7951 | std.debug.print("fetchUrl returning\n", .{}); | | |
| 7952 | return result; | | |
| 7953 | } | | |
| 7954 | | | |
| 7955 | fn readFile(allocator: Allocator, filename: []const u8) ![]u8 { | | |
| 7956 | _ = filename; // this is just an example, we don't actually do it! | | |
| 7957 | const result = try allocator.dupe(u8, "this is the file contents"); | | |
| 7958 | errdefer allocator.free(result); | | |
| 7959 | std.debug.print("readFile returning\n", .{}); | | |
| 7960 | return result; | | |
| 7961 | } | | |
| 7962 | {#code_end#} | | |
| 7963 | <p> | | |
| 7964 | Previously, the {#syntax#}fetchUrl{#endsyntax#} and {#syntax#}readFile{#endsyntax#} functions suspended, | | |
| 7965 | and were resumed in an order determined by the {#syntax#}main{#endsyntax#} function. Now, | | |
| 7966 | since there are no suspend points, the order of the printed "... returning" messages | | |
| 7967 | is determined by the order of {#syntax#}async{#endsyntax#} callsites. | | |
| 7968 | </p> | | |
| 7969 | {#header_close#} | 7570 | {#header_close#} |
| 7970 | | 7571 | |
| 7971 | {#header_close#} | 7572 | {#header_open|Builtin Functions|2col#} |
| 7972 | {#header_open|Builtin Functions|2col#} | | |
| 7973 | <p> | 7573 | <p> |
| 7974 | Builtin functions are provided by the compiler and are prefixed with <code>@</code>. | 7574 | Builtin functions are provided by the compiler and are prefixed with <code>@</code>. |
| 7975 | The {#syntax#}comptime{#endsyntax#} keyword on a parameter means that the parameter must be known | 7575 | The {#syntax#}comptime{#endsyntax#} keyword on a parameter means that the parameter must be known |
| ... | @@ -8028,49 +7628,6 @@ comptime { | ... | @@ -8028,49 +7628,6 @@ comptime { |
| 8028 | </p> | 7628 | </p> |
| 8029 | {#header_close#} | 7629 | {#header_close#} |
| 8030 | | 7630 | |
| 8031 | {#header_open|@asyncCall#} | | |
| 8032 | <pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: anytype) anyframe->T{#endsyntax#}</pre> | | |
| 8033 | <p> | | |
| 8034 | {#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer, | | |
| 8035 | which may or may not be an {#link|async function|Async Functions#}. | | |
| 8036 | </p> | | |
| 8037 | <p> | | |
| 8038 | The provided {#syntax#}frame_buffer{#endsyntax#} must be large enough to fit the entire function frame. | | |
| 8039 | This size can be determined with {#link|@frameSize#}. To provide a too-small buffer | | |
| 8040 | invokes safety-checked {#link|Undefined Behavior#}. | | |
| 8041 | </p> | | |
| 8042 | <p> | | |
| 8043 | {#syntax#}result_ptr{#endsyntax#} is optional ({#link|null#} may be provided). If provided, | | |
| 8044 | the function call will write its result directly to the result pointer, which will be available to | | |
| 8045 | read after {#link|await|Async and Await#} completes. Any result location provided to | | |
| 8046 | {#syntax#}await{#endsyntax#} will copy the result from {#syntax#}result_ptr{#endsyntax#}. | | |
| 8047 | </p> | | |
| 8048 | {#code_begin|test|async_struct_field_fn_pointer#} | | |
| 8049 | {#backend_stage1#} | | |
| 8050 | const std = @import("std"); | | |
| 8051 | const expect = std.testing.expect; | | |
| 8052 | | | |
| 8053 | test "async fn pointer in a struct field" { | | |
| 8054 | var data: i32 = 1; | | |
| 8055 | const Foo = struct { | | |
| 8056 | bar: fn (*i32) callconv(.Async) void, | | |
| 8057 | }; | | |
| 8058 | var foo = Foo{ .bar = func }; | | |
| 8059 | var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined; | | |
| 8060 | const f = @asyncCall(&bytes, {}, foo.bar, .{&data}); | | |
| 8061 | try expect(data == 2); | | |
| 8062 | resume f; | | |
| 8063 | try expect(data == 4); | | |
| 8064 | } | | |
| 8065 | | | |
| 8066 | fn func(y: *i32) void { | | |
| 8067 | defer y.* += 2; | | |
| 8068 | y.* += 1; | | |
| 8069 | suspend {} | | |
| 8070 | } | | |
| 8071 | {#code_end#} | | |
| 8072 | {#header_close#} | | |
| 8073 | | | |
| 8074 | {#header_open|@atomicLoad#} | 7631 | {#header_open|@atomicLoad#} |
| 8075 | <pre>{#syntax#}@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre> | 7632 | <pre>{#syntax#}@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre> |
| 8076 | <p> | 7633 | <p> |
| ... | @@ -8786,45 +8343,6 @@ test "decl access by string" { | ... | @@ -8786,45 +8343,6 @@ test "decl access by string" { |
| 8786 | {#see_also|@intToFloat#} | 8343 | {#see_also|@intToFloat#} |
| 8787 | {#header_close#} | 8344 | {#header_close#} |
| 8788 | | 8345 | |
| 8789 | {#header_open|@frame#} | | |
| 8790 | <pre>{#syntax#}@frame() *@Frame(func){#endsyntax#}</pre> | | |
| 8791 | <p> | | |
| 8792 | This function returns a pointer to the frame for a given function. This type | | |
| 8793 | can be {#link|coerced|Type Coercion#} to {#syntax#}anyframe->T{#endsyntax#} and | | |
| 8794 | to {#syntax#}anyframe{#endsyntax#}, where {#syntax#}T{#endsyntax#} is the return type | | |
| 8795 | of the function in scope. | | |
| 8796 | </p> | | |
| 8797 | <p> | | |
| 8798 | This function does not mark a suspension point, but it does cause the function in scope | | |
| 8799 | to become an {#link|async function|Async Functions#}. | | |
| 8800 | </p> | | |
| 8801 | {#header_close#} | | |
| 8802 | | | |
| 8803 | {#header_open|@Frame#} | | |
| 8804 | <pre>{#syntax#}@Frame(func: anytype) type{#endsyntax#}</pre> | | |
| 8805 | <p> | | |
| 8806 | This function returns the frame type of a function. This works for {#link|Async Functions#} | | |
| 8807 | as well as any function without a specific calling convention. | | |
| 8808 | </p> | | |
| 8809 | <p> | | |
| 8810 | This type is suitable to be used as the return type of {#link|async|Async and Await#} which | | |
| 8811 | allows one to, for example, heap-allocate an async function frame: | | |
| 8812 | </p> | | |
| 8813 | {#code_begin|test|heap_allocated_frame#} | | |
| 8814 | {#backend_stage1#} | | |
| 8815 | const std = @import("std"); | | |
| 8816 | | | |
| 8817 | test "heap allocated frame" { | | |
| 8818 | const frame = try std.heap.page_allocator.create(@Frame(func)); | | |
| 8819 | frame.* = async func(); | | |
| 8820 | } | | |
| 8821 | | | |
| 8822 | fn func() void { | | |
| 8823 | suspend {} | | |
| 8824 | } | | |
| 8825 | {#code_end#} | | |
| 8826 | {#header_close#} | | |
| 8827 | | | |
| 8828 | {#header_open|@frameAddress#} | 8346 | {#header_open|@frameAddress#} |
| 8829 | <pre>{#syntax#}@frameAddress() usize{#endsyntax#}</pre> | 8347 | <pre>{#syntax#}@frameAddress() usize{#endsyntax#}</pre> |
| 8830 | <p> | 8348 | <p> |
| ... | @@ -8840,17 +8358,6 @@ fn func() void { | ... | @@ -8840,17 +8358,6 @@ fn func() void { |
| 8840 | </p> | 8358 | </p> |
| 8841 | {#header_close#} | 8359 | {#header_close#} |
| 8842 | | 8360 | |
| 8843 | {#header_open|@frameSize#} | | |
| 8844 | <pre>{#syntax#}@frameSize(func: anytype) usize{#endsyntax#}</pre> | | |
| 8845 | <p> | | |
| 8846 | This is the same as {#syntax#}@sizeOf(@Frame(func)){#endsyntax#}, where {#syntax#}func{#endsyntax#} | | |
| 8847 | may be runtime-known. | | |
| 8848 | </p> | | |
| 8849 | <p> | | |
| 8850 | This function is typically used in conjunction with {#link|@asyncCall#}. | | |
| 8851 | </p> | | |
| 8852 | {#header_close#} | | |
| 8853 | | | |
| 8854 | {#header_open|@hasDecl#} | 8361 | {#header_open|@hasDecl#} |
| 8855 | <pre>{#syntax#}@hasDecl(comptime Container: type, comptime name: []const u8) bool{#endsyntax#}</pre> | 8362 | <pre>{#syntax#}@hasDecl(comptime Container: type, comptime name: []const u8) bool{#endsyntax#}</pre> |
| 8856 | <p> | 8363 | <p> |
| ... | @@ -9851,7 +9358,6 @@ test "integer truncation" { | ... | @@ -9851,7 +9358,6 @@ test "integer truncation" { |
| 9851 | <li>{#link|Error Union Type#}</li> | 9358 | <li>{#link|Error Union Type#}</li> |
| 9852 | <li>{#link|Vectors#}</li> | 9359 | <li>{#link|Vectors#}</li> |
| 9853 | <li>{#link|opaque#}</li> | 9360 | <li>{#link|opaque#}</li> |
| 9854 | <li>{#link|@Frame#}</li> | | |
| 9855 | <li>{#syntax#}anyframe{#endsyntax#}</li> | 9361 | <li>{#syntax#}anyframe{#endsyntax#}</li> |
| 9856 | <li>{#link|struct#}</li> | 9362 | <li>{#link|struct#}</li> |
| 9857 | <li>{#link|enum#}</li> | 9363 | <li>{#link|enum#}</li> |
| ... | @@ -10242,7 +9748,6 @@ test "wraparound addition and subtraction" { | ... | @@ -10242,7 +9748,6 @@ test "wraparound addition and subtraction" { |
| 10242 | {#header_open|Exact Left Shift Overflow#} | 9748 | {#header_open|Exact Left Shift Overflow#} |
| 10243 | <p>At compile-time:</p> | 9749 | <p>At compile-time:</p> |
| 10244 | {#code_begin|test_err|operation caused overflow#} | 9750 | {#code_begin|test_err|operation caused overflow#} |
| 10245 | {#backend_stage1#} | | |
| 10246 | comptime { | 9751 | comptime { |
| 10247 | const x = @shlExact(@as(u8, 0b01010101), 2); | 9752 | const x = @shlExact(@as(u8, 0b01010101), 2); |
| 10248 | _ = x; | 9753 | _ = x; |
| ... | @@ -10262,7 +9767,6 @@ pub fn main() void { | ... | @@ -10262,7 +9767,6 @@ pub fn main() void { |
| 10262 | {#header_open|Exact Right Shift Overflow#} | 9767 | {#header_open|Exact Right Shift Overflow#} |
| 10263 | <p>At compile-time:</p> | 9768 | <p>At compile-time:</p> |
| 10264 | {#code_begin|test_err|exact shift shifted out 1 bits#} | 9769 | {#code_begin|test_err|exact shift shifted out 1 bits#} |
| 10265 | {#backend_stage1#} | | |
| 10266 | comptime { | 9770 | comptime { |
| 10267 | const x = @shrExact(@as(u8, 0b10101010), 2); | 9771 | const x = @shrExact(@as(u8, 0b10101010), 2); |
| 10268 | _ = x; | 9772 | _ = x; |
| ... | @@ -10325,8 +9829,7 @@ pub fn main() void { | ... | @@ -10325,8 +9829,7 @@ pub fn main() void { |
| 10325 | {#header_close#} | 9829 | {#header_close#} |
| 10326 | {#header_open|Exact Division Remainder#} | 9830 | {#header_open|Exact Division Remainder#} |
| 10327 | <p>At compile-time:</p> | 9831 | <p>At compile-time:</p> |
| 10328 | {#code_begin|test_err|exact division had a remainder#} | 9832 | {#code_begin|test_err|exact division produced remainder#} |
| 10329 | {#backend_stage1#} | | |
| 10330 | comptime { | 9833 | comptime { |
| 10331 | const a: u32 = 10; | 9834 | const a: u32 = 10; |
| 10332 | const b: u32 = 3; | 9835 | const b: u32 = 3; |
| ... | @@ -10636,7 +10139,6 @@ fn bar(f: *Foo) void { | ... | @@ -10636,7 +10139,6 @@ fn bar(f: *Foo) void { |
| 10636 | </p> | 10139 | </p> |
| 10637 | <p>At compile-time:</p> | 10140 | <p>At compile-time:</p> |
| 10638 | {#code_begin|test_err|null pointer casted to type#} | 10141 | {#code_begin|test_err|null pointer casted to type#} |
| 10639 | {#backend_stage1#} | | |
| 10640 | comptime { | 10142 | comptime { |
| 10641 | const opt_ptr: ?*i32 = null; | 10143 | const opt_ptr: ?*i32 = null; |
| 10642 | const ptr = @ptrCast(*i32, opt_ptr); | 10144 | const ptr = @ptrCast(*i32, opt_ptr); |
| ... | @@ -12271,9 +11773,6 @@ fn readU32Be() u32 {} | ... | @@ -12271,9 +11773,6 @@ fn readU32Be() u32 {} |
| 12271 | </th> | 11773 | </th> |
| 12272 | <td> | 11774 | <td> |
| 12273 | {#syntax#}resume{#endsyntax#} will continue execution of a function frame after the point the function was suspended. | 11775 | {#syntax#}resume{#endsyntax#} will continue execution of a function frame after the point the function was suspended. |
| 12274 | <ul> | | |
| 12275 | <li>See also {#link|Suspend and Resume#}</li> | | |
| 12276 | </ul> | | |
| 12277 | </td> | 11776 | </td> |
| 12278 | </tr> | 11777 | </tr> |
| 12279 | <tr> | 11778 | <tr> |
| ... | @@ -12317,9 +11816,6 @@ fn readU32Be() u32 {} | ... | @@ -12317,9 +11816,6 @@ fn readU32Be() u32 {} |
| 12317 | {#syntax#}suspend{#endsyntax#} will cause control flow to return to the call site or resumer of the function. | 11816 | {#syntax#}suspend{#endsyntax#} will cause control flow to return to the call site or resumer of the function. |
| 12318 | {#syntax#}suspend{#endsyntax#} can also be used before a block within a function, | 11817 | {#syntax#}suspend{#endsyntax#} can also be used before a block within a function, |
| 12319 | to allow the function access to its frame before control flow returns to the call site. | 11818 | to allow the function access to its frame before control flow returns to the call site. |
| 12320 | <ul> | | |
| 12321 | <li>See also {#link|Suspend and Resume#}</li> | | |
| 12322 | </ul> | | |
| 12323 | </td> | 11819 | </td> |
| 12324 | </tr> | 11820 | </tr> |
| 12325 | <tr> | 11821 | <tr> |