Commit 9a271347fe

Andrew Kelley <andrew@ziglang.org>
2021-04-24 05:09:56
AstGen: implement suspend blocks
1 parent b40a8ef
Changed files (3)
src/AstGen.zig
@@ -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);
src/Sema.zig
@@ -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();
src/Zir.zig
@@ -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,