| author | |
| committer | |
| log | 7799423f24a0f628641e772146f1e6a5c2f6bdcb |
| tree | 77f5b4c92b5ce9fc88cb3ca6e9d5fba4838e17fc |
| parent | 5749dc49d839ba607392ed0934008773d33f47d7 |
| signature |
5 files changed, 829 insertions(+), 829 deletions(-)
test/stage1/behavior.zig+6-6| ... | ... | @@ -3,12 +3,13 @@ comptime { |
| 3 | 3 | _ = @import("behavior/alignof.zig"); |
| 4 | 4 | _ = @import("behavior/array.zig"); |
| 5 | 5 | _ = @import("behavior/asm.zig"); |
| 6 | _ = @import("behavior/async_fn.zig"); | |
| 6 | 7 | _ = @import("behavior/atomics.zig"); |
| 8 | _ = @import("behavior/await_struct.zig"); | |
| 7 | 9 | _ = @import("behavior/bit_shifting.zig"); |
| 8 | 10 | _ = @import("behavior/bitcast.zig"); |
| 9 | 11 | _ = @import("behavior/bitreverse.zig"); |
| 10 | 12 | _ = @import("behavior/bool.zig"); |
| 11 | _ = @import("behavior/byteswap.zig"); | |
| 12 | 13 | _ = @import("behavior/bugs/1025.zig"); |
| 13 | 14 | _ = @import("behavior/bugs/1076.zig"); |
| 14 | 15 | _ = @import("behavior/bugs/1111.zig"); |
| ... | ... | @@ -38,23 +39,24 @@ comptime { |
| 38 | 39 | _ = @import("behavior/bugs/726.zig"); |
| 39 | 40 | _ = @import("behavior/bugs/828.zig"); |
| 40 | 41 | _ = @import("behavior/bugs/920.zig"); |
| 42 | _ = @import("behavior/byteswap.zig"); | |
| 41 | 43 | _ = @import("behavior/byval_arg_var.zig"); |
| 42 | 44 | _ = @import("behavior/cancel.zig"); |
| 43 | 45 | _ = @import("behavior/cast.zig"); |
| 44 | 46 | _ = @import("behavior/const_slice_child.zig"); |
| 45 | _ = @import("behavior/coroutine_await_struct.zig"); | |
| 46 | _ = @import("behavior/coroutines.zig"); | |
| 47 | 47 | _ = @import("behavior/defer.zig"); |
| 48 | 48 | _ = @import("behavior/enum.zig"); |
| 49 | 49 | _ = @import("behavior/enum_with_members.zig"); |
| 50 | 50 | _ = @import("behavior/error.zig"); |
| 51 | 51 | _ = @import("behavior/eval.zig"); |
| 52 | 52 | _ = @import("behavior/field_parent_ptr.zig"); |
| 53 | _ = @import("behavior/floatop.zig"); | |
| 53 | 54 | _ = @import("behavior/fn.zig"); |
| 54 | 55 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); |
| 55 | 56 | _ = @import("behavior/for.zig"); |
| 56 | 57 | _ = @import("behavior/generics.zig"); |
| 57 | 58 | _ = @import("behavior/hasdecl.zig"); |
| 59 | _ = @import("behavior/hasfield.zig"); | |
| 58 | 60 | _ = @import("behavior/if.zig"); |
| 59 | 61 | _ = @import("behavior/import.zig"); |
| 60 | 62 | _ = @import("behavior/incomplete_struct_param_tld.zig"); |
| ... | ... | @@ -63,14 +65,13 @@ comptime { |
| 63 | 65 | _ = @import("behavior/math.zig"); |
| 64 | 66 | _ = @import("behavior/merge_error_sets.zig"); |
| 65 | 67 | _ = @import("behavior/misc.zig"); |
| 68 | _ = @import("behavior/muladd.zig"); | |
| 66 | 69 | _ = @import("behavior/namespace_depends_on_compile_var.zig"); |
| 67 | 70 | _ = @import("behavior/new_stack_call.zig"); |
| 68 | 71 | _ = @import("behavior/null.zig"); |
| 69 | 72 | _ = @import("behavior/optional.zig"); |
| 70 | 73 | _ = @import("behavior/pointers.zig"); |
| 71 | 74 | _ = @import("behavior/popcount.zig"); |
| 72 | _ = @import("behavior/muladd.zig"); | |
| 73 | _ = @import("behavior/floatop.zig"); | |
| 74 | 75 | _ = @import("behavior/ptrcast.zig"); |
| 75 | 76 | _ = @import("behavior/pub_enum.zig"); |
| 76 | 77 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); |
| ... | ... | @@ -99,5 +100,4 @@ comptime { |
| 99 | 100 | _ = @import("behavior/void.zig"); |
| 100 | 101 | _ = @import("behavior/while.zig"); |
| 101 | 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 | } |