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`.
This commit is contained in:
@@ -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|
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
42
src/Sema.zig
42
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| {
|
||||
|
||||
70
src/Zir.zig
70
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;
|
||||
|
||||
Reference in New Issue
Block a user