| author | |
| committer | |
| log | 7799423f24a0f628641e772146f1e6a5c2f6bdcb |
| tree | 77f5b4c92b5ce9fc88cb3ca6e9d5fba4838e17fc |
| parent | 5749dc49d839ba607392ed0934008773d33f47d7 |
| signature | Commit is signed but in an unrecognized format. |
5 files changed, 829 insertions(+), 829 deletions(-)
test/stage1/behavior.zig+6-6| ... | @@ -3,12 +3,13 @@ comptime { | ... | @@ -3,12 +3,13 @@ comptime { |
| 3 | _ = @import("behavior/alignof.zig"); | 3 | _ = @import("behavior/alignof.zig"); |
| 4 | _ = @import("behavior/array.zig"); | 4 | _ = @import("behavior/array.zig"); |
| 5 | _ = @import("behavior/asm.zig"); | 5 | _ = @import("behavior/asm.zig"); |
| 6 | _ = @import("behavior/async_fn.zig"); | ||
| 6 | _ = @import("behavior/atomics.zig"); | 7 | _ = @import("behavior/atomics.zig"); |
| 8 | _ = @import("behavior/await_struct.zig"); | ||
| 7 | _ = @import("behavior/bit_shifting.zig"); | 9 | _ = @import("behavior/bit_shifting.zig"); |
| 8 | _ = @import("behavior/bitcast.zig"); | 10 | _ = @import("behavior/bitcast.zig"); |
| 9 | _ = @import("behavior/bitreverse.zig"); | 11 | _ = @import("behavior/bitreverse.zig"); |
| 10 | _ = @import("behavior/bool.zig"); | 12 | _ = @import("behavior/bool.zig"); |
| 11 | _ = @import("behavior/byteswap.zig"); | ||
| 12 | _ = @import("behavior/bugs/1025.zig"); | 13 | _ = @import("behavior/bugs/1025.zig"); |
| 13 | _ = @import("behavior/bugs/1076.zig"); | 14 | _ = @import("behavior/bugs/1076.zig"); |
| 14 | _ = @import("behavior/bugs/1111.zig"); | 15 | _ = @import("behavior/bugs/1111.zig"); |
| ... | @@ -38,23 +39,24 @@ comptime { | ... | @@ -38,23 +39,24 @@ comptime { |
| 38 | _ = @import("behavior/bugs/726.zig"); | 39 | _ = @import("behavior/bugs/726.zig"); |
| 39 | _ = @import("behavior/bugs/828.zig"); | 40 | _ = @import("behavior/bugs/828.zig"); |
| 40 | _ = @import("behavior/bugs/920.zig"); | 41 | _ = @import("behavior/bugs/920.zig"); |
| 42 | _ = @import("behavior/byteswap.zig"); | ||
| 41 | _ = @import("behavior/byval_arg_var.zig"); | 43 | _ = @import("behavior/byval_arg_var.zig"); |
| 42 | _ = @import("behavior/cancel.zig"); | 44 | _ = @import("behavior/cancel.zig"); |
| 43 | _ = @import("behavior/cast.zig"); | 45 | _ = @import("behavior/cast.zig"); |
| 44 | _ = @import("behavior/const_slice_child.zig"); | 46 | _ = @import("behavior/const_slice_child.zig"); |
| 45 | _ = @import("behavior/coroutine_await_struct.zig"); | ||
| 46 | _ = @import("behavior/coroutines.zig"); | ||
| 47 | _ = @import("behavior/defer.zig"); | 47 | _ = @import("behavior/defer.zig"); |
| 48 | _ = @import("behavior/enum.zig"); | 48 | _ = @import("behavior/enum.zig"); |
| 49 | _ = @import("behavior/enum_with_members.zig"); | 49 | _ = @import("behavior/enum_with_members.zig"); |
| 50 | _ = @import("behavior/error.zig"); | 50 | _ = @import("behavior/error.zig"); |
| 51 | _ = @import("behavior/eval.zig"); | 51 | _ = @import("behavior/eval.zig"); |
| 52 | _ = @import("behavior/field_parent_ptr.zig"); | 52 | _ = @import("behavior/field_parent_ptr.zig"); |
| 53 | _ = @import("behavior/floatop.zig"); | ||
| 53 | _ = @import("behavior/fn.zig"); | 54 | _ = @import("behavior/fn.zig"); |
| 54 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); | 55 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); |
| 55 | _ = @import("behavior/for.zig"); | 56 | _ = @import("behavior/for.zig"); |
| 56 | _ = @import("behavior/generics.zig"); | 57 | _ = @import("behavior/generics.zig"); |
| 57 | _ = @import("behavior/hasdecl.zig"); | 58 | _ = @import("behavior/hasdecl.zig"); |
| 59 | _ = @import("behavior/hasfield.zig"); | ||
| 58 | _ = @import("behavior/if.zig"); | 60 | _ = @import("behavior/if.zig"); |
| 59 | _ = @import("behavior/import.zig"); | 61 | _ = @import("behavior/import.zig"); |
| 60 | _ = @import("behavior/incomplete_struct_param_tld.zig"); | 62 | _ = @import("behavior/incomplete_struct_param_tld.zig"); |
| ... | @@ -63,14 +65,13 @@ comptime { | ... | @@ -63,14 +65,13 @@ comptime { |
| 63 | _ = @import("behavior/math.zig"); | 65 | _ = @import("behavior/math.zig"); |
| 64 | _ = @import("behavior/merge_error_sets.zig"); | 66 | _ = @import("behavior/merge_error_sets.zig"); |
| 65 | _ = @import("behavior/misc.zig"); | 67 | _ = @import("behavior/misc.zig"); |
| 68 | _ = @import("behavior/muladd.zig"); | ||
| 66 | _ = @import("behavior/namespace_depends_on_compile_var.zig"); | 69 | _ = @import("behavior/namespace_depends_on_compile_var.zig"); |
| 67 | _ = @import("behavior/new_stack_call.zig"); | 70 | _ = @import("behavior/new_stack_call.zig"); |
| 68 | _ = @import("behavior/null.zig"); | 71 | _ = @import("behavior/null.zig"); |
| 69 | _ = @import("behavior/optional.zig"); | 72 | _ = @import("behavior/optional.zig"); |
| 70 | _ = @import("behavior/pointers.zig"); | 73 | _ = @import("behavior/pointers.zig"); |
| 71 | _ = @import("behavior/popcount.zig"); | 74 | _ = @import("behavior/popcount.zig"); |
| 72 | _ = @import("behavior/muladd.zig"); | ||
| 73 | _ = @import("behavior/floatop.zig"); | ||
| 74 | _ = @import("behavior/ptrcast.zig"); | 75 | _ = @import("behavior/ptrcast.zig"); |
| 75 | _ = @import("behavior/pub_enum.zig"); | 76 | _ = @import("behavior/pub_enum.zig"); |
| 76 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); | 77 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); |
| ... | @@ -99,5 +100,4 @@ comptime { | ... | @@ -99,5 +100,4 @@ comptime { |
| 99 | _ = @import("behavior/void.zig"); | 100 | _ = @import("behavior/void.zig"); |
| 100 | _ = @import("behavior/while.zig"); | 101 | _ = @import("behavior/while.zig"); |
| 101 | _ = @import("behavior/widening.zig"); | 102 | _ = @import("behavior/widening.zig"); |
| 102 | _ = @import("behavior/hasfield.zig"); | ||
| 103 | } | 103 | } |
test/stage1/behavior/async_fn.zig created+779| ... | @@ -0,0 +1,779 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const expect = std.testing.expect; | ||
| 4 | const expectEqual = std.testing.expectEqual; | ||
| 5 | |||
| 6 | var global_x: i32 = 1; | ||
| 7 | |||
| 8 | test "simple coroutine suspend and resume" { | ||
| 9 | const frame = async simpleAsyncFn(); | ||
| 10 | expect(global_x == 2); | ||
| 11 | resume frame; | ||
| 12 | expect(global_x == 3); | ||
| 13 | const af: anyframe->void = &frame; | ||
| 14 | resume frame; | ||
| 15 | expect(global_x == 4); | ||
| 16 | } | ||
| 17 | fn simpleAsyncFn() void { | ||
| 18 | global_x += 1; | ||
| 19 | suspend; | ||
| 20 | global_x += 1; | ||
| 21 | suspend; | ||
| 22 | global_x += 1; | ||
| 23 | } | ||
| 24 | |||
| 25 | var global_y: i32 = 1; | ||
| 26 | |||
| 27 | test "pass parameter to coroutine" { | ||
| 28 | const p = async simpleAsyncFnWithArg(2); | ||
| 29 | expect(global_y == 3); | ||
| 30 | resume p; | ||
| 31 | expect(global_y == 5); | ||
| 32 | } | ||
| 33 | fn simpleAsyncFnWithArg(delta: i32) void { | ||
| 34 | global_y += delta; | ||
| 35 | suspend; | ||
| 36 | global_y += delta; | ||
| 37 | } | ||
| 38 | |||
| 39 | test "suspend at end of function" { | ||
| 40 | const S = struct { | ||
| 41 | var x: i32 = 1; | ||
| 42 | |||
| 43 | fn doTheTest() void { | ||
| 44 | expect(x == 1); | ||
| 45 | const p = async suspendAtEnd(); | ||
| 46 | expect(x == 2); | ||
| 47 | } | ||
| 48 | |||
| 49 | fn suspendAtEnd() void { | ||
| 50 | x += 1; | ||
| 51 | suspend; | ||
| 52 | } | ||
| 53 | }; | ||
| 54 | S.doTheTest(); | ||
| 55 | } | ||
| 56 | |||
| 57 | test "local variable in async function" { | ||
| 58 | const S = struct { | ||
| 59 | var x: i32 = 0; | ||
| 60 | |||
| 61 | fn doTheTest() void { | ||
| 62 | expect(x == 0); | ||
| 63 | const p = async add(1, 2); | ||
| 64 | expect(x == 0); | ||
| 65 | resume p; | ||
| 66 | expect(x == 0); | ||
| 67 | resume p; | ||
| 68 | expect(x == 0); | ||
| 69 | resume p; | ||
| 70 | expect(x == 3); | ||
| 71 | } | ||
| 72 | |||
| 73 | fn add(a: i32, b: i32) void { | ||
| 74 | var accum: i32 = 0; | ||
| 75 | suspend; | ||
| 76 | accum += a; | ||
| 77 | suspend; | ||
| 78 | accum += b; | ||
| 79 | suspend; | ||
| 80 | x = accum; | ||
| 81 | } | ||
| 82 | }; | ||
| 83 | S.doTheTest(); | ||
| 84 | } | ||
| 85 | |||
| 86 | test "calling an inferred async function" { | ||
| 87 | const S = struct { | ||
| 88 | var x: i32 = 1; | ||
| 89 | var other_frame: *@Frame(other) = undefined; | ||
| 90 | |||
| 91 | fn doTheTest() void { | ||
| 92 | _ = async first(); | ||
| 93 | expect(x == 1); | ||
| 94 | resume other_frame.*; | ||
| 95 | expect(x == 2); | ||
| 96 | } | ||
| 97 | |||
| 98 | fn first() void { | ||
| 99 | other(); | ||
| 100 | } | ||
| 101 | fn other() void { | ||
| 102 | other_frame = @frame(); | ||
| 103 | suspend; | ||
| 104 | x += 1; | ||
| 105 | } | ||
| 106 | }; | ||
| 107 | S.doTheTest(); | ||
| 108 | } | ||
| 109 | |||
| 110 | test "@frameSize" { | ||
| 111 | const S = struct { | ||
| 112 | fn doTheTest() void { | ||
| 113 | { | ||
| 114 | var ptr = @ptrCast(async fn(i32) void, other); | ||
| 115 | const size = @frameSize(ptr); | ||
| 116 | expect(size == @sizeOf(@Frame(other))); | ||
| 117 | } | ||
| 118 | { | ||
| 119 | var ptr = @ptrCast(async fn() void, first); | ||
| 120 | const size = @frameSize(ptr); | ||
| 121 | expect(size == @sizeOf(@Frame(first))); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | fn first() void { | ||
| 126 | other(1); | ||
| 127 | } | ||
| 128 | fn other(param: i32) void { | ||
| 129 | var local: i32 = undefined; | ||
| 130 | suspend; | ||
| 131 | } | ||
| 132 | }; | ||
| 133 | S.doTheTest(); | ||
| 134 | } | ||
| 135 | |||
| 136 | test "coroutine suspend, resume" { | ||
| 137 | const S = struct { | ||
| 138 | var frame: anyframe = undefined; | ||
| 139 | |||
| 140 | fn doTheTest() void { | ||
| 141 | _ = async amain(); | ||
| 142 | seq('d'); | ||
| 143 | resume frame; | ||
| 144 | seq('h'); | ||
| 145 | |||
| 146 | expect(std.mem.eql(u8, points, "abcdefgh")); | ||
| 147 | } | ||
| 148 | |||
| 149 | fn amain() void { | ||
| 150 | seq('a'); | ||
| 151 | var f = async testAsyncSeq(); | ||
| 152 | seq('c'); | ||
| 153 | cancel f; | ||
| 154 | seq('g'); | ||
| 155 | } | ||
| 156 | |||
| 157 | fn testAsyncSeq() void { | ||
| 158 | defer seq('f'); | ||
| 159 | |||
| 160 | seq('b'); | ||
| 161 | suspend { | ||
| 162 | frame = @frame(); | ||
| 163 | } | ||
| 164 | seq('e'); | ||
| 165 | } | ||
| 166 | var points = [_]u8{'x'} ** "abcdefgh".len; | ||
| 167 | var index: usize = 0; | ||
| 168 | |||
| 169 | fn seq(c: u8) void { | ||
| 170 | points[index] = c; | ||
| 171 | index += 1; | ||
| 172 | } | ||
| 173 | }; | ||
| 174 | S.doTheTest(); | ||
| 175 | } | ||
| 176 | |||
| 177 | test "coroutine suspend with block" { | ||
| 178 | const p = async testSuspendBlock(); | ||
| 179 | expect(!global_result); | ||
| 180 | resume a_promise; | ||
| 181 | expect(global_result); | ||
| 182 | } | ||
| 183 | |||
| 184 | var a_promise: anyframe = undefined; | ||
| 185 | var global_result = false; | ||
| 186 | async fn testSuspendBlock() void { | ||
| 187 | suspend { | ||
| 188 | comptime expect(@typeOf(@frame()) == *@Frame(testSuspendBlock)); | ||
| 189 | a_promise = @frame(); | ||
| 190 | } | ||
| 191 | |||
| 192 | // Test to make sure that @frame() works as advertised (issue #1296) | ||
| 193 | // var our_handle: anyframe = @frame(); | ||
| 194 | expect(a_promise == anyframe(@frame())); | ||
| 195 | |||
| 196 | global_result = true; | ||
| 197 | } | ||
| 198 | |||
| 199 | var await_a_promise: anyframe = undefined; | ||
| 200 | var await_final_result: i32 = 0; | ||
| 201 | |||
| 202 | test "coroutine await" { | ||
| 203 | await_seq('a'); | ||
| 204 | const p = async await_amain(); | ||
| 205 | await_seq('f'); | ||
| 206 | resume await_a_promise; | ||
| 207 | await_seq('i'); | ||
| 208 | expect(await_final_result == 1234); | ||
| 209 | expect(std.mem.eql(u8, await_points, "abcdefghi")); | ||
| 210 | } | ||
| 211 | async fn await_amain() void { | ||
| 212 | await_seq('b'); | ||
| 213 | const p = async await_another(); | ||
| 214 | await_seq('e'); | ||
| 215 | await_final_result = await p; | ||
| 216 | await_seq('h'); | ||
| 217 | } | ||
| 218 | async fn await_another() i32 { | ||
| 219 | await_seq('c'); | ||
| 220 | suspend { | ||
| 221 | await_seq('d'); | ||
| 222 | await_a_promise = @frame(); | ||
| 223 | } | ||
| 224 | await_seq('g'); | ||
| 225 | return 1234; | ||
| 226 | } | ||
| 227 | |||
| 228 | var await_points = [_]u8{0} ** "abcdefghi".len; | ||
| 229 | var await_seq_index: usize = 0; | ||
| 230 | |||
| 231 | fn await_seq(c: u8) void { | ||
| 232 | await_points[await_seq_index] = c; | ||
| 233 | await_seq_index += 1; | ||
| 234 | } | ||
| 235 | |||
| 236 | var early_final_result: i32 = 0; | ||
| 237 | |||
| 238 | test "coroutine await early return" { | ||
| 239 | early_seq('a'); | ||
| 240 | const p = async early_amain(); | ||
| 241 | early_seq('f'); | ||
| 242 | expect(early_final_result == 1234); | ||
| 243 | expect(std.mem.eql(u8, early_points, "abcdef")); | ||
| 244 | } | ||
| 245 | async fn early_amain() void { | ||
| 246 | early_seq('b'); | ||
| 247 | const p = async early_another(); | ||
| 248 | early_seq('d'); | ||
| 249 | early_final_result = await p; | ||
| 250 | early_seq('e'); | ||
| 251 | } | ||
| 252 | async fn early_another() i32 { | ||
| 253 | early_seq('c'); | ||
| 254 | return 1234; | ||
| 255 | } | ||
| 256 | |||
| 257 | var early_points = [_]u8{0} ** "abcdef".len; | ||
| 258 | var early_seq_index: usize = 0; | ||
| 259 | |||
| 260 | fn early_seq(c: u8) void { | ||
| 261 | early_points[early_seq_index] = c; | ||
| 262 | early_seq_index += 1; | ||
| 263 | } | ||
| 264 | |||
| 265 | test "async function with dot syntax" { | ||
| 266 | const S = struct { | ||
| 267 | var y: i32 = 1; | ||
| 268 | async fn foo() void { | ||
| 269 | y += 1; | ||
| 270 | suspend; | ||
| 271 | } | ||
| 272 | }; | ||
| 273 | const p = async S.foo(); | ||
| 274 | // can't cancel in tests because they are non-async functions | ||
| 275 | expect(S.y == 2); | ||
| 276 | } | ||
| 277 | |||
| 278 | test "async fn pointer in a struct field" { | ||
| 279 | var data: i32 = 1; | ||
| 280 | const Foo = struct { | ||
| 281 | bar: async fn (*i32) void, | ||
| 282 | }; | ||
| 283 | var foo = Foo{ .bar = simpleAsyncFn2 }; | ||
| 284 | var bytes: [64]u8 = undefined; | ||
| 285 | const f = @asyncCall(&bytes, {}, foo.bar, &data); | ||
| 286 | comptime expect(@typeOf(f) == anyframe->void); | ||
| 287 | expect(data == 2); | ||
| 288 | resume f; | ||
| 289 | expect(data == 2); | ||
| 290 | _ = async doTheAwait(f); | ||
| 291 | expect(data == 4); | ||
| 292 | } | ||
| 293 | |||
| 294 | fn doTheAwait(f: anyframe->void) void { | ||
| 295 | await f; | ||
| 296 | } | ||
| 297 | |||
| 298 | async fn simpleAsyncFn2(y: *i32) void { | ||
| 299 | defer y.* += 2; | ||
| 300 | y.* += 1; | ||
| 301 | suspend; | ||
| 302 | } | ||
| 303 | |||
| 304 | test "@asyncCall with return type" { | ||
| 305 | const Foo = struct { | ||
| 306 | bar: async fn () i32, | ||
| 307 | |||
| 308 | var global_frame: anyframe = undefined; | ||
| 309 | |||
| 310 | async fn middle() i32 { | ||
| 311 | return afunc(); | ||
| 312 | } | ||
| 313 | |||
| 314 | fn afunc() i32 { | ||
| 315 | global_frame = @frame(); | ||
| 316 | suspend; | ||
| 317 | return 1234; | ||
| 318 | } | ||
| 319 | }; | ||
| 320 | var foo = Foo{ .bar = Foo.middle }; | ||
| 321 | var bytes: [150]u8 = undefined; | ||
| 322 | var aresult: i32 = 0; | ||
| 323 | _ = @asyncCall(&bytes, &aresult, foo.bar); | ||
| 324 | expect(aresult == 0); | ||
| 325 | resume Foo.global_frame; | ||
| 326 | expect(aresult == 1234); | ||
| 327 | } | ||
| 328 | |||
| 329 | test "async fn with inferred error set" { | ||
| 330 | const S = struct { | ||
| 331 | var global_frame: anyframe = undefined; | ||
| 332 | |||
| 333 | fn doTheTest() void { | ||
| 334 | var frame: [1]@Frame(middle) = undefined; | ||
| 335 | var result: anyerror!void = undefined; | ||
| 336 | _ = @asyncCall(@sliceToBytes(frame[0..]), &result, middle); | ||
| 337 | resume global_frame; | ||
| 338 | std.testing.expectError(error.Fail, result); | ||
| 339 | } | ||
| 340 | |||
| 341 | async fn middle() !void { | ||
| 342 | var f = async middle2(); | ||
| 343 | return await f; | ||
| 344 | } | ||
| 345 | |||
| 346 | fn middle2() !void { | ||
| 347 | return failing(); | ||
| 348 | } | ||
| 349 | |||
| 350 | fn failing() !void { | ||
| 351 | global_frame = @frame(); | ||
| 352 | suspend; | ||
| 353 | return error.Fail; | ||
| 354 | } | ||
| 355 | }; | ||
| 356 | S.doTheTest(); | ||
| 357 | } | ||
| 358 | |||
| 359 | test "error return trace across suspend points - early return" { | ||
| 360 | const p = nonFailing(); | ||
| 361 | resume p; | ||
| 362 | const p2 = async printTrace(p); | ||
| 363 | } | ||
| 364 | |||
| 365 | test "error return trace across suspend points - async return" { | ||
| 366 | const p = nonFailing(); | ||
| 367 | const p2 = async printTrace(p); | ||
| 368 | resume p; | ||
| 369 | } | ||
| 370 | |||
| 371 | fn nonFailing() (anyframe->anyerror!void) { | ||
| 372 | const Static = struct { | ||
| 373 | var frame: @Frame(suspendThenFail) = undefined; | ||
| 374 | }; | ||
| 375 | Static.frame = async suspendThenFail(); | ||
| 376 | return &Static.frame; | ||
| 377 | } | ||
| 378 | async fn suspendThenFail() anyerror!void { | ||
| 379 | suspend; | ||
| 380 | return error.Fail; | ||
| 381 | } | ||
| 382 | async fn printTrace(p: anyframe->(anyerror!void)) void { | ||
| 383 | (await p) catch |e| { | ||
| 384 | std.testing.expect(e == error.Fail); | ||
| 385 | if (@errorReturnTrace()) |trace| { | ||
| 386 | expect(trace.index == 1); | ||
| 387 | } else switch (builtin.mode) { | ||
| 388 | .Debug, .ReleaseSafe => @panic("expected return trace"), | ||
| 389 | .ReleaseFast, .ReleaseSmall => {}, | ||
| 390 | } | ||
| 391 | }; | ||
| 392 | } | ||
| 393 | |||
| 394 | test "break from suspend" { | ||
| 395 | var my_result: i32 = 1; | ||
| 396 | const p = async testBreakFromSuspend(&my_result); | ||
| 397 | // can't cancel here | ||
| 398 | std.testing.expect(my_result == 2); | ||
| 399 | } | ||
| 400 | async fn testBreakFromSuspend(my_result: *i32) void { | ||
| 401 | suspend { | ||
| 402 | resume @frame(); | ||
| 403 | } | ||
| 404 | my_result.* += 1; | ||
| 405 | suspend; | ||
| 406 | my_result.* += 1; | ||
| 407 | } | ||
| 408 | |||
| 409 | test "heap allocated async function frame" { | ||
| 410 | const S = struct { | ||
| 411 | var x: i32 = 42; | ||
| 412 | |||
| 413 | fn doTheTest() !void { | ||
| 414 | const frame = try std.heap.direct_allocator.create(@Frame(someFunc)); | ||
| 415 | defer std.heap.direct_allocator.destroy(frame); | ||
| 416 | |||
| 417 | expect(x == 42); | ||
| 418 | frame.* = async someFunc(); | ||
| 419 | expect(x == 43); | ||
| 420 | resume frame; | ||
| 421 | expect(x == 44); | ||
| 422 | } | ||
| 423 | |||
| 424 | fn someFunc() void { | ||
| 425 | x += 1; | ||
| 426 | suspend; | ||
| 427 | x += 1; | ||
| 428 | } | ||
| 429 | }; | ||
| 430 | try S.doTheTest(); | ||
| 431 | } | ||
| 432 | |||
| 433 | test "async function call return value" { | ||
| 434 | const S = struct { | ||
| 435 | var frame: anyframe = undefined; | ||
| 436 | var pt = Point{.x = 10, .y = 11 }; | ||
| 437 | |||
| 438 | fn doTheTest() void { | ||
| 439 | expectEqual(pt.x, 10); | ||
| 440 | expectEqual(pt.y, 11); | ||
| 441 | _ = async first(); | ||
| 442 | expectEqual(pt.x, 10); | ||
| 443 | expectEqual(pt.y, 11); | ||
| 444 | resume frame; | ||
| 445 | expectEqual(pt.x, 1); | ||
| 446 | expectEqual(pt.y, 2); | ||
| 447 | } | ||
| 448 | |||
| 449 | fn first() void { | ||
| 450 | pt = second(1, 2); | ||
| 451 | } | ||
| 452 | |||
| 453 | fn second(x: i32, y: i32) Point { | ||
| 454 | return other(x, y); | ||
| 455 | } | ||
| 456 | |||
| 457 | fn other(x: i32, y: i32) Point { | ||
| 458 | frame = @frame(); | ||
| 459 | suspend; | ||
| 460 | return Point{ | ||
| 461 | .x = x, | ||
| 462 | .y = y, | ||
| 463 | }; | ||
| 464 | } | ||
| 465 | |||
| 466 | const Point = struct { | ||
| 467 | x: i32, | ||
| 468 | y: i32, | ||
| 469 | }; | ||
| 470 | }; | ||
| 471 | S.doTheTest(); | ||
| 472 | } | ||
| 473 | |||
| 474 | test "suspension points inside branching control flow" { | ||
| 475 | const S = struct { | ||
| 476 | var result: i32 = 10; | ||
| 477 | |||
| 478 | fn doTheTest() void { | ||
| 479 | expect(10 == result); | ||
| 480 | var frame = async func(true); | ||
| 481 | expect(10 == result); | ||
| 482 | resume frame; | ||
| 483 | expect(11 == result); | ||
| 484 | resume frame; | ||
| 485 | expect(12 == result); | ||
| 486 | resume frame; | ||
| 487 | expect(13 == result); | ||
| 488 | } | ||
| 489 | |||
| 490 | fn func(b: bool) void { | ||
| 491 | while (b) { | ||
| 492 | suspend; | ||
| 493 | result += 1; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | }; | ||
| 497 | S.doTheTest(); | ||
| 498 | } | ||
| 499 | |||
| 500 | test "call async function which has struct return type" { | ||
| 501 | const S = struct { | ||
| 502 | var frame: anyframe = undefined; | ||
| 503 | |||
| 504 | fn doTheTest() void { | ||
| 505 | _ = async atest(); | ||
| 506 | resume frame; | ||
| 507 | } | ||
| 508 | |||
| 509 | fn atest() void { | ||
| 510 | const result = func(); | ||
| 511 | expect(result.x == 5); | ||
| 512 | expect(result.y == 6); | ||
| 513 | } | ||
| 514 | |||
| 515 | const Point = struct { | ||
| 516 | x: usize, | ||
| 517 | y: usize, | ||
| 518 | }; | ||
| 519 | |||
| 520 | fn func() Point { | ||
| 521 | suspend { | ||
| 522 | frame = @frame(); | ||
| 523 | } | ||
| 524 | return Point{ | ||
| 525 | .x = 5, | ||
| 526 | .y = 6, | ||
| 527 | }; | ||
| 528 | } | ||
| 529 | }; | ||
| 530 | S.doTheTest(); | ||
| 531 | } | ||
| 532 | |||
| 533 | test "errdefers in scope get run when canceling async fn call" { | ||
| 534 | const S = struct { | ||
| 535 | var frame: anyframe = undefined; | ||
| 536 | var x: u32 = 0; | ||
| 537 | |||
| 538 | fn doTheTest() void { | ||
| 539 | x = 9; | ||
| 540 | _ = async cancelIt(); | ||
| 541 | resume frame; | ||
| 542 | expect(x == 6); | ||
| 543 | |||
| 544 | x = 9; | ||
| 545 | _ = async awaitIt(); | ||
| 546 | resume frame; | ||
| 547 | expect(x == 11); | ||
| 548 | } | ||
| 549 | |||
| 550 | fn cancelIt() void { | ||
| 551 | var f = async func(); | ||
| 552 | cancel f; | ||
| 553 | } | ||
| 554 | |||
| 555 | fn awaitIt() void { | ||
| 556 | var f = async func(); | ||
| 557 | await f; | ||
| 558 | } | ||
| 559 | |||
| 560 | fn func() void { | ||
| 561 | defer x += 1; | ||
| 562 | errdefer x /= 2; | ||
| 563 | defer x += 1; | ||
| 564 | suspend { | ||
| 565 | frame = @frame(); | ||
| 566 | } | ||
| 567 | } | ||
| 568 | }; | ||
| 569 | S.doTheTest(); | ||
| 570 | } | ||
| 571 | |||
| 572 | test "pass string literal to async function" { | ||
| 573 | const S = struct { | ||
| 574 | var frame: anyframe = undefined; | ||
| 575 | var ok: bool = false; | ||
| 576 | |||
| 577 | fn doTheTest() void { | ||
| 578 | _ = async hello("hello"); | ||
| 579 | resume frame; | ||
| 580 | expect(ok); | ||
| 581 | } | ||
| 582 | |||
| 583 | fn hello(msg: []const u8) void { | ||
| 584 | frame = @frame(); | ||
| 585 | suspend; | ||
| 586 | expectEqual(([]const u8)("hello"), msg); | ||
| 587 | ok = true; | ||
| 588 | } | ||
| 589 | }; | ||
| 590 | S.doTheTest(); | ||
| 591 | } | ||
| 592 | |||
| 593 | test "cancel inside an errdefer" { | ||
| 594 | const S = struct { | ||
| 595 | var frame: anyframe = undefined; | ||
| 596 | |||
| 597 | fn doTheTest() void { | ||
| 598 | _ = async amainWrap(); | ||
| 599 | resume frame; | ||
| 600 | } | ||
| 601 | |||
| 602 | fn amainWrap() !void { | ||
| 603 | var foo = async func(); | ||
| 604 | errdefer cancel foo; | ||
| 605 | return error.Bad; | ||
| 606 | } | ||
| 607 | |||
| 608 | fn func() void { | ||
| 609 | frame = @frame(); | ||
| 610 | suspend; | ||
| 611 | } | ||
| 612 | |||
| 613 | }; | ||
| 614 | S.doTheTest(); | ||
| 615 | } | ||
| 616 | |||
| 617 | test "combining try with errdefer cancel" { | ||
| 618 | const S = struct { | ||
| 619 | var frame: anyframe = undefined; | ||
| 620 | var ok = false; | ||
| 621 | |||
| 622 | fn doTheTest() void { | ||
| 623 | _ = async amain(); | ||
| 624 | resume frame; | ||
| 625 | expect(ok); | ||
| 626 | } | ||
| 627 | |||
| 628 | fn amain() !void { | ||
| 629 | var f = async func("https://example.com/"); | ||
| 630 | errdefer cancel f; | ||
| 631 | |||
| 632 | _ = try await f; | ||
| 633 | } | ||
| 634 | |||
| 635 | fn func(url: []const u8) ![]u8 { | ||
| 636 | errdefer ok = true; | ||
| 637 | frame = @frame(); | ||
| 638 | suspend; | ||
| 639 | return error.Bad; | ||
| 640 | } | ||
| 641 | |||
| 642 | }; | ||
| 643 | S.doTheTest(); | ||
| 644 | } | ||
| 645 | |||
| 646 | test "try in an async function with error union and non-zero-bit payload" { | ||
| 647 | const S = struct { | ||
| 648 | var frame: anyframe = undefined; | ||
| 649 | var ok = false; | ||
| 650 | |||
| 651 | fn doTheTest() void { | ||
| 652 | _ = async amain(); | ||
| 653 | resume frame; | ||
| 654 | expect(ok); | ||
| 655 | } | ||
| 656 | |||
| 657 | fn amain() void { | ||
| 658 | std.testing.expectError(error.Bad, theProblem()); | ||
| 659 | ok = true; | ||
| 660 | } | ||
| 661 | |||
| 662 | fn theProblem() ![]u8 { | ||
| 663 | frame = @frame(); | ||
| 664 | suspend; | ||
| 665 | const result = try other(); | ||
| 666 | return result; | ||
| 667 | } | ||
| 668 | |||
| 669 | fn other() ![]u8 { | ||
| 670 | return error.Bad; | ||
| 671 | } | ||
| 672 | }; | ||
| 673 | S.doTheTest(); | ||
| 674 | } | ||
| 675 | |||
| 676 | test "returning a const error from async function" { | ||
| 677 | const S = struct { | ||
| 678 | var frame: anyframe = undefined; | ||
| 679 | var ok = false; | ||
| 680 | |||
| 681 | fn doTheTest() void { | ||
| 682 | _ = async amain(); | ||
| 683 | resume frame; | ||
| 684 | expect(ok); | ||
| 685 | } | ||
| 686 | |||
| 687 | fn amain() !void { | ||
| 688 | var download_frame = async fetchUrl(10, "a string"); | ||
| 689 | const download_text = try await download_frame; | ||
| 690 | |||
| 691 | @panic("should not get here"); | ||
| 692 | } | ||
| 693 | |||
| 694 | fn fetchUrl(unused: i32, url: []const u8) ![]u8 { | ||
| 695 | frame = @frame(); | ||
| 696 | suspend; | ||
| 697 | ok = true; | ||
| 698 | return error.OutOfMemory; | ||
| 699 | } | ||
| 700 | }; | ||
| 701 | S.doTheTest(); | ||
| 702 | } | ||
| 703 | |||
| 704 | test "async/await typical usage" { | ||
| 705 | inline for ([_]bool{false, true}) |b1| { | ||
| 706 | inline for ([_]bool{false, true}) |b2| { | ||
| 707 | testAsyncAwaitTypicalUsage(b1, b2).doTheTest(); | ||
| 708 | } | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 712 | fn testAsyncAwaitTypicalUsage(comptime simulate_fail_download: bool, comptime simulate_fail_file: bool) type { | ||
| 713 | return struct { | ||
| 714 | fn doTheTest() void { | ||
| 715 | _ = async amainWrap(); | ||
| 716 | resume global_file_frame; | ||
| 717 | resume global_download_frame; | ||
| 718 | } | ||
| 719 | fn amainWrap() void { | ||
| 720 | if (amain()) |_| { | ||
| 721 | expect(!simulate_fail_download); | ||
| 722 | expect(!simulate_fail_file); | ||
| 723 | } else |e| switch (e) { | ||
| 724 | error.NoResponse => expect(simulate_fail_download), | ||
| 725 | error.FileNotFound => expect(simulate_fail_file), | ||
| 726 | else => @panic("test failure"), | ||
| 727 | } | ||
| 728 | } | ||
| 729 | |||
| 730 | fn amain() !void { | ||
| 731 | const allocator = std.heap.direct_allocator; // TODO once we have the debug allocator, use that, so that this can detect leaks | ||
| 732 | var download_frame = async fetchUrl(allocator, "https://example.com/"); | ||
| 733 | errdefer cancel download_frame; | ||
| 734 | |||
| 735 | var file_frame = async readFile(allocator, "something.txt"); | ||
| 736 | errdefer cancel file_frame; | ||
| 737 | |||
| 738 | const download_text = try await download_frame; | ||
| 739 | defer allocator.free(download_text); | ||
| 740 | |||
| 741 | const file_text = try await file_frame; | ||
| 742 | defer allocator.free(file_text); | ||
| 743 | |||
| 744 | expect(std.mem.eql(u8, "expected download text", download_text)); | ||
| 745 | expect(std.mem.eql(u8, "expected file text", file_text)); | ||
| 746 | } | ||
| 747 | |||
| 748 | var global_download_frame: anyframe = undefined; | ||
| 749 | fn fetchUrl(allocator: *std.mem.Allocator, url: []const u8) anyerror![]u8 { | ||
| 750 | global_download_frame = @frame(); | ||
| 751 | const result = try std.mem.dupe(allocator, u8, "expected download text"); | ||
| 752 | errdefer allocator.free(result); | ||
| 753 | suspend; | ||
| 754 | if (simulate_fail_download) return error.NoResponse; | ||
| 755 | return result; | ||
| 756 | } | ||
| 757 | |||
| 758 | var global_file_frame: anyframe = undefined; | ||
| 759 | fn readFile(allocator: *std.mem.Allocator, filename: []const u8) anyerror![]u8 { | ||
| 760 | global_file_frame = @frame(); | ||
| 761 | const result = try std.mem.dupe(allocator, u8, "expected file text"); | ||
| 762 | errdefer allocator.free(result); | ||
| 763 | suspend; | ||
| 764 | if (simulate_fail_file) return error.FileNotFound; | ||
| 765 | return result; | ||
| 766 | } | ||
| 767 | }; | ||
| 768 | } | ||
| 769 | |||
| 770 | test "alignment of local variables in async functions" { | ||
| 771 | const S = struct { | ||
| 772 | fn doTheTest() void { | ||
| 773 | var y: u8 = 123; | ||
| 774 | var x: u8 align(128) = 1; | ||
| 775 | expect(@ptrToInt(&x) % 128 == 0); | ||
| 776 | } | ||
| 777 | }; | ||
| 778 | S.doTheTest(); | ||
| 779 | } | ||
test/stage1/behavior/await_struct.zig created+44| ... | @@ -0,0 +1,44 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const expect = std.testing.expect; | ||
| 4 | |||
| 5 | const Foo = struct { | ||
| 6 | x: i32, | ||
| 7 | }; | ||
| 8 | |||
| 9 | var await_a_promise: anyframe = undefined; | ||
| 10 | var await_final_result = Foo{ .x = 0 }; | ||
| 11 | |||
| 12 | test "coroutine await struct" { | ||
| 13 | await_seq('a'); | ||
| 14 | const p = async await_amain(); | ||
| 15 | await_seq('f'); | ||
| 16 | resume await_a_promise; | ||
| 17 | await_seq('i'); | ||
| 18 | expect(await_final_result.x == 1234); | ||
| 19 | expect(std.mem.eql(u8, await_points, "abcdefghi")); | ||
| 20 | } | ||
| 21 | async fn await_amain() void { | ||
| 22 | await_seq('b'); | ||
| 23 | const p = async await_another(); | ||
| 24 | await_seq('e'); | ||
| 25 | await_final_result = await p; | ||
| 26 | await_seq('h'); | ||
| 27 | } | ||
| 28 | async fn await_another() Foo { | ||
| 29 | await_seq('c'); | ||
| 30 | suspend { | ||
| 31 | await_seq('d'); | ||
| 32 | await_a_promise = @frame(); | ||
| 33 | } | ||
| 34 | await_seq('g'); | ||
| 35 | return Foo{ .x = 1234 }; | ||
| 36 | } | ||
| 37 | |||
| 38 | var await_points = [_]u8{0} ** "abcdefghi".len; | ||
| 39 | var await_seq_index: usize = 0; | ||
| 40 | |||
| 41 | fn await_seq(c: u8) void { | ||
| 42 | await_points[await_seq_index] = c; | ||
| 43 | await_seq_index += 1; | ||
| 44 | } | ||
test/stage1/behavior/coroutine_await_struct.zig deleted-44| ... | @@ -1,44 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const expect = std.testing.expect; | ||
| 4 | |||
| 5 | const Foo = struct { | ||
| 6 | x: i32, | ||
| 7 | }; | ||
| 8 | |||
| 9 | var await_a_promise: anyframe = undefined; | ||
| 10 | var await_final_result = Foo{ .x = 0 }; | ||
| 11 | |||
| 12 | test "coroutine await struct" { | ||
| 13 | await_seq('a'); | ||
| 14 | const p = async await_amain(); | ||
| 15 | await_seq('f'); | ||
| 16 | resume await_a_promise; | ||
| 17 | await_seq('i'); | ||
| 18 | expect(await_final_result.x == 1234); | ||
| 19 | expect(std.mem.eql(u8, await_points, "abcdefghi")); | ||
| 20 | } | ||
| 21 | async fn await_amain() void { | ||
| 22 | await_seq('b'); | ||
| 23 | const p = async await_another(); | ||
| 24 | await_seq('e'); | ||
| 25 | await_final_result = await p; | ||
| 26 | await_seq('h'); | ||
| 27 | } | ||
| 28 | async fn await_another() Foo { | ||
| 29 | await_seq('c'); | ||
| 30 | suspend { | ||
| 31 | await_seq('d'); | ||
| 32 | await_a_promise = @frame(); | ||
| 33 | } | ||
| 34 | await_seq('g'); | ||
| 35 | return Foo{ .x = 1234 }; | ||
| 36 | } | ||
| 37 | |||
| 38 | var await_points = [_]u8{0} ** "abcdefghi".len; | ||
| 39 | var await_seq_index: usize = 0; | ||
| 40 | |||
| 41 | fn await_seq(c: u8) void { | ||
| 42 | await_points[await_seq_index] = c; | ||
| 43 | await_seq_index += 1; | ||
| 44 | } | ||
test/stage1/behavior/coroutines.zig deleted-779| ... | @@ -1,779 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const expect = std.testing.expect; | ||
| 4 | const expectEqual = std.testing.expectEqual; | ||
| 5 | |||
| 6 | var global_x: i32 = 1; | ||
| 7 | |||
| 8 | test "simple coroutine suspend and resume" { | ||
| 9 | const frame = async simpleAsyncFn(); | ||
| 10 | expect(global_x == 2); | ||
| 11 | resume frame; | ||
| 12 | expect(global_x == 3); | ||
| 13 | const af: anyframe->void = &frame; | ||
| 14 | resume frame; | ||
| 15 | expect(global_x == 4); | ||
| 16 | } | ||
| 17 | fn simpleAsyncFn() void { | ||
| 18 | global_x += 1; | ||
| 19 | suspend; | ||
| 20 | global_x += 1; | ||
| 21 | suspend; | ||
| 22 | global_x += 1; | ||
| 23 | } | ||
| 24 | |||
| 25 | var global_y: i32 = 1; | ||
| 26 | |||
| 27 | test "pass parameter to coroutine" { | ||
| 28 | const p = async simpleAsyncFnWithArg(2); | ||
| 29 | expect(global_y == 3); | ||
| 30 | resume p; | ||
| 31 | expect(global_y == 5); | ||
| 32 | } | ||
| 33 | fn simpleAsyncFnWithArg(delta: i32) void { | ||
| 34 | global_y += delta; | ||
| 35 | suspend; | ||
| 36 | global_y += delta; | ||
| 37 | } | ||
| 38 | |||
| 39 | test "suspend at end of function" { | ||
| 40 | const S = struct { | ||
| 41 | var x: i32 = 1; | ||
| 42 | |||
| 43 | fn doTheTest() void { | ||
| 44 | expect(x == 1); | ||
| 45 | const p = async suspendAtEnd(); | ||
| 46 | expect(x == 2); | ||
| 47 | } | ||
| 48 | |||
| 49 | fn suspendAtEnd() void { | ||
| 50 | x += 1; | ||
| 51 | suspend; | ||
| 52 | } | ||
| 53 | }; | ||
| 54 | S.doTheTest(); | ||
| 55 | } | ||
| 56 | |||
| 57 | test "local variable in async function" { | ||
| 58 | const S = struct { | ||
| 59 | var x: i32 = 0; | ||
| 60 | |||
| 61 | fn doTheTest() void { | ||
| 62 | expect(x == 0); | ||
| 63 | const p = async add(1, 2); | ||
| 64 | expect(x == 0); | ||
| 65 | resume p; | ||
| 66 | expect(x == 0); | ||
| 67 | resume p; | ||
| 68 | expect(x == 0); | ||
| 69 | resume p; | ||
| 70 | expect(x == 3); | ||
| 71 | } | ||
| 72 | |||
| 73 | fn add(a: i32, b: i32) void { | ||
| 74 | var accum: i32 = 0; | ||
| 75 | suspend; | ||
| 76 | accum += a; | ||
| 77 | suspend; | ||
| 78 | accum += b; | ||
| 79 | suspend; | ||
| 80 | x = accum; | ||
| 81 | } | ||
| 82 | }; | ||
| 83 | S.doTheTest(); | ||
| 84 | } | ||
| 85 | |||
| 86 | test "calling an inferred async function" { | ||
| 87 | const S = struct { | ||
| 88 | var x: i32 = 1; | ||
| 89 | var other_frame: *@Frame(other) = undefined; | ||
| 90 | |||
| 91 | fn doTheTest() void { | ||
| 92 | _ = async first(); | ||
| 93 | expect(x == 1); | ||
| 94 | resume other_frame.*; | ||
| 95 | expect(x == 2); | ||
| 96 | } | ||
| 97 | |||
| 98 | fn first() void { | ||
| 99 | other(); | ||
| 100 | } | ||
| 101 | fn other() void { | ||
| 102 | other_frame = @frame(); | ||
| 103 | suspend; | ||
| 104 | x += 1; | ||
| 105 | } | ||
| 106 | }; | ||
| 107 | S.doTheTest(); | ||
| 108 | } | ||
| 109 | |||
| 110 | test "@frameSize" { | ||
| 111 | const S = struct { | ||
| 112 | fn doTheTest() void { | ||
| 113 | { | ||
| 114 | var ptr = @ptrCast(async fn(i32) void, other); | ||
| 115 | const size = @frameSize(ptr); | ||
| 116 | expect(size == @sizeOf(@Frame(other))); | ||
| 117 | } | ||
| 118 | { | ||
| 119 | var ptr = @ptrCast(async fn() void, first); | ||
| 120 | const size = @frameSize(ptr); | ||
| 121 | expect(size == @sizeOf(@Frame(first))); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | fn first() void { | ||
| 126 | other(1); | ||
| 127 | } | ||
| 128 | fn other(param: i32) void { | ||
| 129 | var local: i32 = undefined; | ||
| 130 | suspend; | ||
| 131 | } | ||
| 132 | }; | ||
| 133 | S.doTheTest(); | ||
| 134 | } | ||
| 135 | |||
| 136 | test "coroutine suspend, resume" { | ||
| 137 | const S = struct { | ||
| 138 | var frame: anyframe = undefined; | ||
| 139 | |||
| 140 | fn doTheTest() void { | ||
| 141 | _ = async amain(); | ||
| 142 | seq('d'); | ||
| 143 | resume frame; | ||
| 144 | seq('h'); | ||
| 145 | |||
| 146 | expect(std.mem.eql(u8, points, "abcdefgh")); | ||
| 147 | } | ||
| 148 | |||
| 149 | fn amain() void { | ||
| 150 | seq('a'); | ||
| 151 | var f = async testAsyncSeq(); | ||
| 152 | seq('c'); | ||
| 153 | cancel f; | ||
| 154 | seq('g'); | ||
| 155 | } | ||
| 156 | |||
| 157 | fn testAsyncSeq() void { | ||
| 158 | defer seq('f'); | ||
| 159 | |||
| 160 | seq('b'); | ||
| 161 | suspend { | ||
| 162 | frame = @frame(); | ||
| 163 | } | ||
| 164 | seq('e'); | ||
| 165 | } | ||
| 166 | var points = [_]u8{'x'} ** "abcdefgh".len; | ||
| 167 | var index: usize = 0; | ||
| 168 | |||
| 169 | fn seq(c: u8) void { | ||
| 170 | points[index] = c; | ||
| 171 | index += 1; | ||
| 172 | } | ||
| 173 | }; | ||
| 174 | S.doTheTest(); | ||
| 175 | } | ||
| 176 | |||
| 177 | test "coroutine suspend with block" { | ||
| 178 | const p = async testSuspendBlock(); | ||
| 179 | expect(!global_result); | ||
| 180 | resume a_promise; | ||
| 181 | expect(global_result); | ||
| 182 | } | ||
| 183 | |||
| 184 | var a_promise: anyframe = undefined; | ||
| 185 | var global_result = false; | ||
| 186 | async fn testSuspendBlock() void { | ||
| 187 | suspend { | ||
| 188 | comptime expect(@typeOf(@frame()) == *@Frame(testSuspendBlock)); | ||
| 189 | a_promise = @frame(); | ||
| 190 | } | ||
| 191 | |||
| 192 | // Test to make sure that @frame() works as advertised (issue #1296) | ||
| 193 | // var our_handle: anyframe = @frame(); | ||
| 194 | expect(a_promise == anyframe(@frame())); | ||
| 195 | |||
| 196 | global_result = true; | ||
| 197 | } | ||
| 198 | |||
| 199 | var await_a_promise: anyframe = undefined; | ||
| 200 | var await_final_result: i32 = 0; | ||
| 201 | |||
| 202 | test "coroutine await" { | ||
| 203 | await_seq('a'); | ||
| 204 | const p = async await_amain(); | ||
| 205 | await_seq('f'); | ||
| 206 | resume await_a_promise; | ||
| 207 | await_seq('i'); | ||
| 208 | expect(await_final_result == 1234); | ||
| 209 | expect(std.mem.eql(u8, await_points, "abcdefghi")); | ||
| 210 | } | ||
| 211 | async fn await_amain() void { | ||
| 212 | await_seq('b'); | ||
| 213 | const p = async await_another(); | ||
| 214 | await_seq('e'); | ||
| 215 | await_final_result = await p; | ||
| 216 | await_seq('h'); | ||
| 217 | } | ||
| 218 | async fn await_another() i32 { | ||
| 219 | await_seq('c'); | ||
| 220 | suspend { | ||
| 221 | await_seq('d'); | ||
| 222 | await_a_promise = @frame(); | ||
| 223 | } | ||
| 224 | await_seq('g'); | ||
| 225 | return 1234; | ||
| 226 | } | ||
| 227 | |||
| 228 | var await_points = [_]u8{0} ** "abcdefghi".len; | ||
| 229 | var await_seq_index: usize = 0; | ||
| 230 | |||
| 231 | fn await_seq(c: u8) void { | ||
| 232 | await_points[await_seq_index] = c; | ||
| 233 | await_seq_index += 1; | ||
| 234 | } | ||
| 235 | |||
| 236 | var early_final_result: i32 = 0; | ||
| 237 | |||
| 238 | test "coroutine await early return" { | ||
| 239 | early_seq('a'); | ||
| 240 | const p = async early_amain(); | ||
| 241 | early_seq('f'); | ||
| 242 | expect(early_final_result == 1234); | ||
| 243 | expect(std.mem.eql(u8, early_points, "abcdef")); | ||
| 244 | } | ||
| 245 | async fn early_amain() void { | ||
| 246 | early_seq('b'); | ||
| 247 | const p = async early_another(); | ||
| 248 | early_seq('d'); | ||
| 249 | early_final_result = await p; | ||
| 250 | early_seq('e'); | ||
| 251 | } | ||
| 252 | async fn early_another() i32 { | ||
| 253 | early_seq('c'); | ||
| 254 | return 1234; | ||
| 255 | } | ||
| 256 | |||
| 257 | var early_points = [_]u8{0} ** "abcdef".len; | ||
| 258 | var early_seq_index: usize = 0; | ||
| 259 | |||
| 260 | fn early_seq(c: u8) void { | ||
| 261 | early_points[early_seq_index] = c; | ||
| 262 | early_seq_index += 1; | ||
| 263 | } | ||
| 264 | |||
| 265 | test "async function with dot syntax" { | ||
| 266 | const S = struct { | ||
| 267 | var y: i32 = 1; | ||
| 268 | async fn foo() void { | ||
| 269 | y += 1; | ||
| 270 | suspend; | ||
| 271 | } | ||
| 272 | }; | ||
| 273 | const p = async S.foo(); | ||
| 274 | // can't cancel in tests because they are non-async functions | ||
| 275 | expect(S.y == 2); | ||
| 276 | } | ||
| 277 | |||
| 278 | test "async fn pointer in a struct field" { | ||
| 279 | var data: i32 = 1; | ||
| 280 | const Foo = struct { | ||
| 281 | bar: async fn (*i32) void, | ||
| 282 | }; | ||
| 283 | var foo = Foo{ .bar = simpleAsyncFn2 }; | ||
| 284 | var bytes: [64]u8 = undefined; | ||
| 285 | const f = @asyncCall(&bytes, {}, foo.bar, &data); | ||
| 286 | comptime expect(@typeOf(f) == anyframe->void); | ||
| 287 | expect(data == 2); | ||
| 288 | resume f; | ||
| 289 | expect(data == 2); | ||
| 290 | _ = async doTheAwait(f); | ||
| 291 | expect(data == 4); | ||
| 292 | } | ||
| 293 | |||
| 294 | fn doTheAwait(f: anyframe->void) void { | ||
| 295 | await f; | ||
| 296 | } | ||
| 297 | |||
| 298 | async fn simpleAsyncFn2(y: *i32) void { | ||
| 299 | defer y.* += 2; | ||
| 300 | y.* += 1; | ||
| 301 | suspend; | ||
| 302 | } | ||
| 303 | |||
| 304 | test "@asyncCall with return type" { | ||
| 305 | const Foo = struct { | ||
| 306 | bar: async fn () i32, | ||
| 307 | |||
| 308 | var global_frame: anyframe = undefined; | ||
| 309 | |||
| 310 | async fn middle() i32 { | ||
| 311 | return afunc(); | ||
| 312 | } | ||
| 313 | |||
| 314 | fn afunc() i32 { | ||
| 315 | global_frame = @frame(); | ||
| 316 | suspend; | ||
| 317 | return 1234; | ||
| 318 | } | ||
| 319 | }; | ||
| 320 | var foo = Foo{ .bar = Foo.middle }; | ||
| 321 | var bytes: [150]u8 = undefined; | ||
| 322 | var aresult: i32 = 0; | ||
| 323 | _ = @asyncCall(&bytes, &aresult, foo.bar); | ||
| 324 | expect(aresult == 0); | ||
| 325 | resume Foo.global_frame; | ||
| 326 | expect(aresult == 1234); | ||
| 327 | } | ||
| 328 | |||
| 329 | test "async fn with inferred error set" { | ||
| 330 | const S = struct { | ||
| 331 | var global_frame: anyframe = undefined; | ||
| 332 | |||
| 333 | fn doTheTest() void { | ||
| 334 | var frame: [1]@Frame(middle) = undefined; | ||
| 335 | var result: anyerror!void = undefined; | ||
| 336 | _ = @asyncCall(@sliceToBytes(frame[0..]), &result, middle); | ||
| 337 | resume global_frame; | ||
| 338 | std.testing.expectError(error.Fail, result); | ||
| 339 | } | ||
| 340 | |||
| 341 | async fn middle() !void { | ||
| 342 | var f = async middle2(); | ||
| 343 | return await f; | ||
| 344 | } | ||
| 345 | |||
| 346 | fn middle2() !void { | ||
| 347 | return failing(); | ||
| 348 | } | ||
| 349 | |||
| 350 | fn failing() !void { | ||
| 351 | global_frame = @frame(); | ||
| 352 | suspend; | ||
| 353 | return error.Fail; | ||
| 354 | } | ||
| 355 | }; | ||
| 356 | S.doTheTest(); | ||
| 357 | } | ||
| 358 | |||
| 359 | test "error return trace across suspend points - early return" { | ||
| 360 | const p = nonFailing(); | ||
| 361 | resume p; | ||
| 362 | const p2 = async printTrace(p); | ||
| 363 | } | ||
| 364 | |||
| 365 | test "error return trace across suspend points - async return" { | ||
| 366 | const p = nonFailing(); | ||
| 367 | const p2 = async printTrace(p); | ||
| 368 | resume p; | ||
| 369 | } | ||
| 370 | |||
| 371 | fn nonFailing() (anyframe->anyerror!void) { | ||
| 372 | const Static = struct { | ||
| 373 | var frame: @Frame(suspendThenFail) = undefined; | ||
| 374 | }; | ||
| 375 | Static.frame = async suspendThenFail(); | ||
| 376 | return &Static.frame; | ||
| 377 | } | ||
| 378 | async fn suspendThenFail() anyerror!void { | ||
| 379 | suspend; | ||
| 380 | return error.Fail; | ||
| 381 | } | ||
| 382 | async fn printTrace(p: anyframe->(anyerror!void)) void { | ||
| 383 | (await p) catch |e| { | ||
| 384 | std.testing.expect(e == error.Fail); | ||
| 385 | if (@errorReturnTrace()) |trace| { | ||
| 386 | expect(trace.index == 1); | ||
| 387 | } else switch (builtin.mode) { | ||
| 388 | .Debug, .ReleaseSafe => @panic("expected return trace"), | ||
| 389 | .ReleaseFast, .ReleaseSmall => {}, | ||
| 390 | } | ||
| 391 | }; | ||
| 392 | } | ||
| 393 | |||
| 394 | test "break from suspend" { | ||
| 395 | var my_result: i32 = 1; | ||
| 396 | const p = async testBreakFromSuspend(&my_result); | ||
| 397 | // can't cancel here | ||
| 398 | std.testing.expect(my_result == 2); | ||
| 399 | } | ||
| 400 | async fn testBreakFromSuspend(my_result: *i32) void { | ||
| 401 | suspend { | ||
| 402 | resume @frame(); | ||
| 403 | } | ||
| 404 | my_result.* += 1; | ||
| 405 | suspend; | ||
| 406 | my_result.* += 1; | ||
| 407 | } | ||
| 408 | |||
| 409 | test "heap allocated async function frame" { | ||
| 410 | const S = struct { | ||
| 411 | var x: i32 = 42; | ||
| 412 | |||
| 413 | fn doTheTest() !void { | ||
| 414 | const frame = try std.heap.direct_allocator.create(@Frame(someFunc)); | ||
| 415 | defer std.heap.direct_allocator.destroy(frame); | ||
| 416 | |||
| 417 | expect(x == 42); | ||
| 418 | frame.* = async someFunc(); | ||
| 419 | expect(x == 43); | ||
| 420 | resume frame; | ||
| 421 | expect(x == 44); | ||
| 422 | } | ||
| 423 | |||
| 424 | fn someFunc() void { | ||
| 425 | x += 1; | ||
| 426 | suspend; | ||
| 427 | x += 1; | ||
| 428 | } | ||
| 429 | }; | ||
| 430 | try S.doTheTest(); | ||
| 431 | } | ||
| 432 | |||
| 433 | test "async function call return value" { | ||
| 434 | const S = struct { | ||
| 435 | var frame: anyframe = undefined; | ||
| 436 | var pt = Point{.x = 10, .y = 11 }; | ||
| 437 | |||
| 438 | fn doTheTest() void { | ||
| 439 | expectEqual(pt.x, 10); | ||
| 440 | expectEqual(pt.y, 11); | ||
| 441 | _ = async first(); | ||
| 442 | expectEqual(pt.x, 10); | ||
| 443 | expectEqual(pt.y, 11); | ||
| 444 | resume frame; | ||
| 445 | expectEqual(pt.x, 1); | ||
| 446 | expectEqual(pt.y, 2); | ||
| 447 | } | ||
| 448 | |||
| 449 | fn first() void { | ||
| 450 | pt = second(1, 2); | ||
| 451 | } | ||
| 452 | |||
| 453 | fn second(x: i32, y: i32) Point { | ||
| 454 | return other(x, y); | ||
| 455 | } | ||
| 456 | |||
| 457 | fn other(x: i32, y: i32) Point { | ||
| 458 | frame = @frame(); | ||
| 459 | suspend; | ||
| 460 | return Point{ | ||
| 461 | .x = x, | ||
| 462 | .y = y, | ||
| 463 | }; | ||
| 464 | } | ||
| 465 | |||
| 466 | const Point = struct { | ||
| 467 | x: i32, | ||
| 468 | y: i32, | ||
| 469 | }; | ||
| 470 | }; | ||
| 471 | S.doTheTest(); | ||
| 472 | } | ||
| 473 | |||
| 474 | test "suspension points inside branching control flow" { | ||
| 475 | const S = struct { | ||
| 476 | var result: i32 = 10; | ||
| 477 | |||
| 478 | fn doTheTest() void { | ||
| 479 | expect(10 == result); | ||
| 480 | var frame = async func(true); | ||
| 481 | expect(10 == result); | ||
| 482 | resume frame; | ||
| 483 | expect(11 == result); | ||
| 484 | resume frame; | ||
| 485 | expect(12 == result); | ||
| 486 | resume frame; | ||
| 487 | expect(13 == result); | ||
| 488 | } | ||
| 489 | |||
| 490 | fn func(b: bool) void { | ||
| 491 | while (b) { | ||
| 492 | suspend; | ||
| 493 | result += 1; | ||
| 494 | } | ||
| 495 | } | ||
| 496 | }; | ||
| 497 | S.doTheTest(); | ||
| 498 | } | ||
| 499 | |||
| 500 | test "call async function which has struct return type" { | ||
| 501 | const S = struct { | ||
| 502 | var frame: anyframe = undefined; | ||
| 503 | |||
| 504 | fn doTheTest() void { | ||
| 505 | _ = async atest(); | ||
| 506 | resume frame; | ||
| 507 | } | ||
| 508 | |||
| 509 | fn atest() void { | ||
| 510 | const result = func(); | ||
| 511 | expect(result.x == 5); | ||
| 512 | expect(result.y == 6); | ||
| 513 | } | ||
| 514 | |||
| 515 | const Point = struct { | ||
| 516 | x: usize, | ||
| 517 | y: usize, | ||
| 518 | }; | ||
| 519 | |||
| 520 | fn func() Point { | ||
| 521 | suspend { | ||
| 522 | frame = @frame(); | ||
| 523 | } | ||
| 524 | return Point{ | ||
| 525 | .x = 5, | ||
| 526 | .y = 6, | ||
| 527 | }; | ||
| 528 | } | ||
| 529 | }; | ||
| 530 | S.doTheTest(); | ||
| 531 | } | ||
| 532 | |||
| 533 | test "errdefers in scope get run when canceling async fn call" { | ||
| 534 | const S = struct { | ||
| 535 | var frame: anyframe = undefined; | ||
| 536 | var x: u32 = 0; | ||
| 537 | |||
| 538 | fn doTheTest() void { | ||
| 539 | x = 9; | ||
| 540 | _ = async cancelIt(); | ||
| 541 | resume frame; | ||
| 542 | expect(x == 6); | ||
| 543 | |||
| 544 | x = 9; | ||
| 545 | _ = async awaitIt(); | ||
| 546 | resume frame; | ||
| 547 | expect(x == 11); | ||
| 548 | } | ||
| 549 | |||
| 550 | fn cancelIt() void { | ||
| 551 | var f = async func(); | ||
| 552 | cancel f; | ||
| 553 | } | ||
| 554 | |||
| 555 | fn awaitIt() void { | ||
| 556 | var f = async func(); | ||
| 557 | await f; | ||
| 558 | } | ||
| 559 | |||
| 560 | fn func() void { | ||
| 561 | defer x += 1; | ||
| 562 | errdefer x /= 2; | ||
| 563 | defer x += 1; | ||
| 564 | suspend { | ||
| 565 | frame = @frame(); | ||
| 566 | } | ||
| 567 | } | ||
| 568 | }; | ||
| 569 | S.doTheTest(); | ||
| 570 | } | ||
| 571 | |||
| 572 | test "pass string literal to async function" { | ||
| 573 | const S = struct { | ||
| 574 | var frame: anyframe = undefined; | ||
| 575 | var ok: bool = false; | ||
| 576 | |||
| 577 | fn doTheTest() void { | ||
| 578 | _ = async hello("hello"); | ||
| 579 | resume frame; | ||
| 580 | expect(ok); | ||
| 581 | } | ||
| 582 | |||
| 583 | fn hello(msg: []const u8) void { | ||
| 584 | frame = @frame(); | ||
| 585 | suspend; | ||
| 586 | expectEqual(([]const u8)("hello"), msg); | ||
| 587 | ok = true; | ||
| 588 | } | ||
| 589 | }; | ||
| 590 | S.doTheTest(); | ||
| 591 | } | ||
| 592 | |||
| 593 | test "cancel inside an errdefer" { | ||
| 594 | const S = struct { | ||
| 595 | var frame: anyframe = undefined; | ||
| 596 | |||
| 597 | fn doTheTest() void { | ||
| 598 | _ = async amainWrap(); | ||
| 599 | resume frame; | ||
| 600 | } | ||
| 601 | |||
| 602 | fn amainWrap() !void { | ||
| 603 | var foo = async func(); | ||
| 604 | errdefer cancel foo; | ||
| 605 | return error.Bad; | ||
| 606 | } | ||
| 607 | |||
| 608 | fn func() void { | ||
| 609 | frame = @frame(); | ||
| 610 | suspend; | ||
| 611 | } | ||
| 612 | |||
| 613 | }; | ||
| 614 | S.doTheTest(); | ||
| 615 | } | ||
| 616 | |||
| 617 | test "combining try with errdefer cancel" { | ||
| 618 | const S = struct { | ||
| 619 | var frame: anyframe = undefined; | ||
| 620 | var ok = false; | ||
| 621 | |||
| 622 | fn doTheTest() void { | ||
| 623 | _ = async amain(); | ||
| 624 | resume frame; | ||
| 625 | expect(ok); | ||
| 626 | } | ||
| 627 | |||
| 628 | fn amain() !void { | ||
| 629 | var f = async func("https://example.com/"); | ||
| 630 | errdefer cancel f; | ||
| 631 | |||
| 632 | _ = try await f; | ||
| 633 | } | ||
| 634 | |||
| 635 | fn func(url: []const u8) ![]u8 { | ||
| 636 | errdefer ok = true; | ||
| 637 | frame = @frame(); | ||
| 638 | suspend; | ||
| 639 | return error.Bad; | ||
| 640 | } | ||
| 641 | |||
| 642 | }; | ||
| 643 | S.doTheTest(); | ||
| 644 | } | ||
| 645 | |||
| 646 | test "try in an async function with error union and non-zero-bit payload" { | ||
| 647 | const S = struct { | ||
| 648 | var frame: anyframe = undefined; | ||
| 649 | var ok = false; | ||
| 650 | |||
| 651 | fn doTheTest() void { | ||
| 652 | _ = async amain(); | ||
| 653 | resume frame; | ||
| 654 | expect(ok); | ||
| 655 | } | ||
| 656 | |||
| 657 | fn amain() void { | ||
| 658 | std.testing.expectError(error.Bad, theProblem()); | ||
| 659 | ok = true; | ||
| 660 | } | ||
| 661 | |||
| 662 | fn theProblem() ![]u8 { | ||
| 663 | frame = @frame(); | ||
| 664 | suspend; | ||
| 665 | const result = try other(); | ||
| 666 | return result; | ||
| 667 | } | ||
| 668 | |||
| 669 | fn other() ![]u8 { | ||
| 670 | return error.Bad; | ||
| 671 | } | ||
| 672 | }; | ||
| 673 | S.doTheTest(); | ||
| 674 | } | ||
| 675 | |||
| 676 | test "returning a const error from async function" { | ||
| 677 | const S = struct { | ||
| 678 | var frame: anyframe = undefined; | ||
| 679 | var ok = false; | ||
| 680 | |||
| 681 | fn doTheTest() void { | ||
| 682 | _ = async amain(); | ||
| 683 | resume frame; | ||
| 684 | expect(ok); | ||
| 685 | } | ||
| 686 | |||
| 687 | fn amain() !void { | ||
| 688 | var download_frame = async fetchUrl(10, "a string"); | ||
| 689 | const download_text = try await download_frame; | ||
| 690 | |||
| 691 | @panic("should not get here"); | ||
| 692 | } | ||
| 693 | |||
| 694 | fn fetchUrl(unused: i32, url: []const u8) ![]u8 { | ||
| 695 | frame = @frame(); | ||
| 696 | suspend; | ||
| 697 | ok = true; | ||
| 698 | return error.OutOfMemory; | ||
| 699 | } | ||
| 700 | }; | ||
| 701 | S.doTheTest(); | ||
| 702 | } | ||
| 703 | |||
| 704 | test "async/await typical usage" { | ||
| 705 | inline for ([_]bool{false, true}) |b1| { | ||
| 706 | inline for ([_]bool{false, true}) |b2| { | ||
| 707 | testAsyncAwaitTypicalUsage(b1, b2).doTheTest(); | ||
| 708 | } | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 712 | fn testAsyncAwaitTypicalUsage(comptime simulate_fail_download: bool, comptime simulate_fail_file: bool) type { | ||
| 713 | return struct { | ||
| 714 | fn doTheTest() void { | ||
| 715 | _ = async amainWrap(); | ||
| 716 | resume global_file_frame; | ||
| 717 | resume global_download_frame; | ||
| 718 | } | ||
| 719 | fn amainWrap() void { | ||
| 720 | if (amain()) |_| { | ||
| 721 | expect(!simulate_fail_download); | ||
| 722 | expect(!simulate_fail_file); | ||
| 723 | } else |e| switch (e) { | ||
| 724 | error.NoResponse => expect(simulate_fail_download), | ||
| 725 | error.FileNotFound => expect(simulate_fail_file), | ||
| 726 | else => @panic("test failure"), | ||
| 727 | } | ||
| 728 | } | ||
| 729 | |||
| 730 | fn amain() !void { | ||
| 731 | const allocator = std.heap.direct_allocator; // TODO once we have the debug allocator, use that, so that this can detect leaks | ||
| 732 | var download_frame = async fetchUrl(allocator, "https://example.com/"); | ||
| 733 | errdefer cancel download_frame; | ||
| 734 | |||
| 735 | var file_frame = async readFile(allocator, "something.txt"); | ||
| 736 | errdefer cancel file_frame; | ||
| 737 | |||
| 738 | const download_text = try await download_frame; | ||
| 739 | defer allocator.free(download_text); | ||
| 740 | |||
| 741 | const file_text = try await file_frame; | ||
| 742 | defer allocator.free(file_text); | ||
| 743 | |||
| 744 | expect(std.mem.eql(u8, "expected download text", download_text)); | ||
| 745 | expect(std.mem.eql(u8, "expected file text", file_text)); | ||
| 746 | } | ||
| 747 | |||
| 748 | var global_download_frame: anyframe = undefined; | ||
| 749 | fn fetchUrl(allocator: *std.mem.Allocator, url: []const u8) anyerror![]u8 { | ||
| 750 | global_download_frame = @frame(); | ||
| 751 | const result = try std.mem.dupe(allocator, u8, "expected download text"); | ||
| 752 | errdefer allocator.free(result); | ||
| 753 | suspend; | ||
| 754 | if (simulate_fail_download) return error.NoResponse; | ||
| 755 | return result; | ||
| 756 | } | ||
| 757 | |||
| 758 | var global_file_frame: anyframe = undefined; | ||
| 759 | fn readFile(allocator: *std.mem.Allocator, filename: []const u8) anyerror![]u8 { | ||
| 760 | global_file_frame = @frame(); | ||
| 761 | const result = try std.mem.dupe(allocator, u8, "expected file text"); | ||
| 762 | errdefer allocator.free(result); | ||
| 763 | suspend; | ||
| 764 | if (simulate_fail_file) return error.FileNotFound; | ||
| 765 | return result; | ||
| 766 | } | ||
| 767 | }; | ||
| 768 | } | ||
| 769 | |||
| 770 | test "alignment of local variables in async functions" { | ||
| 771 | const S = struct { | ||
| 772 | fn doTheTest() void { | ||
| 773 | var y: u8 = 123; | ||
| 774 | var x: u8 align(128) = 1; | ||
| 775 | expect(@ptrToInt(&x) % 128 == 0); | ||
| 776 | } | ||
| 777 | }; | ||
| 778 | S.doTheTest(); | ||
| 779 | } | ||