This is a temporary structure; references to it are valid only
while constructing a Zir.
const GenZir = struct
const GenZir = struct {
const base_tag: Scope.Tag = .gen_zir;
base: Scope = Scope{ .tag = base_tag },
/// Whether we're already in a scope known to be comptime. This is set
/// whenever we know Sema will analyze the current block with `is_comptime`,
/// for instance when we're within a `struct_decl` or a `block_comptime`.
is_comptime: bool,
/// Whether we're in an expression within a `@TypeOf` operand. In this case,
/// closure of runtime variables is permitted where it is usually not.
is_typeof: bool = false,
/// This is set to true for a `GenZir` of a `block_inline`, indicating that
/// exits from this block should use `break_inline` rather than `break`.
is_inline: bool = false,
/// The containing decl AST node.
decl_node_index: Ast.Node.Index,
/// The containing decl line index, absolute.
decl_line: u32,
/// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
parent: *Scope,
/// All `GenZir` scopes for the same ZIR share this.
astgen: *AstGen,
/// Keeps track of the list of instructions in this scope. Possibly shared.
/// Indexes to instructions in `astgen`.
instructions: *ArrayList(Zir.Inst.Index),
/// A sub-block may share its instructions ArrayList with containing GenZir,
/// if use is strictly nested. This saves prior size of list for unstacking.
instructions_top: usize,
label: ?Label = null,
/// If `true`, unlabeled `break` and `continue` exprs can target this `GenZir`.
allow_unlabeled_control_flow: bool = false,
/// If `label` is `null` and `unlabeled_control_flow_target` is `false`,
/// this is unused and may be `undefined`.
/// Otherwise, this is the target for a `break` instruction when a `break`
/// targets this `GenZir`.
break_target: Zir.Inst.Index = undefined,
/// If `label` is `null` and `unlabeled_control_flow_target` is `false`,
/// this is unused and may be `undefined`.
continue_target: union(enum) {
/// A `continue` cannot target this `GenZir`; emit an error.
none,
/// Emit a `break` instruction targeting this block.
@"break": Zir.Inst.Index,
/// Emit a `switch_continue` instruction targeting this `switch_block`.
switch_continue: Zir.Inst.Index,
} = undefined,
/// Only valid when setBreakResultInfo is called.
break_result_info: AstGen.ResultInfo = undefined,
/// If `continue_target` is *not* `switch_continue`, this is unused and may
/// be `undefined`.
continue_result_info: AstGen.ResultInfo = undefined,
suspend_node: Ast.Node.OptionalIndex = .none,
nosuspend_node: Ast.Node.OptionalIndex = .none,
/// Set if this GenZir is a defer.
cur_defer_node: Ast.Node.OptionalIndex = .none,
// Set if this GenZir is a defer or it is inside a defer.
any_defer_node: Ast.Node.OptionalIndex = .none,
const unstacked_top = std.math.maxInt(usize);
/// Call unstack before adding any new instructions to containing GenZir.
fn unstack(self: *GenZir) void {
if (self.instructions_top != unstacked_top) {
self.instructions.items.len = self.instructions_top;
self.instructions_top = unstacked_top;
}
}
fn isEmpty(self: *const GenZir) bool {
return (self.instructions_top == unstacked_top) or
(self.instructions.items.len == self.instructions_top);
}
fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index {
return if (self.instructions_top == unstacked_top)
&[0]Zir.Inst.Index{}
else
self.instructions.items[self.instructions_top..];
}
fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index {
return if (self.instructions_top == unstacked_top)
&[0]Zir.Inst.Index{}
else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top)
self.instructions.items[self.instructions_top..stacked_gz.instructions_top]
else
self.instructions.items[self.instructions_top..];
}
fn instructionsSliceUptoOpt(gz: *const GenZir, maybe_stacked_gz: ?*GenZir) []Zir.Inst.Index {
if (maybe_stacked_gz) |stacked_gz| {
return gz.instructionsSliceUpto(stacked_gz);
} else {
return gz.instructionsSlice();
}
}
fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
return .{
.is_comptime = gz.is_comptime,
.is_typeof = gz.is_typeof,
.decl_node_index = gz.decl_node_index,
.decl_line = gz.decl_line,
.parent = scope,
.astgen = gz.astgen,
.suspend_node = gz.suspend_node,
.nosuspend_node = gz.nosuspend_node,
.any_defer_node = gz.any_defer_node,
.instructions = gz.instructions,
.instructions_top = gz.instructions.items.len,
};
}
const Label = struct {
token: Ast.TokenIndex,
used: bool = false,
used_for_continue: bool = false,
};
/// Assumes nothing stacked on `gz`.
fn endsWithNoReturn(gz: GenZir) bool {
if (gz.isEmpty()) return false;
const tags = gz.astgen.instructions.items(.tag);
const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
return tags[@backingInt(last_inst)].isNoReturn();
}
/// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
if (inst_ref == .unreachable_value) return true;
if (inst_ref.toIndex()) |inst_index| {
return gz.astgen.instructions.items(.tag)[@backingInt(inst_index)].isNoReturn();
}
return false;
}
fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) Ast.Node.Offset {
return gz.decl_node_index.toOffset(node_index);
}
fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) Ast.TokenOffset {
return .init(gz.srcToken(), token);
}
fn srcToken(gz: GenZir) Ast.TokenIndex {
return gz.astgen.tree.firstToken(gz.decl_node_index);
}
fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void {
// Depending on whether the result location is a pointer or value, different
// ZIR needs to be generated. In the former case we rely on storing to the
// pointer to communicate the result, and use breakvoid; in the latter case
// the block break instructions will have the result values.
switch (parent_ri.rl) {
.coerced_ty => |ty_inst| {
// Type coercion needs to happen before breaks.
gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx };
},
.discard => {
// We don't forward the result context here. This prevents
// "unnecessary discard" errors from being caused by expressions
// far from the actual discard, such as a `break` from a
// discarded block.
gz.break_result_info = .{ .rl = .discard };
},
else => {
gz.break_result_info = parent_ri;
},
}
}
/// Assumes nothing stacked on `gz`. Unstacks `gz`.
fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void {
const astgen = gz.astgen;
const gpa = astgen.gpa;
const body = gz.instructionsSlice();
const body_len = astgen.countBodyLenAfterFixups(body);
try astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.BoolBr).@"struct".field_names.len + body_len,
);
const zir_datas = astgen.instructions.items(.data);
zir_datas[@backingInt(bool_br)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.BoolBr{
.lhs = bool_br_lhs,
.body_len = body_len,
});
astgen.appendBodyWithFixups(body);
gz.unstack();
}
/// Assumes nothing stacked on `gz`. Unstacks `gz`.
/// Asserts `inst` is not a `block_comptime`.
fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
const astgen = gz.astgen;
const gpa = astgen.gpa;
const body = gz.instructionsSlice();
const body_len = astgen.countBodyLenAfterFixups(body);
const zir_tags = astgen.instructions.items(.tag);
assert(zir_tags[@backingInt(inst)] != .block_comptime); // use `setComptimeBlockBody` instead
try astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.Block).@"struct".field_names.len + body_len,
);
const zir_datas = astgen.instructions.items(.data);
zir_datas[@backingInt(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
Zir.Inst.Block{ .body_len = body_len },
);
astgen.appendBodyWithFixups(body);
gz.unstack();
}
/// Assumes nothing stacked on `gz`. Unstacks `gz`.
/// Asserts `inst` is a `block_comptime`.
fn setBlockComptimeBody(gz: *GenZir, inst: Zir.Inst.Index, comptime_reason: std.zig.SimpleComptimeReason) !void {
const astgen = gz.astgen;
const gpa = astgen.gpa;
const body = gz.instructionsSlice();
const body_len = astgen.countBodyLenAfterFixups(body);
const zir_tags = astgen.instructions.items(.tag);
assert(zir_tags[@backingInt(inst)] == .block_comptime); // use `setBlockBody` instead
try astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.BlockComptime).@"struct".field_names.len + body_len,
);
const zir_datas = astgen.instructions.items(.data);
zir_datas[@backingInt(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
Zir.Inst.BlockComptime{
.reason = comptime_reason,
.body_len = body_len,
},
);
astgen.appendBodyWithFixups(body);
gz.unstack();
}
/// Assumes nothing stacked on `gz`. Unstacks `gz`.
fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
const astgen = gz.astgen;
const gpa = astgen.gpa;
const body = gz.instructionsSlice();
const body_len = astgen.countBodyLenAfterFixups(body);
try astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.Try).@"struct".field_names.len + body_len,
);
const zir_datas = astgen.instructions.items(.data);
zir_datas[@backingInt(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
Zir.Inst.Try{
.operand = operand,
.body_len = body_len,
},
);
astgen.appendBodyWithFixups(body);
gz.unstack();
}
/// Must be called with the following stack set up:
/// * gz (bottom)
/// * ret_gz
/// * cc_gz
/// * body_gz (top)
/// Unstacks all of those except for `gz`.
fn addFunc(
gz: *GenZir,
args: struct {
src_node: Ast.Node.Index,
lbrace_line: u32 = 0,
lbrace_column: u32 = 0,
param_block: Zir.Inst.Index,
ret_gz: ?*GenZir,
body_gz: ?*GenZir,
cc_gz: ?*GenZir,
ret_param_refs: []Zir.Inst.Index,
param_insts: []Zir.Inst.Index, // refs to params in `body_gz` should still be in `astgen.ref_table`
ret_ty_is_generic: bool,
cc_ref: Zir.Inst.Ref,
ret_ref: Zir.Inst.Ref,
noalias_bits: u32,
is_var_args: bool,
is_inferred_error: bool,
is_noinline: bool,
/// Ignored if `body_gz == null`.
proto_hash: std.zig.SrcHash,
},
) !Zir.Inst.Ref {
assert(args.src_node != .root);
const astgen = gz.astgen;
const gpa = astgen.gpa;
const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
try gz.instructions.ensureUnusedCapacity(gpa, 1);
try astgen.instructions.ensureUnusedCapacity(gpa, 1);
const body, const cc_body, const ret_body = bodies: {
var stacked_gz: ?*GenZir = null;
const body: []const Zir.Inst.Index = if (args.body_gz) |body_gz| body: {
const body = body_gz.instructionsSliceUptoOpt(stacked_gz);
stacked_gz = body_gz;
break :body body;
} else &.{};
const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: {
const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz);
stacked_gz = cc_gz;
break :body cc_body;
} else &.{};
const ret_body: []const Zir.Inst.Index = if (args.ret_gz) |ret_gz| body: {
const ret_body = ret_gz.instructionsSliceUptoOpt(stacked_gz);
stacked_gz = ret_gz;
break :body ret_body;
} else &.{};
break :bodies .{ body, cc_body, ret_body };
};
var src_locs_and_hash_buffer: [7]u32 = undefined;
const src_locs_and_hash: []const u32 = if (args.body_gz != null) src_locs_and_hash: {
const tree = astgen.tree;
const fn_decl = args.src_node;
const block = switch (tree.nodeTag(fn_decl)) {
.fn_decl => tree.nodeData(fn_decl).node_and_node[1],
.test_decl => tree.nodeData(fn_decl).opt_token_and_node[1],
else => unreachable,
};
const rbrace_start = tree.tokenStart(tree.lastToken(block));
astgen.advanceSourceCursor(rbrace_start);
const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line);
const rbrace_column: u32 = @intCast(astgen.source_column);
const columns = args.lbrace_column | (rbrace_column << 16);
const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash);
src_locs_and_hash_buffer = .{
args.lbrace_line,
rbrace_line,
columns,
proto_hash_arr[0],
proto_hash_arr[1],
proto_hash_arr[2],
proto_hash_arr[3],
};
break :src_locs_and_hash &src_locs_and_hash_buffer;
} else &.{};
const body_len = astgen.countBodyLenAfterFixupsExtraRefs(body, args.param_insts);
const tag: Zir.Inst.Tag, const payload_index: u32 = if (args.cc_ref != .none or
args.is_var_args or args.noalias_bits != 0 or args.is_noinline)
inst_info: {
try astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.FuncFancy).@"struct".field_names.len +
fancyFnExprExtraLen(astgen, &.{}, cc_body, args.cc_ref) +
fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
body_len + src_locs_and_hash.len +
@intFromBool(args.noalias_bits != 0),
);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
.param_block = args.param_block,
.body_len = body_len,
.bits = .{
.is_var_args = args.is_var_args,
.is_inferred_error = args.is_inferred_error,
.is_noinline = args.is_noinline,
.has_any_noalias = args.noalias_bits != 0,
.has_cc_ref = args.cc_ref != .none,
.has_ret_ty_ref = ret_ref != .none,
.has_cc_body = cc_body.len != 0,
.has_ret_ty_body = ret_body.len != 0,
.ret_ty_is_generic = args.ret_ty_is_generic,
},
});
const zir_datas = astgen.instructions.items(.data);
if (cc_body.len != 0) {
astgen.extra.appendAssumeCapacity(astgen.countBodyLenAfterFixups(cc_body));
astgen.appendBodyWithFixups(cc_body);
const break_extra = zir_datas[@backingInt(cc_body[cc_body.len - 1])].@"break".payload_index;
astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
@backingInt(new_index);
} else if (args.cc_ref != .none) {
astgen.extra.appendAssumeCapacity(@backingInt(args.cc_ref));
}
if (ret_body.len != 0) {
astgen.extra.appendAssumeCapacity(
astgen.countBodyLenAfterFixups(args.ret_param_refs) +
astgen.countBodyLenAfterFixups(ret_body),
);
astgen.appendBodyWithFixups(args.ret_param_refs);
astgen.appendBodyWithFixups(ret_body);
const break_extra = zir_datas[@backingInt(ret_body[ret_body.len - 1])].@"break".payload_index;
astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
@backingInt(new_index);
} else if (ret_ref != .none) {
astgen.extra.appendAssumeCapacity(@backingInt(ret_ref));
}
if (args.noalias_bits != 0) {
astgen.extra.appendAssumeCapacity(args.noalias_bits);
}
astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts);
astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
break :inst_info .{ .func_fancy, payload_index };
} else inst_info: {
try astgen.extra.ensureUnusedCapacity(
gpa,
@typeInfo(Zir.Inst.Func).@"struct".field_names.len + 1 +
fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
body_len + src_locs_and_hash.len,
);
const ret_body_len = if (ret_body.len != 0)
countBodyLenAfterFixups(astgen, args.ret_param_refs) + countBodyLenAfterFixups(astgen, ret_body)
else
@intFromBool(ret_ref != .none);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
.param_block = args.param_block,
.ret_ty = .{
.body_len = @intCast(ret_body_len),
.is_generic = args.ret_ty_is_generic,
},
.body_len = body_len,
});
const zir_datas = astgen.instructions.items(.data);
if (ret_body.len != 0) {
astgen.appendBodyWithFixups(args.ret_param_refs);
astgen.appendBodyWithFixups(ret_body);
const break_extra = zir_datas[@backingInt(ret_body[ret_body.len - 1])].@"break".payload_index;
astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
@backingInt(new_index);
} else if (ret_ref != .none) {
astgen.extra.appendAssumeCapacity(@backingInt(ret_ref));
}
astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts);
astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
break :inst_info .{
if (args.is_inferred_error) .func_inferred else .func,
payload_index,
};
};
// Order is important when unstacking.
if (args.body_gz) |body_gz| body_gz.unstack();
if (args.cc_gz) |cc_gz| cc_gz.unstack();
if (args.ret_gz) |ret_gz| ret_gz.unstack();
astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .pl_node = .{
.src_node = gz.nodeIndexToRelative(args.src_node),
.payload_index = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return new_index.toRef();
}
fn fancyFnExprExtraLen(astgen: *AstGen, param_refs_body: []const Zir.Inst.Index, main_body: []const Zir.Inst.Index, ref: Zir.Inst.Ref) u32 {
return countBodyLenAfterFixups(astgen, param_refs_body) +
countBodyLenAfterFixups(astgen, main_body) +
// If there is a body, we need an element for its length; otherwise, if there is a ref, we need to include that.
@intFromBool(main_body.len > 0 or ref != .none);
}
fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
return gz.add(.{
.tag = .int,
.data = .{ .int = integer },
});
}
fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !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.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
astgen.instructions.appendAssumeCapacity(.{
.tag = .int_big,
.data = .{ .str = .{
.start = @fromBackingInt(@intCast(astgen.string_bytes.items.len)),
.len = @intCast(limbs.len),
} },
});
gz.instructions.appendAssumeCapacity(new_index);
astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
return new_index.toRef();
}
fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
return gz.add(.{
.tag = .float,
.data = .{ .float = number },
});
}
fn addUnNode(
gz: *GenZir,
tag: Zir.Inst.Tag,
operand: Zir.Inst.Ref,
/// Absolute node index. This function does the conversion to offset from Decl.
src_node: Ast.Node.Index,
) !Zir.Inst.Ref {
assert(operand != .none);
return gz.add(.{
.tag = tag,
.data = .{ .un_node = .{
.operand = operand,
.src_node = gz.nodeIndexToRelative(src_node),
} },
});
}
fn makeUnNode(
gz: *GenZir,
tag: Zir.Inst.Tag,
operand: Zir.Inst.Ref,
/// Absolute node index. This function does the conversion to offset from Decl.
src_node: Ast.Node.Index,
) !Zir.Inst.Index {
assert(operand != .none);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
try gz.astgen.instructions.append(gz.astgen.gpa, .{
.tag = tag,
.data = .{ .un_node = .{
.operand = operand,
.src_node = gz.nodeIndexToRelative(src_node),
} },
});
return new_index;
}
fn addPlNode(
gz: *GenZir,
tag: Zir.Inst.Tag,
/// Absolute node index. This function does the conversion to offset from Decl.
src_node: Ast.Node.Index,
extra: anytype,
) !Zir.Inst.Ref {
const gpa = gz.astgen.gpa;
try gz.instructions.ensureUnusedCapacity(gpa, 1);
try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
const payload_index = try gz.astgen.addExtra(extra);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
gz.astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .pl_node = .{
.src_node = gz.nodeIndexToRelative(src_node),
.payload_index = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return new_index.toRef();
}
fn addPlNodePayloadIndex(
gz: *GenZir,
tag: Zir.Inst.Tag,
/// Absolute node index. This function does the conversion to offset from Decl.
src_node: Ast.Node.Index,
payload_index: u32,
) !Zir.Inst.Ref {
return try gz.add(.{
.tag = tag,
.data = .{ .pl_node = .{
.src_node = gz.nodeIndexToRelative(src_node),
.payload_index = payload_index,
} },
});
}
/// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`.
fn addParam(
gz: *GenZir,
param_gz: *GenZir,
/// Previous parameters, which might be referenced in `param_gz` (the new parameter type).
/// `ref`s of these instructions will be put into this param's type body, and removed from `AstGen.ref_table`.
prev_param_insts: []const Zir.Inst.Index,
ty_is_generic: bool,
tag: Zir.Inst.Tag,
/// Absolute token index. This function does the conversion to Decl offset.
abs_tok_index: Ast.TokenIndex,
name: Zir.NullTerminatedString,
) !Zir.Inst.Index {
const gpa = gz.astgen.gpa;
const param_body = param_gz.instructionsSlice();
const body_len = gz.astgen.countBodyLenAfterFixupsExtraRefs(param_body, prev_param_insts);
try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).@"struct".field_names.len + body_len);
const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
.name = name,
.type = .{
.body_len = @intCast(body_len),
.is_generic = ty_is_generic,
},
});
gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, param_body, prev_param_insts);
param_gz.unstack();
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
gz.astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .pl_tok = .{
.src_tok = gz.tokenIndexToRelative(abs_tok_index),
.payload_index = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return new_index;
}
fn addStdLangValue(gz: *GenZir, src_node: Ast.Node.Index, val: Zir.Inst.StdLangValue) !Zir.Inst.Ref {
return addExtendedNodeSmall(gz, .std_lang_value, src_node, @backingInt(val));
}
fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref {
return addExtendedPayloadSmall(gz, opcode, undefined, extra);
}
fn addExtendedPayloadSmall(
gz: *GenZir,
opcode: Zir.Inst.Extended,
small: u16,
extra: anytype,
) !Zir.Inst.Ref {
const gpa = gz.astgen.gpa;
try gz.instructions.ensureUnusedCapacity(gpa, 1);
try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
const payload_index = try gz.astgen.addExtra(extra);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
gz.astgen.instructions.appendAssumeCapacity(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = opcode,
.small = small,
.operand = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return new_index.toRef();
}
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".field_names.len + operands.len,
);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
.src_node = gz.nodeIndexToRelative(node),
});
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
astgen.instructions.appendAssumeCapacity(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = opcode,
.small = @intCast(operands.len),
.operand = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
astgen.appendRefsAssumeCapacity(operands);
return new_index.toRef();
}
fn addExtendedMultiOpPayloadIndex(
gz: *GenZir,
opcode: Zir.Inst.Extended,
payload_index: u32,
trailing_len: usize,
) !Zir.Inst.Ref {
const astgen = gz.astgen;
const gpa = astgen.gpa;
try gz.instructions.ensureUnusedCapacity(gpa, 1);
try astgen.instructions.ensureUnusedCapacity(gpa, 1);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
astgen.instructions.appendAssumeCapacity(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = opcode,
.small = @intCast(trailing_len),
.operand = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return new_index.toRef();
}
fn addExtendedNodeSmall(
gz: *GenZir,
opcode: Zir.Inst.Extended,
src_node: Ast.Node.Index,
small: u16,
) !Zir.Inst.Ref {
const astgen = gz.astgen;
const gpa = astgen.gpa;
try gz.instructions.ensureUnusedCapacity(gpa, 1);
try astgen.instructions.ensureUnusedCapacity(gpa, 1);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
astgen.instructions.appendAssumeCapacity(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = opcode,
.small = small,
.operand = @bitCast(@backingInt(gz.nodeIndexToRelative(src_node))),
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return new_index.toRef();
}
fn addUnTok(
gz: *GenZir,
tag: Zir.Inst.Tag,
operand: Zir.Inst.Ref,
/// Absolute token index. This function does the conversion to Decl offset.
abs_tok_index: Ast.TokenIndex,
) !Zir.Inst.Ref {
assert(operand != .none);
return gz.add(.{
.tag = tag,
.data = .{ .un_tok = .{
.operand = operand,
.src_tok = gz.tokenIndexToRelative(abs_tok_index),
} },
});
}
fn makeUnTok(
gz: *GenZir,
tag: Zir.Inst.Tag,
operand: Zir.Inst.Ref,
/// Absolute token index. This function does the conversion to Decl offset.
abs_tok_index: Ast.TokenIndex,
) !Zir.Inst.Index {
const astgen = gz.astgen;
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
assert(operand != .none);
try astgen.instructions.append(astgen.gpa, .{
.tag = tag,
.data = .{ .un_tok = .{
.operand = operand,
.src_tok = gz.tokenIndexToRelative(abs_tok_index),
} },
});
return new_index;
}
fn addStrTok(
gz: *GenZir,
tag: Zir.Inst.Tag,
str_index: Zir.NullTerminatedString,
/// Absolute token index. This function does the conversion to Decl offset.
abs_tok_index: Ast.TokenIndex,
) !Zir.Inst.Ref {
return gz.add(.{
.tag = tag,
.data = .{ .str_tok = .{
.start = str_index,
.src_tok = gz.tokenIndexToRelative(abs_tok_index),
} },
});
}
fn addSaveErrRetIndex(
gz: *GenZir,
cond: union(enum) {
always: void,
if_of_error_type: Zir.Inst.Ref,
},
) !Zir.Inst.Index {
return gz.addAsIndex(.{
.tag = .save_err_ret_index,
.data = .{ .save_err_ret_index = .{
.operand = switch (cond) {
.if_of_error_type => |x| x,
else => .none,
},
} },
});
}
const BranchTarget = union(enum) {
ret,
block: Zir.Inst.Index,
};
fn addRestoreErrRetIndex(
gz: *GenZir,
bt: BranchTarget,
cond: union(enum) {
always: void,
if_non_error: Zir.Inst.Ref,
},
src_node: Ast.Node.Index,
) !Zir.Inst.Index {
switch (cond) {
.always => return gz.addAsIndex(.{
.tag = .restore_err_ret_index_unconditional,
.data = .{ .un_node = .{
.operand = switch (bt) {
.ret => .none,
.block => |b| b.toRef(),
},
.src_node = gz.nodeIndexToRelative(src_node),
} },
}),
.if_non_error => |operand| switch (bt) {
.ret => return gz.addAsIndex(.{
.tag = .restore_err_ret_index_fn_entry,
.data = .{ .un_node = .{
.operand = operand,
.src_node = gz.nodeIndexToRelative(src_node),
} },
}),
.block => |block| return (try gz.addExtendedPayload(
.restore_err_ret_index,
Zir.Inst.RestoreErrRetIndex{
.src_node = gz.nodeIndexToRelative(src_node),
.block = block.toRef(),
.operand = operand,
},
)).toIndex().?,
},
}
}
fn addBreak(
gz: *GenZir,
tag: Zir.Inst.Tag,
block_inst: Zir.Inst.Index,
operand: Zir.Inst.Ref,
) !Zir.Inst.Index {
const gpa = gz.astgen.gpa;
try gz.instructions.ensureUnusedCapacity(gpa, 1);
const new_index = try gz.makeBreak(tag, block_inst, operand);
gz.instructions.appendAssumeCapacity(new_index);
return new_index;
}
fn makeBreak(
gz: *GenZir,
tag: Zir.Inst.Tag,
block_inst: Zir.Inst.Index,
operand: Zir.Inst.Ref,
) !Zir.Inst.Index {
return gz.makeBreakCommon(tag, block_inst, operand, null);
}
fn addBreakWithSrcNode(
gz: *GenZir,
tag: Zir.Inst.Tag,
block_inst: Zir.Inst.Index,
operand: Zir.Inst.Ref,
operand_src_node: Ast.Node.Index,
) !Zir.Inst.Index {
const gpa = gz.astgen.gpa;
try gz.instructions.ensureUnusedCapacity(gpa, 1);
const new_index = try gz.makeBreakWithSrcNode(tag, block_inst, operand, operand_src_node);
gz.instructions.appendAssumeCapacity(new_index);
return new_index;
}
fn makeBreakWithSrcNode(
gz: *GenZir,
tag: Zir.Inst.Tag,
block_inst: Zir.Inst.Index,
operand: Zir.Inst.Ref,
operand_src_node: Ast.Node.Index,
) !Zir.Inst.Index {
return gz.makeBreakCommon(tag, block_inst, operand, operand_src_node);
}
fn makeBreakCommon(
gz: *GenZir,
tag: Zir.Inst.Tag,
block_inst: Zir.Inst.Index,
operand: Zir.Inst.Ref,
operand_src_node: ?Ast.Node.Index,
) !Zir.Inst.Index {
const gpa = gz.astgen.gpa;
try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).@"struct".field_names.len);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
gz.astgen.instructions.appendAssumeCapacity(.{
.tag = tag,
.data = .{ .@"break" = .{
.operand = operand,
.payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Break{
.operand_src_node = if (operand_src_node) |src_node|
gz.nodeIndexToRelative(src_node).toOptional()
else
.none,
.block_inst = block_inst,
}),
} },
});
return new_index;
}
fn addBin(
gz: *GenZir,
tag: Zir.Inst.Tag,
lhs: Zir.Inst.Ref,
rhs: Zir.Inst.Ref,
) !Zir.Inst.Ref {
assert(lhs != .none);
assert(rhs != .none);
return gz.add(.{
.tag = tag,
.data = .{ .bin = .{
.lhs = lhs,
.rhs = rhs,
} },
});
}
fn addDefer(gz: *GenZir, index: u32, len: u32) !void {
_ = try gz.add(.{
.tag = .@"defer",
.data = .{ .@"defer" = .{
.index = index,
.len = len,
} },
});
}
fn addDecl(
gz: *GenZir,
tag: Zir.Inst.Tag,
decl_index: u32,
src_node: Ast.Node.Index,
) !Zir.Inst.Ref {
return gz.add(.{
.tag = tag,
.data = .{ .pl_node = .{
.src_node = gz.nodeIndexToRelative(src_node),
.payload_index = decl_index,
} },
});
}
fn addNode(
gz: *GenZir,
tag: Zir.Inst.Tag,
/// Absolute node index. This function does the conversion to offset from Decl.
src_node: Ast.Node.Index,
) !Zir.Inst.Ref {
return gz.add(.{
.tag = tag,
.data = .{ .node = gz.nodeIndexToRelative(src_node) },
});
}
fn addInstNode(
gz: *GenZir,
tag: Zir.Inst.Tag,
inst: Zir.Inst.Index,
/// Absolute node index. This function does the conversion to offset from Decl.
src_node: Ast.Node.Index,
) !Zir.Inst.Ref {
return gz.add(.{
.tag = tag,
.data = .{ .inst_node = .{
.inst = inst,
.src_node = gz.nodeIndexToRelative(src_node),
} },
});
}
fn addNodeExtended(
gz: *GenZir,
opcode: Zir.Inst.Extended,
/// Absolute node index. This function does the conversion to offset from Decl.
src_node: Ast.Node.Index,
) !Zir.Inst.Ref {
return gz.add(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = opcode,
.small = undefined,
.operand = @bitCast(@backingInt(gz.nodeIndexToRelative(src_node))),
} },
});
}
fn addAllocExtended(
gz: *GenZir,
args: struct {
/// Absolute node index. This function does the conversion to offset from Decl.
node: Ast.Node.Index,
type_inst: Zir.Inst.Ref,
align_inst: Zir.Inst.Ref,
is_const: bool,
is_comptime: bool,
},
) !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.AllocExtended).@"struct".field_names.len +
@intFromBool(args.type_inst != .none) +
@intFromBool(args.align_inst != .none),
);
const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
.src_node = gz.nodeIndexToRelative(args.node),
});
if (args.type_inst != .none) {
astgen.extra.appendAssumeCapacity(@backingInt(args.type_inst));
}
if (args.align_inst != .none) {
astgen.extra.appendAssumeCapacity(@backingInt(args.align_inst));
}
const has_type: u4 = @intFromBool(args.type_inst != .none);
const has_align: u4 = @intFromBool(args.align_inst != .none);
const is_const: u4 = @intFromBool(args.is_const);
const is_comptime: u4 = @intFromBool(args.is_comptime);
const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
astgen.instructions.appendAssumeCapacity(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .alloc,
.small = small,
.operand = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return new_index.toRef();
}
fn addAsm(
gz: *GenZir,
args: struct {
tag: Zir.Inst.Extended,
/// Absolute node index. This function does the conversion to offset from Decl.
node: Ast.Node.Index,
asm_source: Zir.NullTerminatedString,
output_type_bits: u32,
is_volatile: bool,
outputs: []const Zir.Inst.Asm.Output,
inputs: []const Zir.Inst.Asm.Input,
clobbers: 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.Asm).@"struct".field_names.len +
args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).@"struct".field_names.len +
args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).@"struct".field_names.len);
const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
.src_node = gz.nodeIndexToRelative(args.node),
.asm_source = args.asm_source,
.output_type_bits = args.output_type_bits,
.clobbers = args.clobbers,
});
for (args.outputs) |output| {
_ = gz.astgen.addExtraAssumeCapacity(output);
}
for (args.inputs) |input| {
_ = gz.astgen.addExtraAssumeCapacity(input);
}
const small: Zir.Inst.Asm.Small = .{
.is_volatile = args.is_volatile,
.outputs_len = @intCast(args.outputs.len),
.inputs_len = @intCast(args.inputs.len),
};
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(astgen.instructions.len));
astgen.instructions.appendAssumeCapacity(.{
.tag = .extended,
.data = .{ .extended = .{
.opcode = args.tag,
.small = @bitCast(small),
.operand = payload_index,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return new_index.toRef();
}
/// Note that this returns a `Zir.Inst.Index` not a ref.
/// Does *not* append the block instruction to the scope.
/// Leaves the `payload_index` field undefined.
fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
const gpa = gz.astgen.gpa;
try gz.astgen.instructions.append(gpa, .{
.tag = tag,
.data = .{ .pl_node = .{
.src_node = gz.nodeIndexToRelative(node),
.payload_index = undefined,
} },
});
return new_index;
}
/// Note that this returns a `Zir.Inst.Index` not a ref.
/// Does *not* append the block instruction to the scope.
/// Leaves the `payload_index` field undefined. Use `setDeclaration` to finalize.
fn makeDeclaration(gz: *GenZir, node: Ast.Node.Index) !Zir.Inst.Index {
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
try gz.astgen.instructions.append(gz.astgen.gpa, .{
.tag = .declaration,
.data = .{ .declaration = .{
.src_node = node,
.payload_index = undefined,
} },
});
return new_index;
}
/// Note that this returns a `Zir.Inst.Index` not a ref.
/// Leaves the `payload_index` field undefined.
fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
const gpa = gz.astgen.gpa;
try gz.instructions.ensureUnusedCapacity(gpa, 1);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
try gz.astgen.instructions.append(gpa, .{
.tag = tag,
.data = .{ .pl_node = .{
.src_node = gz.nodeIndexToRelative(node),
.payload_index = undefined,
} },
});
gz.instructions.appendAssumeCapacity(new_index);
return new_index;
}
fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
src_node: Ast.Node.Index,
name_strat: Zir.Inst.NameStrategy,
layout: std.lang.Type.ContainerLayout,
backing_int_type_body_len: ?u32,
decls_len: u32,
fields_len: u32,
any_field_aligns: bool,
any_field_defaults: bool,
any_comptime_fields: bool,
fields_hash: std.zig.SrcHash,
captures: []const Zir.Inst.Capture,
capture_names: []const Zir.NullTerminatedString,
/// The trailing declaration list, field information, and body instructions.
remaining: []const u32,
}) !void {
const astgen = gz.astgen;
const gpa = astgen.gpa;
// Node .root is valid for the root `struct_decl` of a file!
assert(args.src_node != .root or gz.parent.tag == .top);
const captures_len: u32 = @intCast(args.captures.len);
assert(args.capture_names.len == captures_len);
const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".field_names.len +
4 + // `captures_len`, `decls_len`, `fields_len`, `backing_int_type_body_len`
captures_len * 2 + // `capture`, `capture_name`
args.remaining.len);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{
.fields_hash_0 = fields_hash_arr[0],
.fields_hash_1 = fields_hash_arr[1],
.fields_hash_2 = fields_hash_arr[2],
.fields_hash_3 = fields_hash_arr[3],
.src_line = astgen.source_line,
.src_node = args.src_node,
});
if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len);
if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len);
if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len);
if (args.backing_int_type_body_len) |n| astgen.extra.appendAssumeCapacity(n);
astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names));
astgen.extra.appendSliceAssumeCapacity(args.remaining);
astgen.instructions.set(@backingInt(inst), .{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .struct_decl,
.small = @bitCast(Zir.Inst.StructDecl.Small{
.has_captures_len = captures_len != 0,
.has_decls_len = args.decls_len != 0,
.has_fields_len = args.fields_len != 0,
.name_strategy = args.name_strat,
.layout = args.layout,
.has_backing_int_type = args.backing_int_type_body_len != null,
.any_field_aligns = args.any_field_aligns,
.any_field_defaults = args.any_field_defaults,
.any_comptime_fields = args.any_comptime_fields,
}),
.operand = payload_index,
} },
});
}
fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
src_node: Ast.Node.Index,
name_strat: Zir.Inst.NameStrategy,
kind: Zir.Inst.UnionDecl.Kind,
arg_type_body_len: ?u32,
decls_len: u32,
fields_len: u32,
any_field_aligns: bool,
any_field_values: bool,
fields_hash: std.zig.SrcHash,
captures: []const Zir.Inst.Capture,
capture_names: []const Zir.NullTerminatedString,
/// The trailing declaration list, field information, and body instructions.
remaining: []const u32,
}) !void {
const astgen = gz.astgen;
const gpa = astgen.gpa;
assert(args.src_node != .root);
const captures_len: u32 = @intCast(args.captures.len);
assert(args.capture_names.len == captures_len);
const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".field_names.len +
4 + // `captures_len`, `decls_len`, `fields_len`, `arg_type_body_len`
captures_len * 2 + // `capture`, `capture_name`
args.remaining.len);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{
.fields_hash_0 = fields_hash_arr[0],
.fields_hash_1 = fields_hash_arr[1],
.fields_hash_2 = fields_hash_arr[2],
.fields_hash_3 = fields_hash_arr[3],
.src_line = astgen.source_line,
.src_node = args.src_node,
});
if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len);
if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len);
if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len);
if (args.kind.hasArgType()) {
astgen.extra.appendAssumeCapacity(args.arg_type_body_len.?);
} else {
assert(args.arg_type_body_len == null);
}
astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names));
astgen.extra.appendSliceAssumeCapacity(args.remaining);
astgen.instructions.set(@backingInt(inst), .{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .union_decl,
.small = @bitCast(Zir.Inst.UnionDecl.Small{
.has_captures_len = captures_len != 0,
.has_decls_len = args.decls_len != 0,
.has_fields_len = args.fields_len != 0,
.name_strategy = args.name_strat,
.kind = args.kind,
.any_field_aligns = args.any_field_aligns,
.any_field_values = args.any_field_values,
}),
.operand = payload_index,
} },
});
}
fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
src_node: Ast.Node.Index,
name_strat: Zir.Inst.NameStrategy,
tag_type_body_len: ?u32,
nonexhaustive: bool,
decls_len: u32,
fields_len: u32,
any_field_values: bool,
fields_hash: std.zig.SrcHash,
captures: []const Zir.Inst.Capture,
capture_names: []const Zir.NullTerminatedString,
/// The trailing declaration list, field information, and body instructions.
remaining: []const u32,
}) !void {
const astgen = gz.astgen;
const gpa = astgen.gpa;
assert(args.src_node != .root);
const captures_len: u32 = @intCast(args.captures.len);
assert(args.capture_names.len == captures_len);
const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".field_names.len +
4 + // `captures_len`, `decls_len`, `fields_len`, `tag_type_body_len`
captures_len * 2 + // `capture`, `capture_name`
args.remaining.len);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{
.fields_hash_0 = fields_hash_arr[0],
.fields_hash_1 = fields_hash_arr[1],
.fields_hash_2 = fields_hash_arr[2],
.fields_hash_3 = fields_hash_arr[3],
.src_line = astgen.source_line,
.src_node = args.src_node,
});
if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len);
if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len);
if (args.fields_len != 0) astgen.extra.appendAssumeCapacity(args.fields_len);
if (args.tag_type_body_len) |n| astgen.extra.appendAssumeCapacity(n);
astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names));
astgen.extra.appendSliceAssumeCapacity(args.remaining);
astgen.instructions.set(@backingInt(inst), .{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .enum_decl,
.small = @bitCast(Zir.Inst.EnumDecl.Small{
.has_captures_len = captures_len != 0,
.has_decls_len = args.decls_len != 0,
.has_fields_len = args.fields_len != 0,
.name_strategy = args.name_strat,
.has_tag_type = args.tag_type_body_len != null,
.nonexhaustive = args.nonexhaustive,
.any_field_values = args.any_field_values,
}),
.operand = payload_index,
} },
});
}
fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
src_node: Ast.Node.Index,
name_strat: Zir.Inst.NameStrategy,
decls_len: u32,
captures: []const Zir.Inst.Capture,
capture_names: []const Zir.NullTerminatedString,
decls: []const Zir.Inst.Index,
}) !void {
const astgen = gz.astgen;
const gpa = astgen.gpa;
assert(args.src_node != .root);
const captures_len: u32 = @intCast(args.captures.len);
assert(args.capture_names.len == captures_len);
try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).@"struct".field_names.len +
2 + // `captures_len`, `decls_len`
captures_len * 2 + // `capture`, `capture_name`
args.decls.len);
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{
.src_line = astgen.source_line,
.src_node = args.src_node,
});
if (captures_len != 0) astgen.extra.appendAssumeCapacity(captures_len);
if (args.decls_len != 0) astgen.extra.appendAssumeCapacity(args.decls_len);
astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.captures));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.capture_names));
astgen.extra.appendSliceAssumeCapacity(@ptrCast(args.decls));
astgen.instructions.set(@backingInt(inst), .{
.tag = .extended,
.data = .{ .extended = .{
.opcode = .opaque_decl,
.small = @bitCast(Zir.Inst.OpaqueDecl.Small{
.has_captures_len = captures_len != 0,
.has_decls_len = args.decls_len != 0,
.name_strategy = args.name_strat,
}),
.operand = payload_index,
} },
});
}
fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
return (try gz.addAsIndex(inst)).toRef();
}
fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
const gpa = gz.astgen.gpa;
try gz.instructions.ensureUnusedCapacity(gpa, 1);
try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
gz.astgen.instructions.appendAssumeCapacity(inst);
gz.instructions.appendAssumeCapacity(new_index);
return new_index;
}
fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index {
const gpa = gz.astgen.gpa;
try gz.instructions.ensureUnusedCapacity(gpa, 1);
try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
const new_index: Zir.Inst.Index = @fromBackingInt(@intCast(gz.astgen.instructions.len));
gz.astgen.instructions.len += 1;
gz.instructions.appendAssumeCapacity(new_index);
return new_index;
}
fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
switch (ri.rl) {
.ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node),
.coerced_ty => _ = try gz.addUnNode(.ret_node, operand, node),
else => unreachable,
}
}
fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void {
if (gz.is_comptime) return;
_ = try gz.add(.{ .tag = tag, .data = .{
.str_op = .{
.str = name,
.operand = inst,
},
} });
}
}