mirror of
https://github.com/ziglang/zig.git
synced 2025-12-31 10:33:19 +00:00
stage2: rework AstGen for switch expressions
The switch_br ZIR instructions are now switch_block instructions. This avoids a pointless block always surrounding a switchbr in emitted ZIR code. Introduce typeof_elem ZIR instruction for getting the type of the element of a pointer value in 1 instruction. Change typeof to be un_node, not un_tok. Introduce switch_capture ZIR instructions for obtaining the capture value of switch prongs. Introduce Sema.resolveBody for when you want to extract a *Inst out of a block and you know that there is only going to be 1 break from it. What's not working yet: AstGen does not correctly elide store instructions when it turns out that the result location does not need to be used as a pointer. Also Sema validation code for duplicate switch items is not yet implemented.
This commit is contained in:
parent
195ddab2be
commit
2a1dd174cd
44
BRANCH_TODO
44
BRANCH_TODO
@ -82,47 +82,3 @@ Performance optimizations to look into:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fn switchCaseExpr(
|
||||
gz: *GenZir,
|
||||
scope: *Scope,
|
||||
rl: ResultLoc,
|
||||
block: *zir.Inst.Block,
|
||||
case: ast.full.SwitchCase,
|
||||
target: zir.Inst.Ref,
|
||||
) !void {
|
||||
const tree = gz.tree();
|
||||
const node_datas = tree.nodes.items(.data);
|
||||
const main_tokens = tree.nodes.items(.main_token);
|
||||
const token_tags = tree.tokens.items(.tag);
|
||||
|
||||
const case_src = token_starts[case.ast.arrow_token];
|
||||
const sub_scope = blk: {
|
||||
const payload_token = case.payload_token orelse break :blk scope;
|
||||
const ident = if (token_tags[payload_token] == .asterisk)
|
||||
payload_token + 1
|
||||
else
|
||||
payload_token;
|
||||
const is_ptr = ident != payload_token;
|
||||
const value_name = tree.tokenSlice(ident);
|
||||
if (mem.eql(u8, value_name, "_")) {
|
||||
if (is_ptr) {
|
||||
return mod.failTok(scope, payload_token, "pointer modifier invalid on discard", .{});
|
||||
}
|
||||
break :blk scope;
|
||||
}
|
||||
return mod.failTok(scope, ident, "TODO implement switch value payload", .{});
|
||||
};
|
||||
|
||||
const case_body = try expr(gz, sub_scope, rl, case.ast.target_expr);
|
||||
if (!case_body.tag.isNoReturn()) {
|
||||
_ = try addZIRInst(mod, sub_scope, case_src, zir.Inst.Break, .{
|
||||
.block = block,
|
||||
.operand = case_body,
|
||||
}, .{});
|
||||
}
|
||||
}
|
||||
|
||||
399
src/AstGen.zig
399
src/AstGen.zig
@ -22,6 +22,7 @@ const Scope = Module.Scope;
|
||||
const GenZir = Scope.GenZir;
|
||||
const InnerError = Module.InnerError;
|
||||
const Decl = Module.Decl;
|
||||
const LazySrcLoc = Module.LazySrcLoc;
|
||||
const BuiltinFn = @import("BuiltinFn.zig");
|
||||
|
||||
instructions: std.MultiArrayList(zir.Inst) = .{},
|
||||
@ -1215,6 +1216,7 @@ fn blockExprStmts(
|
||||
.negate,
|
||||
.negate_wrap,
|
||||
.typeof,
|
||||
.typeof_elem,
|
||||
.xor,
|
||||
.optional_type,
|
||||
.optional_type_from_ptr_elem,
|
||||
@ -1243,6 +1245,24 @@ fn blockExprStmts(
|
||||
.slice_sentinel,
|
||||
.import,
|
||||
.typeof_peer,
|
||||
.switch_block,
|
||||
.switch_block_multi,
|
||||
.switch_block_else,
|
||||
.switch_block_else_multi,
|
||||
.switch_block_under,
|
||||
.switch_block_under_multi,
|
||||
.switch_block_ref,
|
||||
.switch_block_ref_multi,
|
||||
.switch_block_ref_else,
|
||||
.switch_block_ref_else_multi,
|
||||
.switch_block_ref_under,
|
||||
.switch_block_ref_under_multi,
|
||||
.switch_capture,
|
||||
.switch_capture_ref,
|
||||
.switch_capture_multi,
|
||||
.switch_capture_multi_ref,
|
||||
.switch_capture_else,
|
||||
.switch_capture_else_ref,
|
||||
=> break :b false,
|
||||
|
||||
// ZIR instructions that are always either `noreturn` or `void`.
|
||||
@ -1257,18 +1277,6 @@ fn blockExprStmts(
|
||||
.break_inline,
|
||||
.condbr,
|
||||
.condbr_inline,
|
||||
.switch_br,
|
||||
.switch_br_multi,
|
||||
.switch_br_else,
|
||||
.switch_br_else_multi,
|
||||
.switch_br_under,
|
||||
.switch_br_under_multi,
|
||||
.switch_br_ref,
|
||||
.switch_br_ref_multi,
|
||||
.switch_br_ref_else,
|
||||
.switch_br_ref_else_multi,
|
||||
.switch_br_ref_under,
|
||||
.switch_br_ref_under_multi,
|
||||
.compile_error,
|
||||
.ret_node,
|
||||
.ret_tok,
|
||||
@ -1543,7 +1551,7 @@ fn assignOp(
|
||||
|
||||
const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
|
||||
const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
|
||||
const lhs_type = try gz.addUnTok(.typeof, lhs, infix_node);
|
||||
const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
|
||||
const rhs = try expr(gz, scope, .{ .ty = lhs_type }, node_datas[infix_node].rhs);
|
||||
|
||||
const result = try gz.addPlNode(op_inst_tag, infix_node, zir.Inst.Bin{
|
||||
@ -2548,15 +2556,28 @@ fn switchExpr(
|
||||
rl: ResultLoc,
|
||||
switch_node: ast.Node.Index,
|
||||
) InnerError!zir.Inst.Ref {
|
||||
const astgen = parent_gz.astgen;
|
||||
const mod = astgen.mod;
|
||||
const gpa = mod.gpa;
|
||||
const tree = parent_gz.tree();
|
||||
const node_datas = tree.nodes.items(.data);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
const main_tokens = tree.nodes.items(.main_token);
|
||||
const token_tags = tree.tokens.items(.tag);
|
||||
const operand_node = node_datas[switch_node].lhs;
|
||||
const extra = tree.extraData(node_datas[switch_node].rhs, ast.Node.SubRange);
|
||||
const case_nodes = tree.extra_data[extra.start..extra.end];
|
||||
|
||||
// We perform two passes over the AST. This first pass is to collect information
|
||||
// for the following variables, make note of the special prong AST node index,
|
||||
// and bail out with a compile error if there are multiple special prongs present.
|
||||
var any_payload_is_ref = false;
|
||||
var scalar_cases_len: u32 = 0;
|
||||
var multi_cases_len: u32 = 0;
|
||||
var special_prong: zir.SpecialProng = .none;
|
||||
var special_node: ast.Node.Index = 0;
|
||||
var else_src: ?LazySrcLoc = null;
|
||||
var underscore_src: ?LazySrcLoc = null;
|
||||
for (case_nodes) |case_node| {
|
||||
const case = switch (node_tags[case_node]) {
|
||||
.switch_case_one => tree.switchCaseOne(case_node),
|
||||
@ -2568,22 +2589,346 @@ fn switchExpr(
|
||||
any_payload_is_ref = true;
|
||||
}
|
||||
}
|
||||
// Check for else/`_` prong.
|
||||
if (case.ast.values.len == 0) {
|
||||
const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1);
|
||||
if (else_src) |src| {
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(
|
||||
scope,
|
||||
case_src,
|
||||
"multiple else prongs in switch expression",
|
||||
.{},
|
||||
);
|
||||
errdefer msg.destroy(gpa);
|
||||
try mod.errNote(scope, src, msg, "previous else prong is here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return mod.failWithOwnedErrorMsg(scope, msg);
|
||||
} else if (underscore_src) |some_underscore| {
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(
|
||||
scope,
|
||||
parent_gz.nodeSrcLoc(switch_node),
|
||||
"else and '_' prong in switch expression",
|
||||
.{},
|
||||
);
|
||||
errdefer msg.destroy(gpa);
|
||||
try mod.errNote(scope, case_src, msg, "else prong is here", .{});
|
||||
try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return mod.failWithOwnedErrorMsg(scope, msg);
|
||||
}
|
||||
special_node = case_node;
|
||||
special_prong = .@"else";
|
||||
else_src = case_src;
|
||||
continue;
|
||||
} else if (case.ast.values.len == 1 and
|
||||
node_tags[case.ast.values[0]] == .identifier and
|
||||
mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
|
||||
{
|
||||
const case_src = parent_gz.tokSrcLoc(case.ast.arrow_token - 1);
|
||||
if (underscore_src) |src| {
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(
|
||||
scope,
|
||||
case_src,
|
||||
"multiple '_' prongs in switch expression",
|
||||
.{},
|
||||
);
|
||||
errdefer msg.destroy(gpa);
|
||||
try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return mod.failWithOwnedErrorMsg(scope, msg);
|
||||
} else if (else_src) |some_else| {
|
||||
const msg = msg: {
|
||||
const msg = try mod.errMsg(
|
||||
scope,
|
||||
parent_gz.nodeSrcLoc(switch_node),
|
||||
"else and '_' prong in switch expression",
|
||||
.{},
|
||||
);
|
||||
errdefer msg.destroy(gpa);
|
||||
try mod.errNote(scope, some_else, msg, "else prong is here", .{});
|
||||
try mod.errNote(scope, case_src, msg, "'_' prong is here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return mod.failWithOwnedErrorMsg(scope, msg);
|
||||
}
|
||||
special_node = case_node;
|
||||
special_prong = .under;
|
||||
underscore_src = case_src;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (case.ast.values.len == 1 and
|
||||
getRangeNode(node_tags, node_datas, case.ast.values[0]) == null)
|
||||
{
|
||||
scalar_cases_len += 1;
|
||||
} else {
|
||||
multi_cases_len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
const rl_and_tag: struct { rl: ResultLoc, tag: zir.Inst.Tag } = if (any_payload_is_ref) .{
|
||||
.rl = .ref,
|
||||
.tag = .switch_br_ref,
|
||||
} else .{
|
||||
.rl = .none,
|
||||
.tag = .switch_br,
|
||||
};
|
||||
const operand = try expr(parent_gz, scope, rl_and_tag.rl, operand_node);
|
||||
const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
|
||||
const operand = try expr(parent_gz, scope, operand_rl, operand_node);
|
||||
// We need the type of the operand to use as the result location for all the prong items.
|
||||
const typeof_tag: zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof;
|
||||
const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node);
|
||||
const item_rl: ResultLoc = .{ .ty = operand_ty_inst };
|
||||
|
||||
const result = try parent_gz.addPlNode(.switch_br, switch_node, zir.Inst.SwitchBr{
|
||||
.operand = operand,
|
||||
.cases_len = 0,
|
||||
});
|
||||
return rvalue(parent_gz, scope, rl, result, switch_node);
|
||||
// Contains the data that goes into the `extra` array for the SwitchBr/SwitchBrMulti.
|
||||
// This is the header as well as the optional else prong body, as well as all the
|
||||
// scalar cases.
|
||||
// At the end we will memcpy this into place.
|
||||
var scalar_cases_payload = std.ArrayListUnmanaged(u32){};
|
||||
defer scalar_cases_payload.deinit(gpa);
|
||||
// Same deal, but this is only the `extra` data for the multi cases.
|
||||
var multi_cases_payload = std.ArrayListUnmanaged(u32){};
|
||||
defer multi_cases_payload.deinit(gpa);
|
||||
|
||||
var block_scope: GenZir = .{
|
||||
.parent = scope,
|
||||
.astgen = astgen,
|
||||
.force_comptime = parent_gz.force_comptime,
|
||||
.instructions = .{},
|
||||
};
|
||||
block_scope.setBreakResultLoc(rl);
|
||||
defer block_scope.instructions.deinit(gpa);
|
||||
|
||||
// This gets added to the parent block later, after the item expressions.
|
||||
const switch_block = try parent_gz.addBlock(undefined, switch_node);
|
||||
|
||||
// We re-use this same scope for all cases, including the special prong, if any.
|
||||
var case_scope: GenZir = .{
|
||||
.parent = &block_scope.base,
|
||||
.astgen = astgen,
|
||||
.force_comptime = parent_gz.force_comptime,
|
||||
.instructions = .{},
|
||||
};
|
||||
defer case_scope.instructions.deinit(gpa);
|
||||
|
||||
// Do the else/`_` first because it goes first in the payload.
|
||||
var capture_val_scope: Scope.LocalVal = undefined;
|
||||
if (special_node != 0) {
|
||||
const case = switch (node_tags[special_node]) {
|
||||
.switch_case_one => tree.switchCaseOne(special_node),
|
||||
.switch_case => tree.switchCase(special_node),
|
||||
else => unreachable,
|
||||
};
|
||||
const sub_scope = blk: {
|
||||
const payload_token = case.payload_token orelse break :blk &case_scope.base;
|
||||
const ident = if (token_tags[payload_token] == .asterisk)
|
||||
payload_token + 1
|
||||
else
|
||||
payload_token;
|
||||
const is_ptr = ident != payload_token;
|
||||
if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
|
||||
if (is_ptr) {
|
||||
return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{});
|
||||
}
|
||||
break :blk &case_scope.base;
|
||||
}
|
||||
const capture_tag: zir.Inst.Tag = if (is_ptr)
|
||||
.switch_capture_else_ref
|
||||
else
|
||||
.switch_capture_else;
|
||||
const capture = try case_scope.add(.{
|
||||
.tag = capture_tag,
|
||||
.data = .{ .switch_capture = .{
|
||||
.switch_inst = switch_block,
|
||||
.prong_index = undefined,
|
||||
} },
|
||||
});
|
||||
const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token);
|
||||
capture_val_scope = .{
|
||||
.parent = &case_scope.base,
|
||||
.gen_zir = &case_scope,
|
||||
.name = capture_name,
|
||||
.inst = capture,
|
||||
.src = parent_gz.tokSrcLoc(payload_token),
|
||||
};
|
||||
break :blk &capture_val_scope.base;
|
||||
};
|
||||
block_scope.break_count += 1;
|
||||
const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
|
||||
if (!astgen.refIsNoReturn(case_result)) {
|
||||
_ = try case_scope.addBreak(.@"break", switch_block, case_result);
|
||||
}
|
||||
// Documentation for this: `zir.Inst.SwitchBr` and `zir.Inst.SwitchBrMulti`.
|
||||
try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
|
||||
3 + // operand, scalar_cases_len, else body len
|
||||
@boolToInt(multi_cases_len != 0) +
|
||||
case_scope.instructions.items.len);
|
||||
scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
|
||||
scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
|
||||
if (multi_cases_len != 0) {
|
||||
scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
|
||||
}
|
||||
scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
|
||||
scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
|
||||
} else {
|
||||
// Documentation for this: `zir.Inst.SwitchBr` and `zir.Inst.SwitchBrMulti`.
|
||||
try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
|
||||
2 + // operand, scalar_cases_len
|
||||
@boolToInt(multi_cases_len != 0));
|
||||
scalar_cases_payload.appendAssumeCapacity(@enumToInt(operand));
|
||||
scalar_cases_payload.appendAssumeCapacity(scalar_cases_len);
|
||||
if (multi_cases_len != 0) {
|
||||
scalar_cases_payload.appendAssumeCapacity(multi_cases_len);
|
||||
}
|
||||
}
|
||||
|
||||
// In this pass we generate all the item and prong expressions except the special case.
|
||||
for (case_nodes) |case_node| {
|
||||
if (case_node == special_node)
|
||||
continue;
|
||||
const case = switch (node_tags[case_node]) {
|
||||
.switch_case_one => tree.switchCaseOne(case_node),
|
||||
.switch_case => tree.switchCase(case_node),
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
// Reset the scope.
|
||||
case_scope.instructions.shrinkRetainingCapacity(0);
|
||||
|
||||
const is_multi_case = case.ast.values.len != 1 or
|
||||
getRangeNode(node_tags, node_datas, case.ast.values[0]) != null;
|
||||
|
||||
const sub_scope = blk: {
|
||||
const payload_token = case.payload_token orelse break :blk &case_scope.base;
|
||||
const ident = if (token_tags[payload_token] == .asterisk)
|
||||
payload_token + 1
|
||||
else
|
||||
payload_token;
|
||||
const is_ptr = ident != payload_token;
|
||||
if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
|
||||
if (is_ptr) {
|
||||
return mod.failTok(&case_scope.base, payload_token, "pointer modifier invalid on discard", .{});
|
||||
}
|
||||
break :blk &case_scope.base;
|
||||
}
|
||||
const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
|
||||
const is_ptr_bits: u2 = @boolToInt(is_ptr);
|
||||
const capture_tag: zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
|
||||
0b00 => .switch_capture,
|
||||
0b01 => .switch_capture_ref,
|
||||
0b10 => .switch_capture_multi,
|
||||
0b11 => .switch_capture_multi_ref,
|
||||
};
|
||||
const capture_index = if (is_multi_case) multi_cases_len else scalar_cases_len;
|
||||
const capture = try case_scope.add(.{
|
||||
.tag = capture_tag,
|
||||
.data = .{ .switch_capture = .{
|
||||
.switch_inst = switch_block,
|
||||
.prong_index = capture_index,
|
||||
} },
|
||||
});
|
||||
const capture_name = try mod.identifierTokenString(&parent_gz.base, payload_token);
|
||||
capture_val_scope = .{
|
||||
.parent = &case_scope.base,
|
||||
.gen_zir = &case_scope,
|
||||
.name = capture_name,
|
||||
.inst = capture,
|
||||
.src = parent_gz.tokSrcLoc(payload_token),
|
||||
};
|
||||
break :blk &capture_val_scope.base;
|
||||
};
|
||||
|
||||
if (is_multi_case) {
|
||||
// items_len, ranges_len, body_len
|
||||
const header_index = multi_cases_payload.items.len;
|
||||
try multi_cases_payload.resize(gpa, multi_cases_payload.items.len + 3);
|
||||
|
||||
// items
|
||||
var items_len: u32 = 0;
|
||||
for (case.ast.values) |item_node| {
|
||||
if (getRangeNode(node_tags, node_datas, item_node) != null) continue;
|
||||
items_len += 1;
|
||||
|
||||
const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
|
||||
try multi_cases_payload.append(gpa, @enumToInt(item_inst));
|
||||
}
|
||||
|
||||
// ranges
|
||||
var ranges_len: u32 = 0;
|
||||
for (case.ast.values) |item_node| {
|
||||
const range = getRangeNode(node_tags, node_datas, item_node) orelse continue;
|
||||
ranges_len += 1;
|
||||
|
||||
const first = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].lhs);
|
||||
const last = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].rhs);
|
||||
try multi_cases_payload.appendSlice(gpa, &[_]u32{
|
||||
@enumToInt(first), @enumToInt(last),
|
||||
});
|
||||
}
|
||||
|
||||
block_scope.break_count += 1;
|
||||
const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
|
||||
if (!astgen.refIsNoReturn(case_result)) {
|
||||
_ = try case_scope.addBreak(.@"break", switch_block, case_result);
|
||||
}
|
||||
|
||||
multi_cases_payload.items[header_index + 0] = items_len;
|
||||
multi_cases_payload.items[header_index + 1] = ranges_len;
|
||||
multi_cases_payload.items[header_index + 2] = @intCast(u32, case_scope.instructions.items.len);
|
||||
try multi_cases_payload.appendSlice(gpa, case_scope.instructions.items);
|
||||
} else {
|
||||
const item_node = case.ast.values[0];
|
||||
const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
|
||||
block_scope.break_count += 1;
|
||||
const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
|
||||
if (!astgen.refIsNoReturn(case_result)) {
|
||||
_ = try case_scope.addBreak(.@"break", switch_block, case_result);
|
||||
}
|
||||
try scalar_cases_payload.ensureCapacity(gpa, scalar_cases_payload.items.len +
|
||||
2 + case_scope.instructions.items.len);
|
||||
scalar_cases_payload.appendAssumeCapacity(@enumToInt(item_inst));
|
||||
scalar_cases_payload.appendAssumeCapacity(@intCast(u32, case_scope.instructions.items.len));
|
||||
scalar_cases_payload.appendSliceAssumeCapacity(case_scope.instructions.items);
|
||||
}
|
||||
}
|
||||
// Now that the item expressions are generated we can add this.
|
||||
try parent_gz.instructions.append(gpa, switch_block);
|
||||
|
||||
const ref_bit: u4 = @boolToInt(any_payload_is_ref);
|
||||
const multi_bit: u4 = @boolToInt(multi_cases_len != 0);
|
||||
const special_prong_bits: u4 = @enumToInt(special_prong);
|
||||
comptime {
|
||||
assert(@enumToInt(zir.SpecialProng.none) == 0b00);
|
||||
assert(@enumToInt(zir.SpecialProng.@"else") == 0b01);
|
||||
assert(@enumToInt(zir.SpecialProng.under) == 0b10);
|
||||
}
|
||||
const zir_tags = astgen.instructions.items(.tag);
|
||||
zir_tags[switch_block] = switch ((ref_bit << 3) | (special_prong_bits << 1) | multi_bit) {
|
||||
0b0_00_0 => .switch_block,
|
||||
0b0_00_1 => .switch_block_multi,
|
||||
0b0_01_0 => .switch_block_else,
|
||||
0b0_01_1 => .switch_block_else_multi,
|
||||
0b0_10_0 => .switch_block_under,
|
||||
0b0_10_1 => .switch_block_under_multi,
|
||||
0b1_00_0 => .switch_block_ref,
|
||||
0b1_00_1 => .switch_block_ref_multi,
|
||||
0b1_01_0 => .switch_block_ref_else,
|
||||
0b1_01_1 => .switch_block_ref_else_multi,
|
||||
0b1_10_0 => .switch_block_ref_under,
|
||||
0b1_10_1 => .switch_block_ref_under_multi,
|
||||
else => unreachable,
|
||||
};
|
||||
const zir_datas = astgen.instructions.items(.data);
|
||||
zir_datas[switch_block].pl_node.payload_index = @intCast(u32, astgen.extra.items.len);
|
||||
try astgen.extra.ensureCapacity(gpa, astgen.extra.items.len +
|
||||
scalar_cases_payload.items.len + multi_cases_payload.items.len);
|
||||
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items);
|
||||
astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items);
|
||||
const strat = rl.strategy(&block_scope);
|
||||
assert(strat.tag == .break_operand); // TODO
|
||||
assert(!strat.elide_store_to_block_ptr_instructions); // TODO
|
||||
assert(rl != .ref); // TODO
|
||||
const switch_block_ref = astgen.indexToRef(switch_block);
|
||||
return rvalue(parent_gz, scope, rl, switch_block_ref, switch_node);
|
||||
}
|
||||
|
||||
fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!zir.Inst.Ref {
|
||||
@ -3021,7 +3366,7 @@ fn typeOf(
|
||||
return gz.astgen.mod.failTok(scope, builtin_token, "expected at least 1 argument, found 0", .{});
|
||||
}
|
||||
if (params.len == 1) {
|
||||
const result = try gz.addUnTok(.typeof, try expr(gz, scope, .none, params[0]), node);
|
||||
const result = try gz.addUnNode(.typeof, try expr(gz, scope, .none, params[0]), node);
|
||||
return rvalue(gz, scope, rl, result, node);
|
||||
}
|
||||
const arena = gz.astgen.arena;
|
||||
|
||||
180
src/Sema.zig
180
src/Sema.zig
@ -84,6 +84,15 @@ pub fn rootAsType(sema: *Sema, root_block: *Scope.Block) !Type {
|
||||
return sema.resolveType(root_block, .unneeded, zir_inst_ref);
|
||||
}
|
||||
|
||||
/// Returns only the result from the body that is specified.
|
||||
/// Only appropriate to call when it is determined at comptime that this body
|
||||
/// has no peers.
|
||||
fn resolveBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) InnerError!*Inst {
|
||||
const break_inst = try sema.analyzeBody(block, body);
|
||||
const operand_ref = sema.code.instructions.items(.data)[break_inst].@"break".operand;
|
||||
return sema.resolveInst(operand_ref);
|
||||
}
|
||||
|
||||
/// ZIR instructions which are always `noreturn` return this. This matches the
|
||||
/// return type of `analyzeBody` so that we can tail call them.
|
||||
/// Only appropriate to return when the instruction is known to be NoReturn
|
||||
@ -229,7 +238,26 @@ pub fn analyzeBody(
|
||||
.str => try sema.zirStr(block, inst),
|
||||
.sub => try sema.zirArithmetic(block, inst),
|
||||
.subwrap => try sema.zirArithmetic(block, inst),
|
||||
.switch_block => try sema.zirSwitchBlock(block, inst, false, .none),
|
||||
.switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),
|
||||
.switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),
|
||||
.switch_block_else_multi => try sema.zirSwitchBlockMulti(block, inst, false, .@"else"),
|
||||
.switch_block_under => try sema.zirSwitchBlock(block, inst, false, .under),
|
||||
.switch_block_under_multi => try sema.zirSwitchBlockMulti(block, inst, false, .under),
|
||||
.switch_block_ref => try sema.zirSwitchBlock(block, inst, true, .none),
|
||||
.switch_block_ref_multi => try sema.zirSwitchBlockMulti(block, inst, true, .none),
|
||||
.switch_block_ref_else => try sema.zirSwitchBlock(block, inst, true, .@"else"),
|
||||
.switch_block_ref_else_multi => try sema.zirSwitchBlockMulti(block, inst, true, .@"else"),
|
||||
.switch_block_ref_under => try sema.zirSwitchBlock(block, inst, true, .under),
|
||||
.switch_block_ref_under_multi => try sema.zirSwitchBlockMulti(block, inst, true, .under),
|
||||
.switch_capture => try sema.zirSwitchCapture(block, inst, false, false),
|
||||
.switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
|
||||
.switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
|
||||
.switch_capture_multi_ref => try sema.zirSwitchCapture(block, inst, true, true),
|
||||
.switch_capture_else => try sema.zirSwitchCaptureElse(block, inst, false),
|
||||
.switch_capture_else_ref => try sema.zirSwitchCaptureElse(block, inst, true),
|
||||
.typeof => try sema.zirTypeof(block, inst),
|
||||
.typeof_elem => try sema.zirTypeofElem(block, inst),
|
||||
.typeof_peer => try sema.zirTypeofPeer(block, inst),
|
||||
.xor => try sema.zirBitwise(block, inst, .xor),
|
||||
|
||||
@ -245,18 +273,6 @@ pub fn analyzeBody(
|
||||
.ret_tok => return sema.zirRetTok(block, inst, false),
|
||||
.@"unreachable" => return sema.zirUnreachable(block, inst),
|
||||
.repeat => return sema.zirRepeat(block, inst),
|
||||
.switch_br => return sema.zirSwitchBr(block, inst, false, .none),
|
||||
.switch_br_multi => return sema.zirSwitchBrMulti(block, inst, false, .none),
|
||||
.switch_br_else => return sema.zirSwitchBr(block, inst, false, .@"else"),
|
||||
.switch_br_else_multi => return sema.zirSwitchBrMulti(block, inst, false, .@"else"),
|
||||
.switch_br_under => return sema.zirSwitchBr(block, inst, false, .under),
|
||||
.switch_br_under_multi => return sema.zirSwitchBrMulti(block, inst, false, .under),
|
||||
.switch_br_ref => return sema.zirSwitchBr(block, inst, true, .none),
|
||||
.switch_br_ref_multi => return sema.zirSwitchBrMulti(block, inst, true, .none),
|
||||
.switch_br_ref_else => return sema.zirSwitchBr(block, inst, true, .@"else"),
|
||||
.switch_br_ref_else_multi => return sema.zirSwitchBrMulti(block, inst, true, .@"else"),
|
||||
.switch_br_ref_under => return sema.zirSwitchBr(block, inst, true, .under),
|
||||
.switch_br_ref_under_multi => return sema.zirSwitchBrMulti(block, inst, true, .under),
|
||||
|
||||
// Instructions that we know can *never* be noreturn based solely on
|
||||
// their tag. We avoid needlessly checking if they are noreturn and
|
||||
@ -1034,7 +1050,7 @@ fn analyzeBlockBody(
|
||||
}
|
||||
assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand);
|
||||
// Here we depend on the br instruction having been over-allocated (if necessary)
|
||||
// inide analyzeBreak so that it can be converted into a br_block_flat instruction.
|
||||
// inside zirBreak so that it can be converted into a br_block_flat instruction.
|
||||
const br_src = br.base.src;
|
||||
const br_ty = br.base.ty;
|
||||
const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br);
|
||||
@ -1063,22 +1079,15 @@ fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
|
||||
_ = try block.addNoOp(src, Type.initTag(.void), .breakpoint);
|
||||
}
|
||||
|
||||
fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
|
||||
fn zirBreak(sema: *Sema, start_block: *Scope.Block, inst: zir.Inst.Index) InnerError!zir.Inst.Index {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].@"break";
|
||||
const src = sema.src;
|
||||
const operand = try sema.resolveInst(inst_data.operand);
|
||||
return sema.analyzeBreak(block, sema.src, inst_data.block_inst, operand);
|
||||
}
|
||||
const zir_block = inst_data.block_inst;
|
||||
|
||||
fn analyzeBreak(
|
||||
sema: *Sema,
|
||||
start_block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
zir_block: zir.Inst.Index,
|
||||
operand: *Inst,
|
||||
) InnerError!zir.Inst.Index {
|
||||
var block = start_block;
|
||||
while (true) {
|
||||
if (block.label) |*label| {
|
||||
@ -1103,7 +1112,7 @@ fn analyzeBreak(
|
||||
try start_block.instructions.append(sema.gpa, &br.base);
|
||||
try label.merges.results.append(sema.gpa, operand);
|
||||
try label.merges.br_list.append(sema.gpa, br);
|
||||
return always_noreturn;
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
block = block.parent.?;
|
||||
@ -2208,15 +2217,38 @@ fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) Inne
|
||||
return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src);
|
||||
}
|
||||
|
||||
const SpecialProng = enum { none, @"else", under };
|
||||
fn zirSwitchCapture(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
inst: zir.Inst.Index,
|
||||
is_multi: bool,
|
||||
is_ref: bool,
|
||||
) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
fn zirSwitchBr(
|
||||
@panic("TODO implement Sema for zirSwitchCapture");
|
||||
}
|
||||
|
||||
fn zirSwitchCaptureElse(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
inst: zir.Inst.Index,
|
||||
is_ref: bool,
|
||||
special_prong: SpecialProng,
|
||||
) InnerError!zir.Inst.Index {
|
||||
) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@panic("TODO implement Sema for zirSwitchCaptureElse");
|
||||
}
|
||||
|
||||
fn zirSwitchBlock(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
inst: zir.Inst.Index,
|
||||
is_ref: bool,
|
||||
special_prong: zir.SpecialProng,
|
||||
) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@ -2238,17 +2270,18 @@ fn zirSwitchBr(
|
||||
special_prong,
|
||||
extra.data.cases_len,
|
||||
0,
|
||||
inst,
|
||||
inst_data.src_node,
|
||||
);
|
||||
}
|
||||
|
||||
fn zirSwitchBrMulti(
|
||||
fn zirSwitchBlockMulti(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
inst: zir.Inst.Index,
|
||||
is_ref: bool,
|
||||
special_prong: SpecialProng,
|
||||
) InnerError!zir.Inst.Index {
|
||||
special_prong: zir.SpecialProng,
|
||||
) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@ -2270,6 +2303,7 @@ fn zirSwitchBrMulti(
|
||||
special_prong,
|
||||
extra.data.scalar_cases_len,
|
||||
extra.data.multi_cases_len,
|
||||
inst,
|
||||
inst_data.src_node,
|
||||
);
|
||||
}
|
||||
@ -2279,11 +2313,12 @@ fn analyzeSwitch(
|
||||
block: *Scope.Block,
|
||||
operand: *Inst,
|
||||
extra_end: usize,
|
||||
special_prong: SpecialProng,
|
||||
special_prong: zir.SpecialProng,
|
||||
scalar_cases_len: usize,
|
||||
multi_cases_len: usize,
|
||||
switch_inst: zir.Inst.Index,
|
||||
src_node_offset: i32,
|
||||
) InnerError!zir.Inst.Index {
|
||||
) InnerError!*Inst {
|
||||
const special: struct { body: []const zir.Inst.Index, end: usize } = switch (special_prong) {
|
||||
.none => .{ .body = &.{}, .end = extra_end },
|
||||
.under, .@"else" => blk: {
|
||||
@ -2584,7 +2619,7 @@ fn analyzeSwitch(
|
||||
const item = try sema.resolveInst(item_ref);
|
||||
const item_val = try sema.resolveConstValue(block, item.src, item);
|
||||
if (operand_val.eql(item_val)) {
|
||||
return sema.analyzeBody(block, body);
|
||||
return sema.resolveBody(block, body);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2605,7 +2640,7 @@ fn analyzeSwitch(
|
||||
const item = try sema.resolveInst(item_ref);
|
||||
const item_val = try sema.resolveConstValue(block, item.src, item);
|
||||
if (operand_val.eql(item_val)) {
|
||||
return sema.analyzeBody(block, body);
|
||||
return sema.resolveBody(block, body);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2621,26 +2656,59 @@ fn analyzeSwitch(
|
||||
if (Value.compare(operand_val, .gte, first_tv.val) and
|
||||
Value.compare(operand_val, .lte, last_tv.val))
|
||||
{
|
||||
return sema.analyzeBody(block, body);
|
||||
return sema.resolveBody(block, body);
|
||||
}
|
||||
}
|
||||
|
||||
extra_index += body_len;
|
||||
}
|
||||
}
|
||||
return sema.analyzeBody(block, special.body);
|
||||
return sema.resolveBody(block, special.body);
|
||||
}
|
||||
|
||||
if (scalar_cases_len + multi_cases_len == 0) {
|
||||
return sema.analyzeBody(block, special.body);
|
||||
return sema.resolveBody(block, special.body);
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
|
||||
const block_inst = try sema.arena.create(Inst.Block);
|
||||
block_inst.* = .{
|
||||
.base = .{
|
||||
.tag = Inst.Block.base_tag,
|
||||
.ty = undefined, // Set after analysis.
|
||||
.src = src,
|
||||
},
|
||||
.body = undefined,
|
||||
};
|
||||
|
||||
var child_block: Scope.Block = .{
|
||||
.parent = block,
|
||||
.sema = sema,
|
||||
.src_decl = block.src_decl,
|
||||
.instructions = .{},
|
||||
// TODO @as here is working around a stage1 miscompilation bug :(
|
||||
.label = @as(?Scope.Block.Label, Scope.Block.Label{
|
||||
.zir_block = switch_inst,
|
||||
.merges = .{
|
||||
.results = .{},
|
||||
.br_list = .{},
|
||||
.block_inst = block_inst,
|
||||
},
|
||||
}),
|
||||
.inlining = block.inlining,
|
||||
.is_comptime = block.is_comptime,
|
||||
};
|
||||
const merges = &child_block.label.?.merges;
|
||||
defer child_block.instructions.deinit(sema.gpa);
|
||||
defer merges.results.deinit(sema.gpa);
|
||||
defer merges.br_list.deinit(sema.gpa);
|
||||
|
||||
// TODO when reworking TZIR memory layout make multi cases get generated as cases,
|
||||
// not as part of the "else" block.
|
||||
const cases = try sema.arena.alloc(Inst.SwitchBr.Case, scalar_cases_len);
|
||||
|
||||
var case_block = block.makeSubBlock();
|
||||
var case_block = child_block.makeSubBlock();
|
||||
defer case_block.instructions.deinit(sema.gpa);
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
@ -2656,7 +2724,7 @@ fn analyzeSwitch(
|
||||
|
||||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
const item = try sema.resolveInst(item_ref);
|
||||
const item_val = try sema.resolveConstValue(block, item.src, item);
|
||||
const item_val = try sema.resolveConstValue(&case_block, item.src, item);
|
||||
|
||||
_ = try sema.analyzeBody(&case_block, body);
|
||||
|
||||
@ -2687,7 +2755,7 @@ fn analyzeSwitch(
|
||||
|
||||
for (items) |item_ref| {
|
||||
const item = try sema.resolveInst(item_ref);
|
||||
_ = try sema.resolveConstValue(block, item.src, item);
|
||||
_ = try sema.resolveConstValue(&child_block, item.src, item);
|
||||
|
||||
const cmp_ok = try case_block.addBinOp(item.src, bool_ty, .cmp_eq, operand, item);
|
||||
if (any_ok) |some| {
|
||||
@ -2707,8 +2775,8 @@ fn analyzeSwitch(
|
||||
const item_first = try sema.resolveInst(first_ref);
|
||||
const item_last = try sema.resolveInst(last_ref);
|
||||
|
||||
_ = try sema.resolveConstValue(block, item_first.src, item_first);
|
||||
_ = try sema.resolveConstValue(block, item_last.src, item_last);
|
||||
_ = try sema.resolveConstValue(&child_block, item_first.src, item_first);
|
||||
_ = try sema.resolveConstValue(&child_block, item_last.src, item_last);
|
||||
|
||||
const range_src = item_first.src;
|
||||
|
||||
@ -2779,8 +2847,8 @@ fn analyzeSwitch(
|
||||
.instructions = try sema.arena.dupe(*Inst, &[1]*Inst{&first_condbr.base}),
|
||||
};
|
||||
|
||||
_ = try block.addSwitchBr(src, operand, cases, final_else_body);
|
||||
return always_noreturn;
|
||||
_ = try child_block.addSwitchBr(src, operand, cases, final_else_body);
|
||||
return sema.analyzeBlockBody(block, &child_block, merges);
|
||||
}
|
||||
|
||||
fn validateSwitchItem(
|
||||
@ -3261,12 +3329,18 @@ fn zirCmp(
|
||||
}
|
||||
|
||||
fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src = inst_data.src();
|
||||
const operand = try sema.resolveInst(inst_data.operand);
|
||||
return sema.mod.constType(sema.arena, inst_data.src(), operand.ty);
|
||||
return sema.mod.constType(sema.arena, src, operand.ty);
|
||||
}
|
||||
|
||||
fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src = inst_data.src();
|
||||
const operand_ptr = try sema.resolveInst(inst_data.operand);
|
||||
const elem_ty = operand_ptr.ty.elemType();
|
||||
return sema.mod.constType(sema.arena, src, elem_ty);
|
||||
}
|
||||
|
||||
fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
|
||||
@ -3360,8 +3434,7 @@ fn zirBoolBr(
|
||||
// comptime-known left-hand side. No need for a block here; the result
|
||||
// is simply the rhs expression. Here we rely on there only being 1
|
||||
// break instruction (`break_inline`).
|
||||
const break_inst = try sema.analyzeBody(parent_block, body);
|
||||
return sema.resolveInst(datas[break_inst].@"break".operand);
|
||||
return sema.resolveBody(parent_block, body);
|
||||
}
|
||||
|
||||
const block_inst = try sema.arena.create(Inst.Block);
|
||||
@ -3392,8 +3465,7 @@ fn zirBoolBr(
|
||||
});
|
||||
_ = try lhs_block.addBr(src, block_inst, lhs_result);
|
||||
|
||||
const rhs_break_inst = try sema.analyzeBody(rhs_block, body);
|
||||
const rhs_result = try sema.resolveInst(datas[rhs_break_inst].@"break".operand);
|
||||
const rhs_result = try sema.resolveBody(rhs_block, body);
|
||||
_ = try rhs_block.addBr(src, block_inst, rhs_result);
|
||||
|
||||
const tzir_then_body: ir.Body = .{ .instructions = try sema.arena.dupe(*Inst, then_block.instructions.items) };
|
||||
|
||||
231
src/zir.zig
231
src/zir.zig
@ -514,6 +514,9 @@ pub const Inst = struct {
|
||||
/// Returns the type of a value.
|
||||
/// Uses the `un_tok` field.
|
||||
typeof,
|
||||
/// 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`.
|
||||
@ -588,32 +591,55 @@ pub const Inst = struct {
|
||||
/// A switch expression. Uses the `pl_node` union field.
|
||||
/// AST node is the switch, payload is `SwitchBr`.
|
||||
/// All prongs of target handled.
|
||||
switch_br,
|
||||
/// Same as switch_br, except one or more prongs have multiple items.
|
||||
switch_br_multi,
|
||||
/// Same as switch_br, except has an else prong.
|
||||
switch_br_else,
|
||||
/// Same as switch_br_else, except one or more prongs have multiple items.
|
||||
switch_br_else_multi,
|
||||
/// Same as switch_br, except has an underscore prong.
|
||||
switch_br_under,
|
||||
/// Same as switch_br, except one or more prongs have multiple items.
|
||||
switch_br_under_multi,
|
||||
/// Same as `switch_br` but the target is a pointer to the value being switched on.
|
||||
switch_br_ref,
|
||||
/// Same as `switch_br_multi` but the target is a pointer to the value being switched on.
|
||||
switch_br_ref_multi,
|
||||
/// Same as `switch_br_else` but the target is a pointer to the value being switched on.
|
||||
switch_br_ref_else,
|
||||
/// Same as `switch_br_else_multi` but the target is a pointer to the
|
||||
switch_block,
|
||||
/// Same as switch_block, except one or more prongs have multiple items.
|
||||
switch_block_multi,
|
||||
/// Same as switch_block, except has an else prong.
|
||||
switch_block_else,
|
||||
/// Same as switch_block_else, except one or more prongs have multiple items.
|
||||
switch_block_else_multi,
|
||||
/// Same as switch_block, except has an underscore prong.
|
||||
switch_block_under,
|
||||
/// Same as switch_block, except one or more prongs have multiple items.
|
||||
switch_block_under_multi,
|
||||
/// Same as `switch_block` but the target is a pointer to the value being switched on.
|
||||
switch_block_ref,
|
||||
/// Same as `switch_block_multi` but the target is a pointer to the value being switched on.
|
||||
switch_block_ref_multi,
|
||||
/// Same as `switch_block_else` but the target is a pointer to the value being switched on.
|
||||
switch_block_ref_else,
|
||||
/// Same as `switch_block_else_multi` but the target is a pointer to the
|
||||
/// value being switched on.
|
||||
switch_br_ref_else_multi,
|
||||
/// Same as `switch_br_under` but the target is a pointer to the value
|
||||
switch_block_ref_else_multi,
|
||||
/// Same as `switch_block_under` but the target is a pointer to the value
|
||||
/// being switched on.
|
||||
switch_br_ref_under,
|
||||
/// Same as `switch_br_under_multi` but the target is a pointer to
|
||||
switch_block_ref_under,
|
||||
/// Same as `switch_block_under_multi` but the target is a pointer to
|
||||
/// the value being switched on.
|
||||
switch_br_ref_under_multi,
|
||||
switch_block_ref_under_multi,
|
||||
/// Produces the capture value for a switch prong.
|
||||
/// Uses the `switch_capture` field.
|
||||
switch_capture,
|
||||
/// Produces the capture value for a switch prong.
|
||||
/// Result is a pointer to the value.
|
||||
/// Uses the `switch_capture` field.
|
||||
switch_capture_ref,
|
||||
/// Produces the capture value for a switch prong.
|
||||
/// The prong is one of the multi cases.
|
||||
/// Uses the `switch_capture` field.
|
||||
switch_capture_multi,
|
||||
/// Produces the capture value for a switch prong.
|
||||
/// The prong is one of the multi cases.
|
||||
/// Result is a pointer to the value.
|
||||
/// Uses the `switch_capture` field.
|
||||
switch_capture_multi_ref,
|
||||
/// Produces the capture value for the else/'_' switch prong.
|
||||
/// Uses the `switch_capture` field.
|
||||
switch_capture_else,
|
||||
/// Produces the capture value for the else/'_' switch prong.
|
||||
/// Result is a pointer to the value.
|
||||
/// Uses the `switch_capture` field.
|
||||
switch_capture_else_ref,
|
||||
|
||||
/// Returns whether the instruction is one of the control flow "noreturn" types.
|
||||
/// Function calls do not count.
|
||||
@ -710,6 +736,7 @@ pub const Inst = struct {
|
||||
.negate,
|
||||
.negate_wrap,
|
||||
.typeof,
|
||||
.typeof_elem,
|
||||
.xor,
|
||||
.optional_type,
|
||||
.optional_type_from_ptr_elem,
|
||||
@ -743,6 +770,24 @@ pub const Inst = struct {
|
||||
.set_eval_branch_quota,
|
||||
.compile_log,
|
||||
.elided,
|
||||
.switch_capture,
|
||||
.switch_capture_ref,
|
||||
.switch_capture_multi,
|
||||
.switch_capture_multi_ref,
|
||||
.switch_capture_else,
|
||||
.switch_capture_else_ref,
|
||||
.switch_block,
|
||||
.switch_block_multi,
|
||||
.switch_block_else,
|
||||
.switch_block_else_multi,
|
||||
.switch_block_under,
|
||||
.switch_block_under_multi,
|
||||
.switch_block_ref,
|
||||
.switch_block_ref_multi,
|
||||
.switch_block_ref_else,
|
||||
.switch_block_ref_else_multi,
|
||||
.switch_block_ref_under,
|
||||
.switch_block_ref_under_multi,
|
||||
=> false,
|
||||
|
||||
.@"break",
|
||||
@ -756,18 +801,6 @@ pub const Inst = struct {
|
||||
.@"unreachable",
|
||||
.repeat,
|
||||
.repeat_inline,
|
||||
.switch_br,
|
||||
.switch_br_multi,
|
||||
.switch_br_else,
|
||||
.switch_br_else_multi,
|
||||
.switch_br_under,
|
||||
.switch_br_under_multi,
|
||||
.switch_br_ref,
|
||||
.switch_br_ref_multi,
|
||||
.switch_br_ref_else,
|
||||
.switch_br_ref_else_multi,
|
||||
.switch_br_ref_under,
|
||||
.switch_br_ref_under_multi,
|
||||
=> true,
|
||||
};
|
||||
}
|
||||
@ -1223,6 +1256,10 @@ pub const Inst = struct {
|
||||
block_inst: Index,
|
||||
operand: Ref,
|
||||
},
|
||||
switch_capture: struct {
|
||||
switch_inst: Index,
|
||||
prong_index: u32,
|
||||
},
|
||||
|
||||
// Make sure we don't accidentally add a field to make this union
|
||||
// bigger than expected. Note that in Debug builds, Zig is allowed
|
||||
@ -1394,6 +1431,8 @@ pub const Inst = struct {
|
||||
};
|
||||
};
|
||||
|
||||
pub const SpecialProng = enum { none, @"else", under };
|
||||
|
||||
const Writer = struct {
|
||||
gpa: *Allocator,
|
||||
arena: *Allocator,
|
||||
@ -1461,12 +1500,13 @@ const Writer = struct {
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.typeof,
|
||||
.typeof_elem,
|
||||
=> try self.writeUnNode(stream, inst),
|
||||
|
||||
.ref,
|
||||
.ret_tok,
|
||||
.ret_coerce,
|
||||
.typeof,
|
||||
.ensure_err_payload_void,
|
||||
=> try self.writeUnTok(stream, inst),
|
||||
|
||||
@ -1542,21 +1582,19 @@ const Writer = struct {
|
||||
.condbr_inline,
|
||||
=> try self.writePlNodeCondBr(stream, inst),
|
||||
|
||||
.switch_br,
|
||||
.switch_br_else,
|
||||
.switch_br_under,
|
||||
.switch_br_ref,
|
||||
.switch_br_ref_else,
|
||||
.switch_br_ref_under,
|
||||
=> try self.writePlNodeSwitchBr(stream, inst),
|
||||
.switch_block => try self.writePlNodeSwitchBr(stream, inst, .none),
|
||||
.switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
|
||||
.switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under),
|
||||
.switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none),
|
||||
.switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
|
||||
.switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under),
|
||||
|
||||
.switch_br_multi,
|
||||
.switch_br_else_multi,
|
||||
.switch_br_under_multi,
|
||||
.switch_br_ref_multi,
|
||||
.switch_br_ref_else_multi,
|
||||
.switch_br_ref_under_multi,
|
||||
=> try self.writePlNodeSwitchBrMulti(stream, inst),
|
||||
.switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
|
||||
.switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
|
||||
.switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
|
||||
.switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
|
||||
.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,
|
||||
@ -1588,10 +1626,19 @@ const Writer = struct {
|
||||
.fn_type_cc => try self.writeFnTypeCc(stream, inst, false),
|
||||
.fn_type_var_args => try self.writeFnType(stream, inst, true),
|
||||
.fn_type_cc_var_args => try self.writeFnTypeCc(stream, inst, true),
|
||||
|
||||
.@"unreachable" => try self.writeUnreachable(stream, inst),
|
||||
|
||||
.enum_literal_small => try self.writeSmallStr(stream, inst),
|
||||
|
||||
.switch_capture,
|
||||
.switch_capture_ref,
|
||||
.switch_capture_multi,
|
||||
.switch_capture_multi_ref,
|
||||
.switch_capture_else,
|
||||
.switch_capture_else_ref,
|
||||
=> try self.writeSwitchCapture(stream, inst),
|
||||
|
||||
.bitcast,
|
||||
.bitcast_ref,
|
||||
.bitcast_result_ptr,
|
||||
@ -1763,11 +1810,46 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writePlNodeSwitchBr(self: *Writer, stream: anytype, inst: Inst.Index) !void {
|
||||
fn writePlNodeSwitchBr(
|
||||
self: *Writer,
|
||||
stream: anytype,
|
||||
inst: Inst.Index,
|
||||
special_prong: SpecialProng,
|
||||
) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Inst.SwitchBr, inst_data.payload_index);
|
||||
const special: struct {
|
||||
body: []const Inst.Index,
|
||||
end: usize,
|
||||
} = switch (special_prong) {
|
||||
.none => .{ .body = &.{}, .end = extra.end },
|
||||
.under, .@"else" => blk: {
|
||||
const body_len = self.code.extra[extra.end];
|
||||
const extra_body_start = extra.end + 1;
|
||||
break :blk .{
|
||||
.body = self.code.extra[extra_body_start..][0..body_len],
|
||||
.end = extra_body_start + body_len,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
try self.writeInstRef(stream, extra.data.operand);
|
||||
var extra_index: usize = extra.end;
|
||||
|
||||
if (special.body.len != 0) {
|
||||
const prong_name = switch (special_prong) {
|
||||
.@"else" => "else",
|
||||
.under => "_",
|
||||
else => unreachable,
|
||||
};
|
||||
try stream.print(", {s} => {{\n", .{prong_name});
|
||||
self.indent += 2;
|
||||
try self.writeBody(stream, special.body);
|
||||
self.indent -= 2;
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
try stream.writeAll("}");
|
||||
}
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
{
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
|
||||
@ -1792,11 +1874,46 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writePlNodeSwitchBrMulti(self: *Writer, stream: anytype, inst: Inst.Index) !void {
|
||||
fn writePlNodeSwitchBlockMulti(
|
||||
self: *Writer,
|
||||
stream: anytype,
|
||||
inst: Inst.Index,
|
||||
special_prong: SpecialProng,
|
||||
) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Inst.SwitchBrMulti, inst_data.payload_index);
|
||||
const special: struct {
|
||||
body: []const Inst.Index,
|
||||
end: usize,
|
||||
} = switch (special_prong) {
|
||||
.none => .{ .body = &.{}, .end = extra.end },
|
||||
.under, .@"else" => blk: {
|
||||
const body_len = self.code.extra[extra.end];
|
||||
const extra_body_start = extra.end + 1;
|
||||
break :blk .{
|
||||
.body = self.code.extra[extra_body_start..][0..body_len],
|
||||
.end = extra_body_start + body_len,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
try self.writeInstRef(stream, extra.data.operand);
|
||||
var extra_index: usize = extra.end;
|
||||
|
||||
if (special.body.len != 0) {
|
||||
const prong_name = switch (special_prong) {
|
||||
.@"else" => "else",
|
||||
.under => "_",
|
||||
else => unreachable,
|
||||
};
|
||||
try stream.print(", {s} => {{\n", .{prong_name});
|
||||
self.indent += 2;
|
||||
try self.writeBody(stream, special.body);
|
||||
self.indent -= 2;
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
try stream.writeAll("}");
|
||||
}
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
{
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
|
||||
@ -2015,6 +2132,12 @@ const Writer = struct {
|
||||
try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
|
||||
}
|
||||
|
||||
fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].switch_capture;
|
||||
try self.writeInstIndex(stream, inst_data.switch_inst);
|
||||
try stream.print(", {d})", .{inst_data.prong_index});
|
||||
}
|
||||
|
||||
fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void {
|
||||
var i: usize = @enumToInt(ref);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user