AstGen: implement suspend blocks

This commit is contained in:
Andrew Kelley
2021-04-23 20:09:56 -07:00
parent b40a8efb9a
commit 9a271347fe
3 changed files with 96 additions and 5 deletions

View File

@@ -827,10 +827,10 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn
.@"comptime" => return comptimeExpr(gz, scope, rl, node_datas[node].lhs),
.@"switch", .switch_comma => return switchExpr(gz, scope, rl, node),
.@"nosuspend" => return astgen.failNode(node, "async and related features are not yet supported", .{}),
.@"suspend" => return astgen.failNode(node, "async and related features are not yet supported", .{}),
.@"await" => return astgen.failNode(node, "async and related features are not yet supported", .{}),
.@"resume" => return astgen.failNode(node, "async and related features are not yet supported", .{}),
.@"nosuspend" => return nosuspendExpr(gz, scope, rl, node),
.@"suspend" => return suspendExpr(gz, scope, rl, node),
.@"await" => return awaitExpr(gz, scope, rl, node),
.@"resume" => return resumeExpr(gz, scope, rl, node),
.@"try" => return tryExpr(gz, scope, rl, node, node_datas[node].lhs),
@@ -883,6 +883,82 @@ pub fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) Inn
}
}
pub fn nosuspendExpr(
gz: *GenZir,
scope: *Scope,
rl: ResultLoc,
node: ast.Node.Index,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
return astgen.failNode(node, "TODO AstGen nosuspendExpr", .{});
}
pub fn suspendExpr(
gz: *GenZir,
scope: *Scope,
rl: ResultLoc,
node: ast.Node.Index,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
const gpa = astgen.gpa;
const tree = &astgen.file.tree;
const node_datas = tree.nodes.items(.data);
const body_node = node_datas[node].lhs;
if (gz.nosuspend_node != 0) {
return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{
try astgen.errNoteNode(gz.nosuspend_node, "nosuspend block here", .{}),
});
}
if (gz.suspend_node != 0) {
return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{
try astgen.errNoteNode(gz.suspend_node, "other suspend block here", .{}),
});
}
if (body_node == 0) {
// Accepted proposal to remove block-less suspend from the language:
// https://github.com/ziglang/zig/issues/8603
// TODO: simplify the parser and make this an assert instead of
// a compile error.
return astgen.failNode(node, "suspend without a block", .{});
}
const suspend_inst = try gz.addBlock(.suspend_block, node);
try gz.instructions.append(gpa, suspend_inst);
var suspend_scope = gz.makeSubBlock(scope);
suspend_scope.suspend_node = node;
defer suspend_scope.instructions.deinit(gpa);
const body_result = try expr(&suspend_scope, &suspend_scope.base, .none, body_node);
if (!gz.refIsNoReturn(body_result)) {
_ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value);
}
try suspend_scope.setBlockBody(suspend_inst);
return gz.indexToRef(suspend_inst);
}
pub fn awaitExpr(
gz: *GenZir,
scope: *Scope,
rl: ResultLoc,
node: ast.Node.Index,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
return astgen.failNode(node, "TODO AstGen awaitExpr", .{});
}
pub fn resumeExpr(
gz: *GenZir,
scope: *Scope,
rl: ResultLoc,
node: ast.Node.Index,
) InnerError!Zir.Inst.Ref {
const astgen = gz.astgen;
return astgen.failNode(node, "TODO AstGen resumeExpr", .{});
}
pub fn fnProtoExpr(
gz: *GenZir,
scope: *Scope,
@@ -1701,6 +1777,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
.block,
.block_inline,
.block_inline_var,
.suspend_block,
.loop,
.bool_br_and,
.bool_br_or,
@@ -4090,7 +4167,9 @@ fn boolBinOp(
var rhs_scope = gz.makeSubBlock(scope);
defer rhs_scope.instructions.deinit(gz.astgen.gpa);
const rhs = try expr(&rhs_scope, &rhs_scope.base, bool_rl, node_datas[node].rhs);
_ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
if (!gz.refIsNoReturn(rhs)) {
_ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
}
try rhs_scope.setBoolBrBody(bool_br);
const block_ref = gz.indexToRef(bool_br);

View File

@@ -151,6 +151,7 @@ pub fn analyzeBody(
.bitcast => try sema.zirBitcast(block, inst),
.bitcast_result_ptr => try sema.zirBitcastResultPtr(block, inst),
.block => try sema.zirBlock(block, inst),
.suspend_block => try sema.zirSuspendBlock(block, inst),
.bool_not => try sema.zirBoolNot(block, inst),
.bool_and => try sema.zirBoolOp(block, inst, false),
.bool_or => try sema.zirBoolOp(block, inst, true),
@@ -1647,6 +1648,12 @@ fn zirCImport(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) Inn
return sema.mod.fail(&parent_block.base, src, "TODO: implement Sema.zirCImport", .{});
}
fn zirSuspendBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
return sema.mod.fail(&parent_block.base, src, "TODO: implement Sema.zirSuspendBlock", .{});
}
fn zirBlock(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
const tracy = trace(@src());
defer tracy.end();

View File

@@ -199,6 +199,9 @@ pub const Inst = struct {
block_inline,
/// Same as `block_inline` but it additionally marks a decl as being a variable.
block_inline_var,
/// Implements `suspend {...}`.
/// Uses the `pl_node` union field. Payload is `Block`.
suspend_block,
/// Boolean AND. See also `bit_and`.
/// Uses the `pl_node` union field. Payload is `Bin`.
bool_and,
@@ -975,6 +978,7 @@ pub const Inst = struct {
.block,
.block_inline,
.block_inline_var,
.suspend_block,
.loop,
.bool_br_and,
.bool_br_or,
@@ -2541,6 +2545,7 @@ const Writer = struct {
.block,
.block_inline,
.block_inline_var,
.suspend_block,
.loop,
.validate_struct_init_ptr,
.validate_array_init_ptr,