stage2: astgen async

This commit is contained in:
Veikka Tuominen 2021-01-28 22:38:25 +02:00
parent 9f722f43ac
commit 17e6e09285
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
4 changed files with 223 additions and 9 deletions

View File

@ -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,
}

View File

@ -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 },

View File

@ -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,
};
}

View File

@ -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,