authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-01 15:28:44-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-01 15:28:44-07:00
log6e52f36d46f6dbcd4be6295fea6d54868399c12f
tree447c91fa3e8eb7b1475b9b9d8e0bc81f580436b7
parent2823fcabd1974550b889a56e8ada0eb52f3d2080

langref: eliminate dependencies on stage1

This commit removes async/await/suspend/resume from the language reference, as that feature does not yet work in the self-hosted compiler. We will be regressing this feature temporarily. Users of these language features should stick with 0.10.x with the `-fstage1` flag until they are restored. See tracking issue #6025.

1 files changed, 8 insertions(+), 512 deletions(-)

doc/langref.html.in+8-512
......@@ -1179,33 +1179,8 @@ test "this will be skipped" {
11791179 return error.SkipZigTest;
11801180}
11811181 {#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#}
1189const std = @import("std");
1190
1191test "async skip test" {
1192 var frame = async func();
1193 const result = await frame;
1194 try std.testing.expect(result == 1);
1195}
1196
1197fn 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>
12081182 {#header_close#}
1183
12091184 {#header_open|Report Memory Leaks#}
12101185 <p>
12111186 When code allocates {#link|Memory#} using the {#link|Zig Standard Library#}'s testing allocator,
......@@ -6288,7 +6263,6 @@ test "float widening" {
62886263 <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>
62896264 </ul>
62906265 {#code_begin|test_err#}
6291 {#backend_stage1#}
62926266// Compile time coercion of float to int
62936267test "implicit cast to comptime_int" {
62946268 var f: f32 = 54.0 / 5;
......@@ -7400,7 +7374,6 @@ pub fn main() void {
74007374 </p>
74017375 {#code_begin|exe#}
74027376 {#target_linux_x86_64#}
7403 {#backend_stage1#}
74047377pub fn main() noreturn {
74057378 const msg = "hello world\n";
74067379 _ = syscall3(SYS_write, STDOUT_FILENO, @ptrToInt(msg), msg.len);
......@@ -7588,388 +7561,15 @@ test "global assembly" {
75887561 <p>TODO: @atomic rmw</p>
75897562 <p>TODO: builtin atomic memory ordering enum</p>
75907563 {#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#}
7622const std = @import("std");
7623const expect = std.testing.expect;
7624
7625var x: i32 = 1;
7626
7627test "suspend with no resume" {
7628 var frame = async func();
7629 try expect(x == 2);
7630 _ = frame;
7631}
7632
7633fn 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#}
7650const std = @import("std");
7651const expect = std.testing.expect;
7652
7653var the_frame: anyframe = undefined;
7654var result = false;
7655
7656test "async function suspend with block" {
7657 _ = async testSuspendBlock();
7658 try expect(!result);
7659 resume the_frame;
7660 try expect(result);
7661}
7662
7663fn 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#}
7689const std = @import("std");
7690const expect = std.testing.expect;
7691
7692test "resume from suspend" {
7693 var my_result: i32 = 1;
7694 _ = async testResumeFromSuspend(&my_result);
7695 try std.testing.expect(my_result == 2);
7696}
7697fn 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#}
7726const std = @import("std");
7727const expect = std.testing.expect;
7728
7729test "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
7737fn 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
7748fn 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#}
7771const std = @import("std");
7772const expect = std.testing.expect;
7773
7774var the_frame: anyframe = undefined;
7775var final_result: i32 = 0;
7776
7777test "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}
7786fn amain() void {
7787 seq('b');
7788 var f = async another();
7789 seq('e');
7790 final_result = await f;
7791 seq('h');
7792}
7793fn another() i32 {
7794 seq('c');
7795 suspend {
7796 seq('d');
7797 the_frame = @frame();
7798 }
7799 seq('g');
7800 return 1234;
7801}
7802
7803var seq_points = [_]u8{0} ** "abcdefghi".len;
7804var seq_index: usize = 0;
7805
7806fn 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#}
7825const std = @import("std");
7826const Allocator = std.mem.Allocator;
7827
7828pub 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
7838fn 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
7848fn 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 };
78557564
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
7874var global_download_frame: anyframe = undefined;
7875fn 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
7886var global_file_frame: anyframe = undefined;
7887fn 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#}
7904const std = @import("std");
7905const Allocator = std.mem.Allocator;
7906
7907pub fn main() void {
7908 _ = async amainWrap();
7909}
7910
7911fn 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
7921fn 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
7947fn 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
7955fn 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>
79697570 {#header_close#}
79707571
7971 {#header_close#}
7972 {#header_open|Builtin Functions|2col#}
7572 {#header_open|Builtin Functions|2col#}
79737573 <p>
79747574 Builtin functions are provided by the compiler and are prefixed with <code>@</code>.
79757575 The {#syntax#}comptime{#endsyntax#} keyword on a parameter means that the parameter must be known
......@@ -8028,49 +7628,6 @@ comptime {
80287628 </p>
80297629 {#header_close#}
80307630
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#}
8050const std = @import("std");
8051const expect = std.testing.expect;
8052
8053test "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
8066fn func(y: *i32) void {
8067 defer y.* += 2;
8068 y.* += 1;
8069 suspend {}
8070}
8071 {#code_end#}
8072 {#header_close#}
8073
80747631 {#header_open|@atomicLoad#}
80757632 <pre>{#syntax#}@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}</pre>
80767633 <p>
......@@ -8786,45 +8343,6 @@ test "decl access by string" {
87868343 {#see_also|@intToFloat#}
87878344 {#header_close#}
87888345
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#}
8815const std = @import("std");
8816
8817test "heap allocated frame" {
8818 const frame = try std.heap.page_allocator.create(@Frame(func));
8819 frame.* = async func();
8820}
8821
8822fn func() void {
8823 suspend {}
8824}
8825 {#code_end#}
8826 {#header_close#}
8827
88288346 {#header_open|@frameAddress#}
88298347 <pre>{#syntax#}@frameAddress() usize{#endsyntax#}</pre>
88308348 <p>
......@@ -8840,17 +8358,6 @@ fn func() void {
88408358 </p>
88418359 {#header_close#}
88428360
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
88548361 {#header_open|@hasDecl#}
88558362 <pre>{#syntax#}@hasDecl(comptime Container: type, comptime name: []const u8) bool{#endsyntax#}</pre>
88568363 <p>
......@@ -9851,7 +9358,6 @@ test "integer truncation" {
98519358 <li>{#link|Error Union Type#}</li>
98529359 <li>{#link|Vectors#}</li>
98539360 <li>{#link|opaque#}</li>
9854 <li>{#link|@Frame#}</li>
98559361 <li>{#syntax#}anyframe{#endsyntax#}</li>
98569362 <li>{#link|struct#}</li>
98579363 <li>{#link|enum#}</li>
......@@ -10242,7 +9748,6 @@ test "wraparound addition and subtraction" {
102429748 {#header_open|Exact Left Shift Overflow#}
102439749 <p>At compile-time:</p>
102449750 {#code_begin|test_err|operation caused overflow#}
10245 {#backend_stage1#}
102469751comptime {
102479752 const x = @shlExact(@as(u8, 0b01010101), 2);
102489753 _ = x;
......@@ -10262,7 +9767,6 @@ pub fn main() void {
102629767 {#header_open|Exact Right Shift Overflow#}
102639768 <p>At compile-time:</p>
102649769 {#code_begin|test_err|exact shift shifted out 1 bits#}
10265 {#backend_stage1#}
102669770comptime {
102679771 const x = @shrExact(@as(u8, 0b10101010), 2);
102689772 _ = x;
......@@ -10325,8 +9829,7 @@ pub fn main() void {
103259829 {#header_close#}
103269830 {#header_open|Exact Division Remainder#}
103279831 <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#}
103309833comptime {
103319834 const a: u32 = 10;
103329835 const b: u32 = 3;
......@@ -10636,7 +10139,6 @@ fn bar(f: *Foo) void {
1063610139 </p>
1063710140 <p>At compile-time:</p>
1063810141 {#code_begin|test_err|null pointer casted to type#}
10639 {#backend_stage1#}
1064010142comptime {
1064110143 const opt_ptr: ?*i32 = null;
1064210144 const ptr = @ptrCast(*i32, opt_ptr);
......@@ -12271,9 +11773,6 @@ fn readU32Be() u32 {}
1227111773 </th>
1227211774 <td>
1227311775 {#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>
1227711776 </td>
1227811777 </tr>
1227911778 <tr>
......@@ -12317,9 +11816,6 @@ fn readU32Be() u32 {}
1231711816 {#syntax#}suspend{#endsyntax#} will cause control flow to return to the call site or resumer of the function.
1231811817 {#syntax#}suspend{#endsyntax#} can also be used before a block within a function,
1231911818 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>
1232311819 </td>
1232411820 </tr>
1232511821 <tr>