Commit fbfae832ea

Andrew Kelley <andrew@ziglang.org>
2021-04-24 06:12:29
AstGen: emit nosuspend function calls
Inside a nosuspend block, emit function calls as nosuspend calls. Also inside a comptime block, emit function calls as comptime calls. Also emit `async foo()` calls as async calls. Remove compile error for `nosuspend` block inside `suspend` block. Instead of implicitly treating every `suspend` block also as a `nosuspend` block (which would make sense), we leave suspension points as compile errors, to hint to the programmer about accidents. Of course they may then assert `nosuspend` by introducing a block within their suspend block. To make room in `Zir.Inst.Tag` I moved `typeof_peer` and `compile_log` to `Extended`.
1 parent 49be888
src/AstGen.zig
@@ -900,11 +900,6 @@ pub fn nosuspendExpr(
             try astgen.errNoteNode(gz.nosuspend_node, "other nosuspend block here", .{}),
         });
     }
-    if (gz.suspend_node != 0) {
-        return astgen.failNodeNotes(node, "inside a suspend block, nosuspend is implied", .{}, &[_]u32{
-            try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}),
-        });
-    }
     gz.nosuspend_node = node;
     const result = try expr(gz, scope, rl, body_node);
     gz.nosuspend_node = 0;
@@ -1803,6 +1798,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
             .bool_and,
             .bool_or,
             .call_compile_time,
+            .call_nosuspend,
+            .call_async,
             .cmp_lt,
             .cmp_lte,
             .cmp_eq,
@@ -1876,7 +1873,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
             .slice_end,
             .slice_sentinel,
             .import,
-            .typeof_peer,
             .switch_block,
             .switch_block_multi,
             .switch_block_else,
@@ -2001,7 +1997,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
             .ensure_result_non_error,
             .@"export",
             .set_eval_branch_quota,
-            .compile_log,
             .ensure_err_payload_void,
             .@"break",
             .break_inline,
@@ -6074,11 +6069,7 @@ fn typeOf(
         items[param_i] = try expr(gz, scope, .none, param);
     }
 
-    const result = try gz.addPlNode(.typeof_peer, node, Zir.Inst.MultiOp{
-        .operands_len = @intCast(u32, params.len),
-    });
-    try gz.astgen.appendRefs(items);
-
+    const result = try gz.addExtendedMultiOp(.typeof_peer, node, items);
     return rvalue(gz, scope, rl, result, node);
 }
 
@@ -6138,10 +6129,7 @@ fn builtinCall(
 
             for (params) |param, i| arg_refs[i] = try expr(gz, scope, .none, param);
 
-            const result = try gz.addPlNode(.compile_log, node, Zir.Inst.MultiOp{
-                .operands_len = @intCast(u32, params.len),
-            });
-            try gz.astgen.appendRefs(arg_refs);
+            const result = try gz.addExtendedMultiOp(.compile_log, node, arg_refs);
             return rvalue(gz, scope, rl, result, node);
         },
         .field => {
@@ -6745,9 +6733,6 @@ fn callExpr(
     call: ast.full.Call,
 ) InnerError!Zir.Inst.Ref {
     const astgen = gz.astgen;
-    if (call.async_token) |async_token| {
-        return astgen.failTok(async_token, "async and related features are not yet supported", .{});
-    }
     const lhs = try expr(gz, scope, .none, call.ast.fn_expr);
 
     const args = try astgen.gpa.alloc(Zir.Inst.Ref, call.ast.params.len);
@@ -6764,9 +6749,17 @@ fn callExpr(
         args[i] = try expr(gz, scope, .{ .ty = param_type }, param_node);
     }
 
-    const modifier: std.builtin.CallOptions.Modifier = switch (call.async_token != null) {
-        true => .async_kw,
-        false => .auto,
+    const modifier: std.builtin.CallOptions.Modifier = blk: {
+        if (gz.force_comptime) {
+            break :blk .compile_time;
+        }
+        if (call.async_token != null) {
+            break :blk .async_kw;
+        }
+        if (gz.nosuspend_node != 0) {
+            break :blk .no_async;
+        }
+        break :blk .auto;
     };
     const result: Zir.Inst.Ref = res: {
         const tag: Zir.Inst.Tag = switch (modifier) {
@@ -6774,10 +6767,10 @@ fn callExpr(
                 true => break :res try gz.addUnNode(.call_none, lhs, node),
                 false => .call,
             },
-            .async_kw => return astgen.failNode(node, "async and related features are not yet supported", .{}),
+            .async_kw => .call_async,
             .never_tail => unreachable,
             .never_inline => unreachable,
-            .no_async => return astgen.failNode(node, "async and related features are not yet supported", .{}),
+            .no_async => .call_nosuspend,
             .always_tail => unreachable,
             .always_inline => unreachable,
             .compile_time => .call_compile_time,
src/Module.zig
@@ -1533,6 +1533,39 @@ pub const Scope = struct {
             return gz.indexToRef(new_index);
         }
 
+        pub fn addExtendedMultiOp(
+            gz: *GenZir,
+            opcode: Zir.Inst.Extended,
+            node: ast.Node.Index,
+            operands: []const Zir.Inst.Ref,
+        ) !Zir.Inst.Ref {
+            const astgen = gz.astgen;
+            const gpa = astgen.gpa;
+
+            try gz.instructions.ensureUnusedCapacity(gpa, 1);
+            try astgen.instructions.ensureUnusedCapacity(gpa, 1);
+            try astgen.extra.ensureUnusedCapacity(
+                gpa,
+                @typeInfo(Zir.Inst.NodeMultiOp).Struct.fields.len + operands.len,
+            );
+
+            const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
+                .src_node = gz.nodeIndexToRelative(node),
+            });
+            const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
+            astgen.instructions.appendAssumeCapacity(.{
+                .tag = .extended,
+                .data = .{ .extended = .{
+                    .opcode = opcode,
+                    .small = @intCast(u16, operands.len),
+                    .operand = payload_index,
+                } },
+            });
+            gz.instructions.appendAssumeCapacity(new_index);
+            astgen.appendRefsAssumeCapacity(operands);
+            return gz.indexToRef(new_index);
+        }
+
         pub fn addArrayTypeSentinel(
             gz: *GenZir,
             len: Zir.Inst.Ref,
src/Sema.zig
@@ -161,6 +161,8 @@ pub fn analyzeBody(
             .call                         => try sema.zirCall(block, inst, .auto, false),
             .call_chkused                 => try sema.zirCall(block, inst, .auto, true),
             .call_compile_time            => try sema.zirCall(block, inst, .compile_time, false),
+            .call_nosuspend               => try sema.zirCall(block, inst, .no_async, false),
+            .call_async                   => try sema.zirCall(block, inst, .async_kw, false),
             .call_none                    => try sema.zirCallNone(block, inst, false),
             .call_none_chkused            => try sema.zirCallNone(block, inst, true),
             .cmp_eq                       => try sema.zirCmp(block, inst, .eq),
@@ -254,7 +256,6 @@ pub fn analyzeBody(
             .bit_size_of                  => try sema.zirBitSizeOf(block, inst),
             .typeof                       => try sema.zirTypeof(block, inst),
             .typeof_elem                  => try sema.zirTypeofElem(block, inst),
-            .typeof_peer                  => try sema.zirTypeofPeer(block, inst),
             .log2_int_type                => try sema.zirLog2IntType(block, inst),
             .typeof_log2_int_type         => try sema.zirTypeofLog2IntType(block, inst),
             .xor                          => try sema.zirBitwise(block, inst, .xor),
@@ -403,10 +404,6 @@ pub fn analyzeBody(
                 try sema.zirEnsureResultUsed(block, inst);
                 continue;
             },
-            .compile_log => {
-                try sema.zirCompileLog(block, inst);
-                continue;
-            },
             .set_eval_branch_quota => {
                 try sema.zirSetEvalBranchQuota(block, inst);
                 continue;
@@ -519,6 +516,8 @@ fn zirExtended(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
         .alloc              => return sema.zirAllocExtended(   block, extended),
         .builtin_extern     => return sema.zirBuiltinExtern(   block, extended),
         .@"asm"             => return sema.zirAsm(             block, extended),
+        .typeof_peer        => return sema.zirTypeofPeer(      block, extended),
+        .compile_log        => return sema.zirCompileLog(      block, extended),
         .c_undef            => return sema.zirCUndef(          block, extended),
         .c_include          => return sema.zirCInclude(        block, extended),
         .c_define           => return sema.zirCDefine(         block, extended),
@@ -1533,14 +1532,18 @@ fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
     return sema.mod.fail(&block.base, src, "{s}", .{msg});
 }
 
-fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
+fn zirCompileLog(
+    sema: *Sema,
+    block: *Scope.Block,
+    extended: Zir.Inst.Extended.InstData,
+) InnerError!*Inst {
     var managed = sema.mod.compile_log_text.toManaged(sema.gpa);
     defer sema.mod.compile_log_text = managed.moveToUnmanaged();
     const writer = managed.writer();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
-    const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
-    const args = sema.code.refSlice(extra.end, extra.data.operands_len);
+    const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
+    const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
+    const args = sema.code.refSlice(extra.end, extended.small);
 
     for (args) |arg_ref, i| {
         if (i != 0) try writer.print(", ", .{});
@@ -1556,8 +1559,12 @@ fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
 
     const gop = try sema.mod.compile_log_decls.getOrPut(sema.gpa, sema.owner_decl);
     if (!gop.found_existing) {
-        gop.entry.value = inst_data.src().toSrcLoc(&block.base);
+        gop.entry.value = src.toSrcLoc(&block.base);
     }
+    return sema.mod.constInst(sema.arena, src, .{
+        .ty = Type.initTag(.void),
+        .val = Value.initTag(.void_value),
+    });
 }
 
 fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index {
@@ -4680,16 +4687,19 @@ fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE
     return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirLog2IntType", .{});
 }
 
-fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
+fn zirTypeofPeer(
+    sema: *Sema,
+    block: *Scope.Block,
+    extended: Zir.Inst.Extended.InstData,
+) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
-    const src = inst_data.src();
-    const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
-    const args = sema.code.refSlice(extra.end, extra.data.operands_len);
+    const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand);
+    const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
+    const args = sema.code.refSlice(extra.end, extended.small);
 
-    const inst_list = try sema.gpa.alloc(*ir.Inst, extra.data.operands_len);
+    const inst_list = try sema.gpa.alloc(*ir.Inst, args.len);
     defer sema.gpa.free(inst_list);
 
     for (args) |arg_ref, i| {
src/Zir.zig
@@ -238,6 +238,10 @@ pub const Inst = struct {
         call_chkused,
         /// Same as `call` but with modifier `.compile_time`.
         call_compile_time,
+        /// Same as `call` but with modifier `.no_suspend`.
+        call_nosuspend,
+        /// Same as `call` but with modifier `.async_kw`.
+        call_async,
         /// Function call with modifier `.auto`, empty parameter list.
         /// Uses the `un_node` field. Operand is callee. AST node is the function call.
         call_none,
@@ -266,10 +270,6 @@ pub const Inst = struct {
         /// Uses the `bin` union field.
         /// LHS is destination element type, RHS is result pointer.
         coerce_result_ptr,
-        /// Log compile time variables and emit an error message.
-        /// Uses the `pl_node` union field. The AST node is the compile log builtin call.
-        /// The payload is `MultiOp`.
-        compile_log,
         /// Conditional branch. Splits control flow based on a boolean condition value.
         /// Uses the `pl_node` union field. AST node is an if, while, for, etc.
         /// Payload is `CondBr`.
@@ -523,10 +523,6 @@ pub const Inst = struct {
         /// Given a value which is a pointer, returns the element type.
         /// Uses the `un_node` field.
         typeof_elem,
-        /// The builtin `@TypeOf` which returns the type after Peer Type Resolution
-        /// of one or more params.
-        /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`.
-        typeof_peer,
         /// Given a value, look at the type of it, which must be an integer type.
         /// Returns the integer type for the RHS of a shift operation.
         /// Uses the `un_node` field.
@@ -990,6 +986,8 @@ pub const Inst = struct {
                 .call,
                 .call_chkused,
                 .call_compile_time,
+                .call_nosuspend,
+                .call_async,
                 .call_none,
                 .call_none_chkused,
                 .cmp_lt,
@@ -1085,12 +1083,10 @@ pub const Inst = struct {
                 .slice_end,
                 .slice_sentinel,
                 .import,
-                .typeof_peer,
                 .typeof_log2_int_type,
                 .log2_int_type,
                 .resolve_inferred_alloc,
                 .set_eval_branch_quota,
-                .compile_log,
                 .switch_capture,
                 .switch_capture_ref,
                 .switch_capture_multi,
@@ -1268,6 +1264,17 @@ pub const Inst = struct {
         ///  * 0bX0000000_00000000 - is volatile
         /// `operand` is payload index to `Asm`.
         @"asm",
+        /// Log compile time variables and emit an error message.
+        /// `operand` is payload index to `NodeMultiOp`.
+        /// `small` is `operands_len`.
+        /// The AST node is the compile log builtin call.
+        compile_log,
+        /// The builtin `@TypeOf` which returns the type after Peer Type Resolution
+        /// of one or more params.
+        /// `operand` is payload index to `NodeMultiOp`.
+        /// `small` is `operands_len`.
+        /// The AST node is the builtin call.
+        typeof_peer,
         /// `operand` is payload index to `UnNode`.
         c_undef,
         /// `operand` is payload index to `UnNode`.
@@ -1897,6 +1904,11 @@ pub const Inst = struct {
         operands_len: u32,
     };
 
+    /// Trailing: operand: Ref, // for each `operands_len` (stored in `small`).
+    pub const NodeMultiOp = struct {
+        src_node: i32,
+    };
+
     /// This data is stored inside extra, with trailing operands according to `body_len`.
     /// Each operand is an `Index`.
     pub const Block = struct {
@@ -2540,6 +2552,8 @@ const Writer = struct {
             .call,
             .call_chkused,
             .call_compile_time,
+            .call_nosuspend,
+            .call_async,
             => try self.writePlNodeCall(stream, inst),
 
             .block,
@@ -2584,10 +2598,6 @@ const Writer = struct {
             .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
             .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
 
-            .compile_log,
-            .typeof_peer,
-            => try self.writePlNodeMultiOp(stream, inst),
-
             .field_ptr,
             .field_val,
             => try self.writePlNodeField(stream, inst),
@@ -2646,6 +2656,10 @@ const Writer = struct {
             .@"asm" => try self.writeAsm(stream, extended),
             .func => try self.writeFuncExtended(stream, extended),
 
+            .compile_log,
+            .typeof_peer,
+            => try self.writeNodeMultiOp(stream, extended),
+
             .alloc,
             .builtin_extern,
             .c_undef,
@@ -2836,6 +2850,19 @@ const Writer = struct {
         try self.writeSrc(stream, inst_data.src());
     }
 
+    fn writeNodeMultiOp(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void {
+        const extra = self.code.extraData(Inst.NodeMultiOp, extended.operand);
+        const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
+        const operands = self.code.refSlice(extra.end, extended.small);
+
+        for (operands) |operand, i| {
+            if (i != 0) try stream.writeAll(", ");
+            try self.writeInstRef(stream, operand);
+        }
+        try stream.writeAll(")) ");
+        try self.writeSrc(stream, src);
+    }
+
     fn writeAsm(self: *Writer, stream: anytype, extended: Inst.Extended.InstData) !void {
         const extra = self.code.extraData(Inst.Asm, extended.operand);
         const src: LazySrcLoc = .{ .node_offset = extra.data.src_node };
@@ -2902,7 +2929,7 @@ const Writer = struct {
                 }
             }
         }
-        try stream.writeAll(") ");
+        try stream.writeAll(")) ");
         try self.writeSrc(stream, src);
     }
 
@@ -3457,19 +3484,6 @@ const Writer = struct {
         try self.writeSrc(stream, inst_data.src());
     }
 
-    fn writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
-        const extra = self.code.extraData(Inst.MultiOp, inst_data.payload_index);
-        const operands = self.code.refSlice(extra.end, extra.data.operands_len);
-
-        for (operands) |operand, i| {
-            if (i != 0) try stream.writeAll(", ");
-            try self.writeInstRef(stream, operand);
-        }
-        try stream.writeAll(") ");
-        try self.writeSrc(stream, inst_data.src());
-    }
-
     fn writePlNodeField(self: *Writer, stream: anytype, inst: Inst.Index) !void {
         const inst_data = self.code.instructions.items(.data)[inst].pl_node;
         const extra = self.code.extraData(Inst.Field, inst_data.payload_index).data;
BRANCH_TODO
@@ -37,7 +37,6 @@
  * when handling decls, catch the error and continue, so that
    AstGen can report more than one compile error.
 
- * AstGen: inside a nosuspend block, emit function calls as nosuspend calls
  * AstGen: add result location pointers to function calls
 
     const container_name_hash: Scope.NameHash = if (found_pkg) |pkg|