diff --git a/src/Module.zig b/src/Module.zig index e6d509ace5..c5cdbb3890 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -370,6 +370,8 @@ pub const Scope = struct { .gen_zir => return self.cast(GenZIR).?.arena, .local_val => return self.cast(LocalVal).?.gen_zir.arena, .local_ptr => return self.cast(LocalPtr).?.gen_zir.arena, + .gen_suspend => return self.cast(GenZIR).?.arena, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.arena, .file => unreachable, .container => unreachable, } @@ -385,6 +387,8 @@ pub const Scope = struct { .gen_zir => self.cast(GenZIR).?.decl, .local_val => self.cast(LocalVal).?.gen_zir.decl, .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, + .gen_suspend => return self.cast(GenZIR).?.decl, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl, .file => null, .container => null, }; @@ -396,6 +400,8 @@ pub const Scope = struct { .gen_zir => self.cast(GenZIR).?.decl, .local_val => self.cast(LocalVal).?.gen_zir.decl, .local_ptr => self.cast(LocalPtr).?.gen_zir.decl, + .gen_suspend => return self.cast(GenZIR).?.decl, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl, .file => null, .container => null, }; @@ -410,6 +416,8 @@ pub const Scope = struct { .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container, .file => return &self.cast(File).?.root_container, .container => return self.cast(Container).?, + .gen_suspend => return self.cast(GenZIR).?.decl.container, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir.decl.container, } } @@ -422,6 +430,8 @@ pub const Scope = struct { .gen_zir => unreachable, .local_val => unreachable, .local_ptr => unreachable, + .gen_suspend => unreachable, + .gen_nosuspend => unreachable, .file => unreachable, .container => return self.cast(Container).?.fullyQualifiedNameHash(name), } @@ -436,6 +446,8 @@ pub const Scope = struct { .local_val => return &self.cast(LocalVal).?.gen_zir.decl.container.file_scope.tree, .local_ptr => return &self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.tree, .container => return &self.cast(Container).?.file_scope.tree, + .gen_suspend => return &self.cast(GenZIR).?.decl.container.file_scope.tree, + .gen_nosuspend => return &self.cast(Nosuspend).?.gen_zir.decl.container.file_scope.tree, } } @@ -443,9 +455,10 @@ pub const Scope = struct { pub fn getGenZIR(self: *Scope) *GenZIR { return switch (self.tag) { .block => unreachable, - .gen_zir => self.cast(GenZIR).?, + .gen_zir, .gen_suspend => self.cast(GenZIR).?, .local_val => return self.cast(LocalVal).?.gen_zir, .local_ptr => return self.cast(LocalPtr).?.gen_zir, + .gen_nosuspend => return self.cast(Nosuspend).?.gen_zir, .file => unreachable, .container => unreachable, }; @@ -461,6 +474,8 @@ pub const Scope = struct { .gen_zir => unreachable, .local_val => unreachable, .local_ptr => unreachable, + .gen_suspend => unreachable, + .gen_nosuspend => unreachable, } } @@ -472,6 +487,8 @@ pub const Scope = struct { .local_val => unreachable, .local_ptr => unreachable, .block => unreachable, + .gen_suspend => unreachable, + .gen_nosuspend => unreachable, } } @@ -486,6 +503,36 @@ pub const Scope = struct { .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, .block => return @fieldParentPtr(Block, "base", cur).src_decl.container.file_scope, + .gen_suspend => @fieldParentPtr(GenZIR, "base", cur).parent, + .gen_nosuspend => @fieldParentPtr(Nosuspend, "base", cur).parent, + }; + } + } + + pub fn getSuspend(base: *Scope) ?*Scope.GenZIR { + var cur = base; + while (true) { + cur = switch (cur.tag) { + .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent, + .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, + .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, + .gen_nosuspend => @fieldParentPtr(Nosuspend, "base", cur).parent, + .gen_suspend => return @fieldParentPtr(GenZIR, "base", cur), + else => return null, + }; + } + } + + pub fn getNosuspend(base: *Scope) ?*Scope.Nosuspend { + var cur = base; + while (true) { + cur = switch (cur.tag) { + .gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent, + .local_val => @fieldParentPtr(LocalVal, "base", cur).parent, + .local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent, + .gen_suspend => @fieldParentPtr(GenZIR, "base", cur).parent, + .gen_nosuspend => return @fieldParentPtr(Nosuspend, "base", cur), + else => return null, }; } } @@ -507,6 +554,8 @@ pub const Scope = struct { gen_zir, local_val, local_ptr, + gen_suspend, + gen_nosuspend, }; pub const Container = struct { @@ -740,6 +789,8 @@ pub const Scope = struct { /// so they can possibly be elided later if the labeled block ends up not needing /// a result location pointer. labeled_store_to_block_ptr_list: std.ArrayListUnmanaged(*zir.Inst.BinOp) = .{}, + /// for suspend error notes + src: usize = 0, pub const Label = struct { token: ast.TokenIndex, @@ -773,6 +824,16 @@ pub const Scope = struct { name: []const u8, ptr: *zir.Inst, }; + + pub const Nosuspend = struct { + pub const base_tag: Tag = .gen_nosuspend; + + base: Scope = Scope{ .tag = base_tag }, + /// Parents can be: `LocalVal`, `LocalPtr`, `GenZIR`. + parent: *Scope, + gen_zir: *GenZIR, + src: usize, + }; }; /// This struct holds data necessary to construct API-facing `AllErrors.Message`. @@ -3586,7 +3647,7 @@ pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) I } self.failed_decls.putAssumeCapacityNoClobber(block.owner_decl, err_msg); }, - .gen_zir => { + .gen_zir, .gen_suspend => { const gen_zir = scope.cast(Scope.GenZIR).?; gen_zir.decl.analysis = .sema_failure; gen_zir.decl.generation = self.generation; @@ -3604,6 +3665,12 @@ pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) I gen_zir.decl.generation = self.generation; self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); }, + .gen_nosuspend => { + const gen_zir = scope.cast(Scope.Nosuspend).?.gen_zir; + gen_zir.decl.analysis = .sema_failure; + gen_zir.decl.generation = self.generation; + self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg); + }, .file => unreachable, .container => unreachable, } diff --git a/src/astgen.zig b/src/astgen.zig index aaf38ed1ea..939fba2ff3 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -626,10 +626,13 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .@"comptime" => return comptimeExpr(mod, scope, rl, node_datas[node].lhs), .@"switch", .switch_comma => return switchExpr(mod, scope, rl, node), + .@"nosuspend" => return nosuspendExpr(mod, scope, rl, node), + .@"suspend" => return rvalue(mod, scope, rl, try suspendExpr(mod, scope, node)), + .@"await" => return awaitExpr(mod, scope, rl, node), + .@"resume" => return rvalue(mod, scope, rl, try resumeExpr(mod, scope, node)), + .@"defer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .defer", .{}), .@"errdefer" => return mod.failNode(scope, node, "TODO implement astgen.expr for .errdefer", .{}), - .@"await" => return mod.failNode(scope, node, "TODO implement astgen.expr for .await", .{}), - .@"resume" => return mod.failNode(scope, node, "TODO implement astgen.expr for .resume", .{}), .@"try" => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}), .array_init_one, @@ -652,15 +655,12 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In .struct_init_comma, => return mod.failNode(scope, node, "TODO implement astgen.expr for struct literals", .{}), - .@"suspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .suspend", .{}), .@"anytype" => return mod.failNode(scope, node, "TODO implement astgen.expr for .anytype", .{}), .fn_proto_simple, .fn_proto_multi, .fn_proto_one, .fn_proto, => return mod.failNode(scope, node, "TODO implement astgen.expr for function prototypes", .{}), - - .@"nosuspend" => return mod.failNode(scope, node, "TODO implement astgen.expr for .nosuspend", .{}), } } @@ -766,6 +766,8 @@ fn breakExpr( }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, + .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent, else => if (break_label != 0) { const label_name = try mod.identifierTokenString(parent_scope, break_label); return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); @@ -819,6 +821,8 @@ fn continueExpr( }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, + .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent, else => if (break_label != 0) { const label_name = try mod.identifierTokenString(parent_scope, break_label); return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name}); @@ -893,6 +897,8 @@ fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIn }, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, + .gen_suspend => scope = scope.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => scope = scope.cast(Scope.Nosuspend).?.parent, else => return, } } @@ -1100,6 +1106,8 @@ fn varDecl( s = local_ptr.parent; }, .gen_zir => s = s.cast(Scope.GenZIR).?.parent, + .gen_suspend => s = s.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => s = s.cast(Scope.Nosuspend).?.parent, else => break, }; } @@ -3021,6 +3029,8 @@ fn identifier( s = local_ptr.parent; }, .gen_zir => s = s.cast(Scope.GenZIR).?.parent, + .gen_suspend => s = s.cast(Scope.GenZIR).?.parent, + .gen_nosuspend => s = s.cast(Scope.Nosuspend).?.parent, else => break, }; } @@ -3633,14 +3643,109 @@ fn callExpr( } const src = token_starts[call.ast.lparen]; + var modifier: std.builtin.CallOptions.Modifier = .auto; + if (call.async_token) |_| modifier = .async_kw; + const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{ .func = lhs, .args = args, + .modifier = modifier, }, .{}); // TODO function call with result location return rvalue(mod, scope, rl, result); } +fn suspendExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]]; + + if (scope.getNosuspend()) |some| { + const msg = msg: { + const msg = try mod.errMsg(scope, src, "suspend in nosuspend block", .{}); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some.src, msg, "nosuspend block here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + + if (scope.getSuspend()) |some| { + const msg = msg: { + const msg = try mod.errMsg(scope, src, "cannot suspend inside suspend block", .{}); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some.src, msg, "other suspend block here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + + var suspend_scope: Scope.GenZIR = .{ + .base = .{ .tag = .gen_suspend }, + .parent = scope, + .decl = scope.ownerDecl().?, + .arena = scope.arena(), + .force_comptime = scope.isComptime(), + .instructions = .{}, + }; + defer suspend_scope.instructions.deinit(mod.gpa); + + const operand = tree.nodes.items(.data)[node].lhs; + if (operand != 0) { + const possibly_unused_result = try expr(mod, &suspend_scope.base, .none, operand); + if (!possibly_unused_result.tag.isNoReturn()) { + _ = try addZIRUnOp(mod, &suspend_scope.base, src, .ensure_result_used, possibly_unused_result); + } + } else { + return addZIRNoOp(mod, scope, src, .@"suspend"); + } + + const block = try addZIRInstBlock(mod, scope, src, .suspend_block, .{ + .instructions = try scope.arena().dupe(*zir.Inst, suspend_scope.instructions.items), + }); + return &block.base; +} + +fn nosuspendExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + var child_scope = Scope.Nosuspend{ + .parent = scope, + .gen_zir = scope.getGenZIR(), + .src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]], + }; + + return expr(mod, &child_scope.base, rl, tree.nodes.items(.data)[node].lhs); +} + +fn awaitExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]]; + const is_nosuspend = scope.getNosuspend() != null; + + // TODO some @asyncCall stuff + + if (scope.getSuspend()) |some| { + const msg = msg: { + const msg = try mod.errMsg(scope, src, "cannot await inside suspend block", .{}); + errdefer msg.destroy(mod.gpa); + try mod.errNote(scope, some.src, msg, "suspend block here", .{}); + break :msg msg; + }; + return mod.failWithOwnedErrorMsg(scope, msg); + } + + const operand = try expr(mod, scope, .ref, tree.nodes.items(.data)[node].lhs); + // TODO pass result location + return addZIRUnOp(mod, scope, src, if (is_nosuspend) .nosuspend_await else .@"await", operand); +} + +fn resumeExpr(mod: *Module, scope: *Scope, node: ast.Node.Index) InnerError!*zir.Inst { + const tree = scope.tree(); + const src = tree.tokens.items(.start)[tree.nodes.items(.main_token)[node]]; + + const operand = try expr(mod, scope, .ref, tree.nodes.items(.data)[node].lhs); + return addZIRUnOp(mod, scope, src, .@"resume", operand); +} + pub const simple_types = std.ComptimeStringMap(Value.Tag, .{ .{ "u8", .u8_type }, .{ "i8", .i8_type }, diff --git a/src/zir.zig b/src/zir.zig index 1331f26dc7..85ecf99f20 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -61,6 +61,8 @@ pub const Inst = struct { as, /// Inline assembly. @"asm", + /// Await an async function. + @"await", /// Bitwise AND. `&` bit_and, /// TODO delete this instruction, it has no purpose. @@ -212,6 +214,8 @@ pub const Inst = struct { mul, /// Twos complement wrapping integer multiplication. mulwrap, + /// An await inside a nosuspend scope. + nosuspend_await, /// Given a reference to a function and a parameter index, returns the /// type of the parameter. TODO what happens when the parameter is `anytype`? param_type, @@ -226,6 +230,8 @@ pub const Inst = struct { /// the memory location is in the stack frame, local to the scope containing the /// instruction. ref, + /// Resume an async function. + @"resume", /// Obtains a pointer to the return value. ret_ptr, /// Obtains the return type of the in-scope function. @@ -348,6 +354,11 @@ pub const Inst = struct { enum_type, /// Does nothing; returns a void value. void_value, + /// Suspend an async function. + @"suspend", + /// Suspend an async function. + /// Same as .suspend but with a block. + suspend_block, /// A switch expression. switchbr, /// Same as `switchbr` but the target is a pointer to the value being switched on. @@ -369,6 +380,7 @@ pub const Inst = struct { .unreachable_unsafe, .unreachable_safe, .void_value, + .@"suspend", => NoOp, .alloc, @@ -417,6 +429,9 @@ pub const Inst = struct { .import, .set_eval_branch_quota, .indexable_ptr_len, + .@"resume", + .@"await", + .nosuspend_await, => UnOp, .add, @@ -461,6 +476,7 @@ pub const Inst = struct { .block_flat, .block_comptime, .block_comptime_flat, + .suspend_block, => Block, .switchbr, .switchbr_ref => SwitchBr, @@ -633,6 +649,9 @@ pub const Inst = struct { .struct_type, .void_value, .switch_range, + .@"resume", + .@"await", + .nosuspend_await, => false, .@"break", @@ -649,6 +668,8 @@ pub const Inst = struct { .container_field, .switchbr, .switchbr_ref, + .@"suspend", + .suspend_block, => true, }; } diff --git a/src/zir_sema.zig b/src/zir_sema.zig index 27e31c6197..735defa323 100644 --- a/src/zir_sema.zig +++ b/src/zir_sema.zig @@ -160,6 +160,11 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! .switchbr => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr).?, false), .switchbr_ref => return zirSwitchBr(mod, scope, old_inst.castTag(.switchbr_ref).?, true), .switch_range => return zirSwitchRange(mod, scope, old_inst.castTag(.switch_range).?), + .@"await" => return zirAwait(mod, scope, old_inst.castTag(.@"await").?), + .nosuspend_await => return zirAwait(mod, scope, old_inst.castTag(.nosuspend_await).?), + .@"resume" => return zirResume(mod, scope, old_inst.castTag(.@"resume").?), + .@"suspend" => return zirSuspend(mod, scope, old_inst.castTag(.@"suspend").?), + .suspend_block => return zirSuspendBlock(mod, scope, old_inst.castTag(.suspend_block).?), .container_field_named, .container_field_typed, @@ -1080,6 +1085,22 @@ fn zirFn(mod: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError!*Inst { }); } +fn zirAwait(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement await", .{}); +} + +fn zirResume(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement resume", .{}); +} + +fn zirSuspend(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement suspend", .{}); +} + +fn zirSuspendBlock(mod: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst { + return mod.fail(scope, inst.base.src, "TODO implement suspend", .{}); +} + fn zirIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -2046,7 +2067,7 @@ fn zirBitwise(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError!*In rhs.ty.arrayLen(), }); } - return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBitwise", .{}); + return mod.fail(scope, inst.base.src, "TODO implement support for vectors in zirBitwise", .{}); } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ lhs.ty, @@ -2127,7 +2148,7 @@ fn zirArithmetic(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerError! rhs.ty.arrayLen(), }); } - return mod.fail(scope, inst.base.src, "TODO implement support for vectors in analyzeInstBinOp", .{}); + return mod.fail(scope, inst.base.src, "TODO implement support for vectors in zirBinOp", .{}); } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { return mod.fail(scope, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ lhs.ty,