Commit 78e03c466c
Changed files (9)
test
stage1
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");