| ... | ... | @@ -1179,33 +1179,8 @@ test "this will be skipped" { |
| 1179 | 1179 | return error.SkipZigTest; |
| 1180 | 1180 | } |
| 1181 | 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 | 1182 | {#header_close#} |
| 1183 | |
| 1209 | 1184 | {#header_open|Report Memory Leaks#} |
| 1210 | 1185 | <p> |
| 1211 | 1186 | When code allocates {#link|Memory#} using the {#link|Zig Standard Library#}'s testing allocator, |
| ... | ... | @@ -6288,7 +6263,6 @@ test "float widening" { |
| 6288 | 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 | 6264 | </ul> |
| 6290 | 6265 | {#code_begin|test_err#} |
| 6291 | | {#backend_stage1#} |
| 6292 | 6266 | // Compile time coercion of float to int |
| 6293 | 6267 | test "implicit cast to comptime_int" { |
| 6294 | 6268 | var f: f32 = 54.0 / 5; |
| ... | ... | @@ -7400,7 +7374,6 @@ pub fn main() void { |
| 7400 | 7374 | </p> |
| 7401 | 7375 | {#code_begin|exe#} |
| 7402 | 7376 | {#target_linux_x86_64#} |
| 7403 | | {#backend_stage1#} |
| 7404 | 7377 | pub fn main() noreturn { |
| 7405 | 7378 | const msg = "hello world\n"; |
| 7406 | 7379 | _ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(msg), msg.len); |
| ... | ... | @@ -7588,388 +7561,15 @@ test "global assembly" { |
| 7588 | 7561 | <p>TODO: @atomic rmw</p> |
| 7589 | 7562 | <p>TODO: builtin atomic memory ordering enum</p> |
| 7590 | 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"); |
| 7857 | | var awaited_file_frame = false; |
| 7858 | | errdefer if (!awaited_file_frame) { |
| 7859 | | if (await file_frame) |r| allocator.free(r) else |_| {} |
| 7860 | | }; |
| 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> |
| 7565 | {#header_open|Async Functions#} |
| 7566 | <p>Async functions are being temporarily regressed and will be |
| 7567 | <a href="https://github.com/ziglang/zig/issues/6025">restored before Zig |
| 7568 | 0.11.0 is tagged</a>. I apologize for the instability. Please use Zig 0.10.0 with |
| 7569 | the <code>-fstage1</code> flag for now if you need this feature.</p> |
| 7969 | 7570 | {#header_close#} |
| 7970 | 7571 | |
| 7971 | | {#header_close#} |
| 7972 | | {#header_open|Builtin Functions|2col#} |
| 7572 | {#header_open|Builtin Functions|2col#} |
| 7973 | 7573 | <p> |
| 7974 | 7574 | Builtin functions are provided by the compiler and are prefixed with <code>@</code>. |
| 7975 | 7575 | The {#syntax#}comptime{#endsyntax#} keyword on a parameter means that the parameter must be known |
| ... | ... | @@ -8028,49 +7628,6 @@ comptime { |
| 8028 | 7628 | </p> |
| 8029 | 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 | 7631 | {#header_open|@atomicLoad#} |
| 8075 | 7632 | <pre>{#syntax#}@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre> |
| 8076 | 7633 | <p> |
| ... | ... | @@ -8786,45 +8343,6 @@ test "decl access by string" { |
| 8786 | 8343 | {#see_also|@intToFloat#} |
| 8787 | 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 | 8346 | {#header_open|@frameAddress#} |
| 8829 | 8347 | <pre>{#syntax#}@frameAddress() usize{#endsyntax#}</pre> |
| 8830 | 8348 | <p> |
| ... | ... | @@ -8840,17 +8358,6 @@ fn func() void { |
| 8840 | 8358 | </p> |
| 8841 | 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 | 8361 | {#header_open|@hasDecl#} |
| 8855 | 8362 | <pre>{#syntax#}@hasDecl(comptime Container: type, comptime name: []const u8) bool{#endsyntax#}</pre> |
| 8856 | 8363 | <p> |
| ... | ... | @@ -9851,7 +9358,6 @@ test "integer truncation" { |
| 9851 | 9358 | <li>{#link|Error Union Type#}</li> |
| 9852 | 9359 | <li>{#link|Vectors#}</li> |
| 9853 | 9360 | <li>{#link|opaque#}</li> |
| 9854 | | <li>{#link|@Frame#}</li> |
| 9855 | 9361 | <li>{#syntax#}anyframe{#endsyntax#}</li> |
| 9856 | 9362 | <li>{#link|struct#}</li> |
| 9857 | 9363 | <li>{#link|enum#}</li> |
| ... | ... | @@ -10242,7 +9748,6 @@ test "wraparound addition and subtraction" { |
| 10242 | 9748 | {#header_open|Exact Left Shift Overflow#} |
| 10243 | 9749 | <p>At compile-time:</p> |
| 10244 | 9750 | {#code_begin|test_err|operation caused overflow#} |
| 10245 | | {#backend_stage1#} |
| 10246 | 9751 | comptime { |
| 10247 | 9752 | const x = @shlExact(@as(u8, 0b01010101), 2); |
| 10248 | 9753 | _ = x; |
| ... | ... | @@ -10262,7 +9767,6 @@ pub fn main() void { |
| 10262 | 9767 | {#header_open|Exact Right Shift Overflow#} |
| 10263 | 9768 | <p>At compile-time:</p> |
| 10264 | 9769 | {#code_begin|test_err|exact shift shifted out 1 bits#} |
| 10265 | | {#backend_stage1#} |
| 10266 | 9770 | comptime { |
| 10267 | 9771 | const x = @shrExact(@as(u8, 0b10101010), 2); |
| 10268 | 9772 | _ = x; |
| ... | ... | @@ -10325,8 +9829,7 @@ pub fn main() void { |
| 10325 | 9829 | {#header_close#} |
| 10326 | 9830 | {#header_open|Exact Division Remainder#} |
| 10327 | 9831 | <p>At compile-time:</p> |
| 10328 | | {#code_begin|test_err|exact division had a remainder#} |
| 10329 | | {#backend_stage1#} |
| 9832 | {#code_begin|test_err|exact division produced remainder#} |
| 10330 | 9833 | comptime { |
| 10331 | 9834 | const a: u32 = 10; |
| 10332 | 9835 | const b: u32 = 3; |
| ... | ... | @@ -10636,7 +10139,6 @@ fn bar(f: *Foo) void { |
| 10636 | 10139 | </p> |
| 10637 | 10140 | <p>At compile-time:</p> |
| 10638 | 10141 | {#code_begin|test_err|null pointer casted to type#} |
| 10639 | | {#backend_stage1#} |
| 10640 | 10142 | comptime { |
| 10641 | 10143 | const opt_ptr: ?*i32 = null; |
| 10642 | 10144 | const ptr = @ptrCast(*i32, opt_ptr); |
| ... | ... | @@ -12271,9 +11773,6 @@ fn readU32Be() u32 {} |
| 12271 | 11773 | </th> |
| 12272 | 11774 | <td> |
| 12273 | 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 | 11776 | </td> |
| 12278 | 11777 | </tr> |
| 12279 | 11778 | <tr> |
| ... | ... | @@ -12317,9 +11816,6 @@ fn readU32Be() u32 {} |
| 12317 | 11816 | {#syntax#}suspend{#endsyntax#} will cause control flow to return to the call site or resumer of the function. |
| 12318 | 11817 | {#syntax#}suspend{#endsyntax#} can also be used before a block within a function, |
| 12319 | 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 | 11819 | </td> |
| 12324 | 11820 | </tr> |
| 12325 | 11821 | <tr> |