Commit 78e03c466c

Andrew Kelley <andrew@ziglang.org>
2019-07-22 01:56:37
simple async function passing test
1 parent 56c08eb
src/all_types.hpp
@@ -1346,7 +1346,16 @@ struct ZigFn {
     Scope *child_scope; // parent is scope for last parameter
     ScopeBlock *def_scope; // parent is child_scope
     Buf symbol_name;
-    ZigType *type_entry; // function type
+    // This is the function type assuming the function does not suspend.
+    // Note that for an async function, this can be shared with non-async functions. So the value here
+    // should only be read for things in common between non-async and async function types.
+    ZigType *type_entry;
+    // For normal functions one could use the type_entry->raw_type_ref and type_entry->raw_di_type.
+    // However for functions that suspend, those values could possibly be their non-suspending equivalents.
+    // So these values should be preferred.
+    LLVMTypeRef raw_type_ref;
+    ZigLLVMDIType *raw_di_type;
+
     ZigType *frame_type; // coro frame type
     // in the case of normal functions this is the implicit return type
     // in the case of async functions this is the implicit return type according to the
src/analyze.cpp
@@ -3750,7 +3750,7 @@ bool resolve_inferred_error_set(CodeGen *g, ZigType *err_set_type, AstNode *sour
     return true;
 }
 
-void analyze_fn_ir(CodeGen *g, ZigFn *fn_table_entry, AstNode *return_type_node) {
+static void analyze_fn_ir(CodeGen *g, ZigFn *fn_table_entry, AstNode *return_type_node) {
     ZigType *fn_type = fn_table_entry->type_entry;
     assert(!fn_type->data.fn.is_generic);
     FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
@@ -5850,6 +5850,7 @@ static const ZigTypeId all_type_ids[] = {
     ZigTypeIdBoundFn,
     ZigTypeIdArgTuple,
     ZigTypeIdOpaque,
+    ZigTypeIdCoroFrame,
     ZigTypeIdVector,
     ZigTypeIdEnumLiteral,
 };
@@ -7035,7 +7036,13 @@ static void resolve_llvm_types_array(CodeGen *g, ZigType *type) {
 }
 
 void resolve_llvm_types_fn(CodeGen *g, ZigType *fn_type, ZigFn *fn) {
-    if (fn_type->llvm_di_type != nullptr) return;
+    if (fn_type->llvm_di_type != nullptr) {
+        if (fn != nullptr) {
+            fn->raw_type_ref = fn_type->data.fn.raw_type_ref;
+            fn->raw_di_type = fn_type->data.fn.raw_di_type;
+        }
+        return;
+    }
 
     FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
     bool first_arg_return = want_first_arg_sret(g, fn_type_id);
@@ -7118,6 +7125,12 @@ void resolve_llvm_types_fn(CodeGen *g, ZigType *fn_type, ZigFn *fn) {
     for (size_t i = 0; i < gen_param_types.length; i += 1) {
         assert(gen_param_types.items[i] != nullptr);
     }
+    if (fn != nullptr) {
+        fn->raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type),
+                gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args);
+        fn->raw_di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0);
+        return;
+    }
     fn_type->data.fn.raw_type_ref = LLVMFunctionType(get_llvm_type(g, gen_return_type),
             gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args);
     fn_type->llvm_type = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);
src/analyze.hpp
@@ -105,7 +105,6 @@ void eval_min_max_value(CodeGen *g, ZigType *type_entry, ConstExprValue *const_v
 void eval_min_max_value_int(CodeGen *g, ZigType *int_type, BigInt *bigint, bool is_max);
 
 void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val);
-void analyze_fn_ir(CodeGen *g, ZigFn *fn_table_entry, AstNode *return_type_node);
 
 ScopeBlock *create_block_scope(CodeGen *g, AstNode *node, Scope *parent);
 ScopeDefer *create_defer_scope(CodeGen *g, AstNode *node, Scope *parent);
src/codegen.cpp
@@ -499,7 +499,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
     ZigType *fn_type = fn_table_entry->type_entry;
     // Make the raw_type_ref populated
     resolve_llvm_types_fn(g, fn_type, fn_table_entry);
-    LLVMTypeRef fn_llvm_type = fn_type->data.fn.raw_type_ref;
+    LLVMTypeRef fn_llvm_type = fn_table_entry->raw_type_ref;
     if (fn_table_entry->body_node == nullptr) {
         LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, buf_ptr(symbol_name));
         if (existing_llvm_fn) {
@@ -521,9 +521,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
                 assert(entry->value->id == TldIdFn);
                 TldFn *tld_fn = reinterpret_cast<TldFn *>(entry->value);
                 // Make the raw_type_ref populated
-                (void)get_llvm_type(g, tld_fn->fn_entry->type_entry);
+                resolve_llvm_types_fn(g, tld_fn->fn_entry->type_entry, tld_fn->fn_entry);
                 tld_fn->fn_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name),
-                        tld_fn->fn_entry->type_entry->data.fn.raw_type_ref);
+                        tld_fn->fn_entry->raw_type_ref);
                 fn_table_entry->llvm_value = LLVMConstBitCast(tld_fn->fn_entry->llvm_value,
                         LLVMPointerType(fn_llvm_type, 0));
                 return fn_table_entry->llvm_value;
@@ -683,10 +683,11 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
             unsigned flags = ZigLLVM_DIFlags_StaticMember;
             ZigLLVMDIScope *fn_di_scope = get_di_scope(g, scope->parent);
             assert(fn_di_scope != nullptr);
+            assert(fn_table_entry->raw_di_type != nullptr);
             ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder,
                 fn_di_scope, buf_ptr(&fn_table_entry->symbol_name), "",
                 import->data.structure.root_struct->di_file, line_number,
-                fn_table_entry->type_entry->data.fn.raw_di_type, is_internal_linkage,
+                fn_table_entry->raw_di_type, is_internal_linkage,
                 is_definition, scope_line, flags, is_optimized, nullptr);
 
             scope->di_scope = ZigLLVMSubprogramToScope(subprogram);
@@ -3472,10 +3473,13 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
         }
 
         gen_param_values.append(result_loc);
-    } else if (first_arg_ret) {
-        gen_param_values.append(result_loc);
-    } else if (prefix_arg_err_ret_stack) {
-        gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope));
+    } else {
+        if (first_arg_ret) {
+            gen_param_values.append(result_loc);
+        }
+        if (prefix_arg_err_ret_stack) {
+            gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope));
+        }
     }
     FnWalk fn_walk = {};
     fn_walk.id = FnWalkIdCall;
std/hash_map.zig
@@ -535,17 +535,18 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
 // TODO improve these hash functions
 pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type) HashInt {
     switch (@typeInfo(@typeOf(key))) {
-        builtin.TypeId.NoReturn,
-        builtin.TypeId.Opaque,
-        builtin.TypeId.Undefined,
-        builtin.TypeId.ArgTuple,
+        .NoReturn,
+        .Opaque,
+        .Undefined,
+        .ArgTuple,
+        .Frame,
         => @compileError("cannot hash this type"),
 
-        builtin.TypeId.Void,
-        builtin.TypeId.Null,
+        .Void,
+        .Null,
         => return 0,
 
-        builtin.TypeId.Int => |info| {
+        .Int => |info| {
             const unsigned_x = @bitCast(@IntType(false, info.bits), key);
             if (info.bits <= HashInt.bit_count) {
                 return HashInt(unsigned_x) ^ comptime rng.scalar(HashInt);
@@ -554,26 +555,26 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
             }
         },
 
-        builtin.TypeId.Float => |info| {
+        .Float => |info| {
             return autoHash(@bitCast(@IntType(false, info.bits), key), rng, HashInt);
         },
-        builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng, HashInt),
-        builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng, HashInt),
-        builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng, HashInt),
-        builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng, HashInt),
-
-        builtin.TypeId.BoundFn,
-        builtin.TypeId.ComptimeFloat,
-        builtin.TypeId.ComptimeInt,
-        builtin.TypeId.Type,
-        builtin.TypeId.EnumLiteral,
+        .Bool => return autoHash(@boolToInt(key), rng, HashInt),
+        .Enum => return autoHash(@enumToInt(key), rng, HashInt),
+        .ErrorSet => return autoHash(@errorToInt(key), rng, HashInt),
+        .Fn => return autoHash(@ptrToInt(key), rng, HashInt),
+
+        .BoundFn,
+        .ComptimeFloat,
+        .ComptimeInt,
+        .Type,
+        .EnumLiteral,
         => return 0,
 
-        builtin.TypeId.Pointer => |info| switch (info.size) {
-            builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto hash for single item pointers"),
-            builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto hash for many item pointers"),
-            builtin.TypeInfo.Pointer.Size.C => @compileError("TODO auto hash C pointers"),
-            builtin.TypeInfo.Pointer.Size.Slice => {
+        .Pointer => |info| switch (info.size) {
+            .One => @compileError("TODO auto hash for single item pointers"),
+            .Many => @compileError("TODO auto hash for many item pointers"),
+            .C => @compileError("TODO auto hash C pointers"),
+            .Slice => {
                 const interval = std.math.max(1, key.len / 256);
                 var i: usize = 0;
                 var h = comptime rng.scalar(HashInt);
@@ -584,44 +585,44 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
             },
         },
 
-        builtin.TypeId.Optional => @compileError("TODO auto hash for optionals"),
-        builtin.TypeId.Array => @compileError("TODO auto hash for arrays"),
-        builtin.TypeId.Vector => @compileError("TODO auto hash for vectors"),
-        builtin.TypeId.Struct => @compileError("TODO auto hash for structs"),
-        builtin.TypeId.Union => @compileError("TODO auto hash for unions"),
-        builtin.TypeId.ErrorUnion => @compileError("TODO auto hash for unions"),
+        .Optional => @compileError("TODO auto hash for optionals"),
+        .Array => @compileError("TODO auto hash for arrays"),
+        .Vector => @compileError("TODO auto hash for vectors"),
+        .Struct => @compileError("TODO auto hash for structs"),
+        .Union => @compileError("TODO auto hash for unions"),
+        .ErrorUnion => @compileError("TODO auto hash for unions"),
     }
 }
 
 pub fn autoEql(a: var, b: @typeOf(a)) bool {
     switch (@typeInfo(@typeOf(a))) {
-        builtin.TypeId.NoReturn,
-        builtin.TypeId.Opaque,
-        builtin.TypeId.Undefined,
-        builtin.TypeId.ArgTuple,
+        .NoReturn,
+        .Opaque,
+        .Undefined,
+        .ArgTuple,
         => @compileError("cannot test equality of this type"),
-        builtin.TypeId.Void,
-        builtin.TypeId.Null,
+        .Void,
+        .Null,
         => return true,
-        builtin.TypeId.Bool,
-        builtin.TypeId.Int,
-        builtin.TypeId.Float,
-        builtin.TypeId.ComptimeFloat,
-        builtin.TypeId.ComptimeInt,
-        builtin.TypeId.EnumLiteral,
-        builtin.TypeId.Promise,
-        builtin.TypeId.Enum,
-        builtin.TypeId.BoundFn,
-        builtin.TypeId.Fn,
-        builtin.TypeId.ErrorSet,
-        builtin.TypeId.Type,
+        .Bool,
+        .Int,
+        .Float,
+        .ComptimeFloat,
+        .ComptimeInt,
+        .EnumLiteral,
+        .Promise,
+        .Enum,
+        .BoundFn,
+        .Fn,
+        .ErrorSet,
+        .Type,
         => return a == b,
 
-        builtin.TypeId.Pointer => |info| switch (info.size) {
-            builtin.TypeInfo.Pointer.Size.One => @compileError("TODO auto eql for single item pointers"),
-            builtin.TypeInfo.Pointer.Size.Many => @compileError("TODO auto eql for many item pointers"),
-            builtin.TypeInfo.Pointer.Size.C => @compileError("TODO auto eql for C pointers"),
-            builtin.TypeInfo.Pointer.Size.Slice => {
+        .Pointer => |info| switch (info.size) {
+            .One => @compileError("TODO auto eql for single item pointers"),
+            .Many => @compileError("TODO auto eql for many item pointers"),
+            .C => @compileError("TODO auto eql for C pointers"),
+            .Slice => {
                 if (a.len != b.len) return false;
                 for (a) |a_item, i| {
                     if (!autoEql(a_item, b[i])) return false;
@@ -630,11 +631,11 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool {
             },
         },
 
-        builtin.TypeId.Optional => @compileError("TODO auto eql for optionals"),
-        builtin.TypeId.Array => @compileError("TODO auto eql for arrays"),
-        builtin.TypeId.Struct => @compileError("TODO auto eql for structs"),
-        builtin.TypeId.Union => @compileError("TODO auto eql for unions"),
-        builtin.TypeId.ErrorUnion => @compileError("TODO auto eql for unions"),
-        builtin.TypeId.Vector => @compileError("TODO auto eql for vectors"),
+        .Optional => @compileError("TODO auto eql for optionals"),
+        .Array => @compileError("TODO auto eql for arrays"),
+        .Struct => @compileError("TODO auto eql for structs"),
+        .Union => @compileError("TODO auto eql for unions"),
+        .ErrorUnion => @compileError("TODO auto eql for unions"),
+        .Vector => @compileError("TODO auto eql for vectors"),
     }
 }
std/testing.zig
@@ -25,35 +25,36 @@ pub fn expectError(expected_error: anyerror, actual_error_union: var) void {
 /// The types must match exactly.
 pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
     switch (@typeInfo(@typeOf(actual))) {
-        TypeId.NoReturn,
-        TypeId.BoundFn,
-        TypeId.ArgTuple,
-        TypeId.Opaque,
+        .NoReturn,
+        .BoundFn,
+        .ArgTuple,
+        .Opaque,
+        .Frame,
         => @compileError("value of type " ++ @typeName(@typeOf(actual)) ++ " encountered"),
 
-        TypeId.Undefined,
-        TypeId.Null,
-        TypeId.Void,
+        .Undefined,
+        .Null,
+        .Void,
         => return,
 
-        TypeId.Type,
-        TypeId.Bool,
-        TypeId.Int,
-        TypeId.Float,
-        TypeId.ComptimeFloat,
-        TypeId.ComptimeInt,
-        TypeId.EnumLiteral,
-        TypeId.Enum,
-        TypeId.Fn,
-        TypeId.Vector,
-        TypeId.ErrorSet,
+        .Type,
+        .Bool,
+        .Int,
+        .Float,
+        .ComptimeFloat,
+        .ComptimeInt,
+        .EnumLiteral,
+        .Enum,
+        .Fn,
+        .Vector,
+        .ErrorSet,
         => {
             if (actual != expected) {
                 std.debug.panic("expected {}, found {}", expected, actual);
             }
         },
 
-        TypeId.Pointer => |pointer| {
+        .Pointer => |pointer| {
             switch (pointer.size) {
                 builtin.TypeInfo.Pointer.Size.One,
                 builtin.TypeInfo.Pointer.Size.Many,
@@ -75,22 +76,22 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
             }
         },
 
-        TypeId.Array => |array| expectEqualSlices(array.child, &expected, &actual),
+        .Array => |array| expectEqualSlices(array.child, &expected, &actual),
 
-        TypeId.Struct => |structType| {
+        .Struct => |structType| {
             inline for (structType.fields) |field| {
                 expectEqual(@field(expected, field.name), @field(actual, field.name));
             }
         },
 
-        TypeId.Union => |union_info| {
+        .Union => |union_info| {
             if (union_info.tag_type == null) {
                 @compileError("Unable to compare untagged union values");
             }
             @compileError("TODO implement testing.expectEqual for tagged unions");
         },
 
-        TypeId.Optional => {
+        .Optional => {
             if (expected) |expected_payload| {
                 if (actual) |actual_payload| {
                     expectEqual(expected_payload, actual_payload);
@@ -104,7 +105,7 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void {
             }
         },
 
-        TypeId.ErrorUnion => {
+        .ErrorUnion => {
             if (expected) |expected_payload| {
                 if (actual) |actual_payload| {
                     expectEqual(expected_payload, actual_payload);
test/stage1/behavior/coroutines.zig
@@ -1,236 +1,245 @@
 const std = @import("std");
 const builtin = @import("builtin");
 const expect = std.testing.expect;
-const allocator = std.heap.direct_allocator;
 
 var x: i32 = 1;
 
-test "create a coroutine and cancel it" {
-    const p = try async<allocator> simpleAsyncFn();
-    comptime expect(@typeOf(p) == promise->void);
-    cancel p;
+test "simple coroutine suspend" {
+    const p = async simpleAsyncFn();
     expect(x == 2);
 }
-async fn simpleAsyncFn() void {
+fn simpleAsyncFn() void {
     x += 1;
     suspend;
     x += 1;
 }
 
-test "coroutine suspend, resume, cancel" {
-    seq('a');
-    const p = try async<allocator> testAsyncSeq();
-    seq('c');
-    resume p;
-    seq('f');
-    cancel p;
-    seq('g');
-
-    expect(std.mem.eql(u8, points, "abcdefg"));
-}
-async fn testAsyncSeq() void {
-    defer seq('e');
-
-    seq('b');
-    suspend;
-    seq('d');
-}
-var points = [_]u8{0} ** "abcdefg".len;
-var index: usize = 0;
-
-fn seq(c: u8) void {
-    points[index] = c;
-    index += 1;
-}
-
-test "coroutine suspend with block" {
-    const p = try async<allocator> testSuspendBlock();
-    std.testing.expect(!result);
-    resume a_promise;
-    std.testing.expect(result);
-    cancel p;
-}
-
-var a_promise: promise = undefined;
-var result = false;
-async fn testSuspendBlock() void {
-    suspend {
-        comptime expect(@typeOf(@handle()) == promise->void);
-        a_promise = @handle();
-    }
-
-    //Test to make sure that @handle() works as advertised (issue #1296)
-    //var our_handle: promise = @handle();
-    expect(a_promise == @handle());
-
-    result = true;
-}
-
-var await_a_promise: promise = undefined;
-var await_final_result: i32 = 0;
-
-test "coroutine await" {
-    await_seq('a');
-    const p = async<allocator> await_amain() catch unreachable;
-    await_seq('f');
-    resume await_a_promise;
-    await_seq('i');
-    expect(await_final_result == 1234);
-    expect(std.mem.eql(u8, await_points, "abcdefghi"));
-}
-async fn await_amain() void {
-    await_seq('b');
-    const p = async await_another() catch unreachable;
-    await_seq('e');
-    await_final_result = await p;
-    await_seq('h');
-}
-async fn await_another() i32 {
-    await_seq('c');
-    suspend {
-        await_seq('d');
-        await_a_promise = @handle();
-    }
-    await_seq('g');
-    return 1234;
-}
-
-var await_points = [_]u8{0} ** "abcdefghi".len;
-var await_seq_index: usize = 0;
-
-fn await_seq(c: u8) void {
-    await_points[await_seq_index] = c;
-    await_seq_index += 1;
-}
-
-var early_final_result: i32 = 0;
-
-test "coroutine await early return" {
-    early_seq('a');
-    const p = async<allocator> early_amain() catch @panic("out of memory");
-    early_seq('f');
-    expect(early_final_result == 1234);
-    expect(std.mem.eql(u8, early_points, "abcdef"));
-}
-async fn early_amain() void {
-    early_seq('b');
-    const p = async early_another() catch @panic("out of memory");
-    early_seq('d');
-    early_final_result = await p;
-    early_seq('e');
-}
-async fn early_another() i32 {
-    early_seq('c');
-    return 1234;
-}
-
-var early_points = [_]u8{0} ** "abcdef".len;
-var early_seq_index: usize = 0;
-
-fn early_seq(c: u8) void {
-    early_points[early_seq_index] = c;
-    early_seq_index += 1;
-}
-
-test "coro allocation failure" {
-    var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0);
-    if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) {
-        @panic("expected allocation failure");
-    } else |err| switch (err) {
-        error.OutOfMemory => {},
-    }
-}
-async fn asyncFuncThatNeverGetsRun() void {
-    @panic("coro frame allocation should fail");
-}
-
-test "async function with dot syntax" {
-    const S = struct {
-        var y: i32 = 1;
-        async fn foo() void {
-            y += 1;
-            suspend;
-        }
-    };
-    const p = try async<allocator> S.foo();
-    cancel p;
-    expect(S.y == 2);
-}
-
-test "async fn pointer in a struct field" {
-    var data: i32 = 1;
-    const Foo = struct {
-        bar: async<*std.mem.Allocator> fn (*i32) void,
-    };
-    var foo = Foo{ .bar = simpleAsyncFn2 };
-    const p = (async<allocator> foo.bar(&data)) catch unreachable;
-    expect(data == 2);
-    cancel p;
-    expect(data == 4);
-}
-async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void {
-    defer y.* += 2;
-    y.* += 1;
-    suspend;
-}
-
-test "async fn with inferred error set" {
-    const p = (async<allocator> failing()) catch unreachable;
-    resume p;
-    cancel p;
-}
-
-async fn failing() !void {
-    suspend;
-    return error.Fail;
-}
-
-test "error return trace across suspend points - early return" {
-    const p = nonFailing();
-    resume p;
-    const p2 = try async<allocator> printTrace(p);
-    cancel p2;
-}
-
-test "error return trace across suspend points - async return" {
-    const p = nonFailing();
-    const p2 = try async<std.debug.global_allocator> printTrace(p);
-    resume p;
-    cancel p2;
-}
-
-fn nonFailing() (promise->anyerror!void) {
-    return async<std.debug.global_allocator> suspendThenFail() catch unreachable;
-}
-async fn suspendThenFail() anyerror!void {
-    suspend;
-    return error.Fail;
-}
-async fn printTrace(p: promise->(anyerror!void)) void {
-    (await p) catch |e| {
-        std.testing.expect(e == error.Fail);
-        if (@errorReturnTrace()) |trace| {
-            expect(trace.index == 1);
-        } else switch (builtin.mode) {
-            builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"),
-            builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {},
-        }
-    };
-}
-
-test "break from suspend" {
-    var buf: [500]u8 = undefined;
-    var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
-    var my_result: i32 = 1;
-    const p = try async<a> testBreakFromSuspend(&my_result);
-    cancel p;
-    std.testing.expect(my_result == 2);
-}
-async fn testBreakFromSuspend(my_result: *i32) void {
-    suspend {
-        resume @handle();
-    }
-    my_result.* += 1;
-    suspend;
-    my_result.* += 1;
-}
+//test "create a coroutine and cancel it" {
+//    const p = try async<allocator> simpleAsyncFn();
+//    comptime expect(@typeOf(p) == promise->void);
+//    cancel p;
+//    expect(x == 2);
+//}
+//async fn simpleAsyncFn() void {
+//    x += 1;
+//    suspend;
+//    x += 1;
+//}
+//
+//test "coroutine suspend, resume, cancel" {
+//    seq('a');
+//    const p = try async<allocator> testAsyncSeq();
+//    seq('c');
+//    resume p;
+//    seq('f');
+//    cancel p;
+//    seq('g');
+//
+//    expect(std.mem.eql(u8, points, "abcdefg"));
+//}
+//async fn testAsyncSeq() void {
+//    defer seq('e');
+//
+//    seq('b');
+//    suspend;
+//    seq('d');
+//}
+//var points = [_]u8{0} ** "abcdefg".len;
+//var index: usize = 0;
+//
+//fn seq(c: u8) void {
+//    points[index] = c;
+//    index += 1;
+//}
+//
+//test "coroutine suspend with block" {
+//    const p = try async<allocator> testSuspendBlock();
+//    std.testing.expect(!result);
+//    resume a_promise;
+//    std.testing.expect(result);
+//    cancel p;
+//}
+//
+//var a_promise: promise = undefined;
+//var result = false;
+//async fn testSuspendBlock() void {
+//    suspend {
+//        comptime expect(@typeOf(@handle()) == promise->void);
+//        a_promise = @handle();
+//    }
+//
+//    //Test to make sure that @handle() works as advertised (issue #1296)
+//    //var our_handle: promise = @handle();
+//    expect(a_promise == @handle());
+//
+//    result = true;
+//}
+//
+//var await_a_promise: promise = undefined;
+//var await_final_result: i32 = 0;
+//
+//test "coroutine await" {
+//    await_seq('a');
+//    const p = async<allocator> await_amain() catch unreachable;
+//    await_seq('f');
+//    resume await_a_promise;
+//    await_seq('i');
+//    expect(await_final_result == 1234);
+//    expect(std.mem.eql(u8, await_points, "abcdefghi"));
+//}
+//async fn await_amain() void {
+//    await_seq('b');
+//    const p = async await_another() catch unreachable;
+//    await_seq('e');
+//    await_final_result = await p;
+//    await_seq('h');
+//}
+//async fn await_another() i32 {
+//    await_seq('c');
+//    suspend {
+//        await_seq('d');
+//        await_a_promise = @handle();
+//    }
+//    await_seq('g');
+//    return 1234;
+//}
+//
+//var await_points = [_]u8{0} ** "abcdefghi".len;
+//var await_seq_index: usize = 0;
+//
+//fn await_seq(c: u8) void {
+//    await_points[await_seq_index] = c;
+//    await_seq_index += 1;
+//}
+//
+//var early_final_result: i32 = 0;
+//
+//test "coroutine await early return" {
+//    early_seq('a');
+//    const p = async<allocator> early_amain() catch @panic("out of memory");
+//    early_seq('f');
+//    expect(early_final_result == 1234);
+//    expect(std.mem.eql(u8, early_points, "abcdef"));
+//}
+//async fn early_amain() void {
+//    early_seq('b');
+//    const p = async early_another() catch @panic("out of memory");
+//    early_seq('d');
+//    early_final_result = await p;
+//    early_seq('e');
+//}
+//async fn early_another() i32 {
+//    early_seq('c');
+//    return 1234;
+//}
+//
+//var early_points = [_]u8{0} ** "abcdef".len;
+//var early_seq_index: usize = 0;
+//
+//fn early_seq(c: u8) void {
+//    early_points[early_seq_index] = c;
+//    early_seq_index += 1;
+//}
+//
+//test "coro allocation failure" {
+//    var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0);
+//    if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) {
+//        @panic("expected allocation failure");
+//    } else |err| switch (err) {
+//        error.OutOfMemory => {},
+//    }
+//}
+//async fn asyncFuncThatNeverGetsRun() void {
+//    @panic("coro frame allocation should fail");
+//}
+//
+//test "async function with dot syntax" {
+//    const S = struct {
+//        var y: i32 = 1;
+//        async fn foo() void {
+//            y += 1;
+//            suspend;
+//        }
+//    };
+//    const p = try async<allocator> S.foo();
+//    cancel p;
+//    expect(S.y == 2);
+//}
+//
+//test "async fn pointer in a struct field" {
+//    var data: i32 = 1;
+//    const Foo = struct {
+//        bar: async<*std.mem.Allocator> fn (*i32) void,
+//    };
+//    var foo = Foo{ .bar = simpleAsyncFn2 };
+//    const p = (async<allocator> foo.bar(&data)) catch unreachable;
+//    expect(data == 2);
+//    cancel p;
+//    expect(data == 4);
+//}
+//async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void {
+//    defer y.* += 2;
+//    y.* += 1;
+//    suspend;
+//}
+//
+//test "async fn with inferred error set" {
+//    const p = (async<allocator> failing()) catch unreachable;
+//    resume p;
+//    cancel p;
+//}
+//
+//async fn failing() !void {
+//    suspend;
+//    return error.Fail;
+//}
+//
+//test "error return trace across suspend points - early return" {
+//    const p = nonFailing();
+//    resume p;
+//    const p2 = try async<allocator> printTrace(p);
+//    cancel p2;
+//}
+//
+//test "error return trace across suspend points - async return" {
+//    const p = nonFailing();
+//    const p2 = try async<std.debug.global_allocator> printTrace(p);
+//    resume p;
+//    cancel p2;
+//}
+//
+//fn nonFailing() (promise->anyerror!void) {
+//    return async<std.debug.global_allocator> suspendThenFail() catch unreachable;
+//}
+//async fn suspendThenFail() anyerror!void {
+//    suspend;
+//    return error.Fail;
+//}
+//async fn printTrace(p: promise->(anyerror!void)) void {
+//    (await p) catch |e| {
+//        std.testing.expect(e == error.Fail);
+//        if (@errorReturnTrace()) |trace| {
+//            expect(trace.index == 1);
+//        } else switch (builtin.mode) {
+//            builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"),
+//            builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {},
+//        }
+//    };
+//}
+//
+//test "break from suspend" {
+//    var buf: [500]u8 = undefined;
+//    var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
+//    var my_result: i32 = 1;
+//    const p = try async<a> testBreakFromSuspend(&my_result);
+//    cancel p;
+//    std.testing.expect(my_result == 2);
+//}
+//async fn testBreakFromSuspend(my_result: *i32) void {
+//    suspend {
+//        resume @handle();
+//    }
+//    my_result.* += 1;
+//    suspend;
+//    my_result.* += 1;
+//}
test/stage1/behavior/type_info.zig
@@ -177,7 +177,7 @@ fn testUnion() void {
     expect(TypeId(typeinfo_info) == TypeId.Union);
     expect(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto);
     expect(typeinfo_info.Union.tag_type.? == TypeId);
-    expect(typeinfo_info.Union.fields.len == 24);
+    expect(typeinfo_info.Union.fields.len == 25);
     expect(typeinfo_info.Union.fields[4].enum_field != null);
     expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
     expect(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int));
test/stage1/behavior.zig
@@ -43,7 +43,7 @@ comptime {
     _ = @import("behavior/cast.zig");
     _ = @import("behavior/const_slice_child.zig");
     //_ = @import("behavior/coroutine_await_struct.zig");
-    //_ = @import("behavior/coroutines.zig");
+    _ = @import("behavior/coroutines.zig");
     _ = @import("behavior/defer.zig");
     _ = @import("behavior/enum.zig");
     _ = @import("behavior/enum_with_members.zig");