mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
Merge pull request #15880 from mlugg/feat/better-switch-zir-2
Simplify and compact switch ZIR, and resolve union payload captures with PTR
This commit is contained in:
commit
df6319418a
153
src/AstGen.zig
153
src/AstGen.zig
@ -2610,13 +2610,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
|
|||||||
.slice_length,
|
.slice_length,
|
||||||
.import,
|
.import,
|
||||||
.switch_block,
|
.switch_block,
|
||||||
.switch_cond,
|
.switch_block_ref,
|
||||||
.switch_cond_ref,
|
|
||||||
.switch_capture,
|
|
||||||
.switch_capture_ref,
|
|
||||||
.switch_capture_multi,
|
|
||||||
.switch_capture_multi_ref,
|
|
||||||
.switch_capture_tag,
|
|
||||||
.struct_init_empty,
|
.struct_init_empty,
|
||||||
.struct_init,
|
.struct_init,
|
||||||
.struct_init_ref,
|
.struct_init_ref,
|
||||||
@ -2960,7 +2954,7 @@ fn deferStmt(
|
|||||||
try gz.astgen.instructions.append(gz.astgen.gpa, .{
|
try gz.astgen.instructions.append(gz.astgen.gpa, .{
|
||||||
.tag = .extended,
|
.tag = .extended,
|
||||||
.data = .{ .extended = .{
|
.data = .{ .extended = .{
|
||||||
.opcode = .errdefer_err_code,
|
.opcode = .value_placeholder,
|
||||||
.small = undefined,
|
.small = undefined,
|
||||||
.operand = undefined,
|
.operand = undefined,
|
||||||
} },
|
} },
|
||||||
@ -6715,6 +6709,7 @@ fn switchExpr(
|
|||||||
// for the following variables, make note of the special prong AST node index,
|
// 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.
|
// and bail out with a compile error if there are multiple special prongs present.
|
||||||
var any_payload_is_ref = false;
|
var any_payload_is_ref = false;
|
||||||
|
var any_has_tag_capture = false;
|
||||||
var scalar_cases_len: u32 = 0;
|
var scalar_cases_len: u32 = 0;
|
||||||
var multi_cases_len: u32 = 0;
|
var multi_cases_len: u32 = 0;
|
||||||
var inline_cases_len: u32 = 0;
|
var inline_cases_len: u32 = 0;
|
||||||
@ -6725,8 +6720,12 @@ fn switchExpr(
|
|||||||
for (case_nodes) |case_node| {
|
for (case_nodes) |case_node| {
|
||||||
const case = tree.fullSwitchCase(case_node).?;
|
const case = tree.fullSwitchCase(case_node).?;
|
||||||
if (case.payload_token) |payload_token| {
|
if (case.payload_token) |payload_token| {
|
||||||
if (token_tags[payload_token] == .asterisk) {
|
const ident = if (token_tags[payload_token] == .asterisk) blk: {
|
||||||
any_payload_is_ref = true;
|
any_payload_is_ref = true;
|
||||||
|
break :blk payload_token + 1;
|
||||||
|
} else payload_token;
|
||||||
|
if (token_tags[ident + 1] == .comma) {
|
||||||
|
any_has_tag_capture = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check for else/`_` prong.
|
// Check for else/`_` prong.
|
||||||
@ -6835,13 +6834,7 @@ fn switchExpr(
|
|||||||
const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
|
const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
|
||||||
|
|
||||||
const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
|
const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
|
||||||
const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
|
const item_ri: ResultInfo = .{ .rl = .none };
|
||||||
const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node);
|
|
||||||
// Sema expects a dbg_stmt immediately after switch_cond(_ref)
|
|
||||||
try emitDbgStmt(parent_gz, operand_lc);
|
|
||||||
// We need the type of the operand to use as the result location for all the prong items.
|
|
||||||
const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node);
|
|
||||||
const item_ri: ResultInfo = .{ .rl = .{ .ty = cond_ty_inst } };
|
|
||||||
|
|
||||||
// This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
|
// This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
|
||||||
// except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
|
// except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
|
||||||
@ -6860,13 +6853,30 @@ fn switchExpr(
|
|||||||
block_scope.instructions_top = GenZir.unstacked_top;
|
block_scope.instructions_top = GenZir.unstacked_top;
|
||||||
block_scope.setBreakResultInfo(ri);
|
block_scope.setBreakResultInfo(ri);
|
||||||
|
|
||||||
|
// Sema expects a dbg_stmt immediately before switch_block(_ref)
|
||||||
|
try emitDbgStmt(parent_gz, operand_lc);
|
||||||
// This gets added to the parent block later, after the item expressions.
|
// This gets added to the parent block later, after the item expressions.
|
||||||
const switch_block = try parent_gz.makeBlockInst(.switch_block, switch_node);
|
const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
|
||||||
|
const switch_block = try parent_gz.makeBlockInst(switch_tag, switch_node);
|
||||||
|
|
||||||
// We re-use this same scope for all cases, including the special prong, if any.
|
// We re-use this same scope for all cases, including the special prong, if any.
|
||||||
var case_scope = parent_gz.makeSubBlock(&block_scope.base);
|
var case_scope = parent_gz.makeSubBlock(&block_scope.base);
|
||||||
case_scope.instructions_top = GenZir.unstacked_top;
|
case_scope.instructions_top = GenZir.unstacked_top;
|
||||||
|
|
||||||
|
// If any prong has an inline tag capture, allocate a shared dummy instruction for it
|
||||||
|
const tag_inst = if (any_has_tag_capture) tag_inst: {
|
||||||
|
const inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
|
||||||
|
try astgen.instructions.append(astgen.gpa, .{
|
||||||
|
.tag = .extended,
|
||||||
|
.data = .{ .extended = .{
|
||||||
|
.opcode = .value_placeholder,
|
||||||
|
.small = undefined,
|
||||||
|
.operand = undefined,
|
||||||
|
} }, // TODO rename opcode
|
||||||
|
});
|
||||||
|
break :tag_inst inst;
|
||||||
|
} else undefined;
|
||||||
|
|
||||||
// In this pass we generate all the item and prong expressions.
|
// In this pass we generate all the item and prong expressions.
|
||||||
var multi_case_index: u32 = 0;
|
var multi_case_index: u32 = 0;
|
||||||
var scalar_case_index: u32 = 0;
|
var scalar_case_index: u32 = 0;
|
||||||
@ -6880,17 +6890,22 @@ fn switchExpr(
|
|||||||
var dbg_var_inst: Zir.Inst.Ref = undefined;
|
var dbg_var_inst: Zir.Inst.Ref = undefined;
|
||||||
var dbg_var_tag_name: ?u32 = null;
|
var dbg_var_tag_name: ?u32 = null;
|
||||||
var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
|
var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
|
||||||
var capture_inst: Zir.Inst.Index = 0;
|
var has_tag_capture = false;
|
||||||
var tag_inst: Zir.Inst.Index = 0;
|
|
||||||
var capture_val_scope: Scope.LocalVal = undefined;
|
var capture_val_scope: Scope.LocalVal = undefined;
|
||||||
var tag_scope: Scope.LocalVal = undefined;
|
var tag_scope: Scope.LocalVal = undefined;
|
||||||
|
|
||||||
|
var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none;
|
||||||
|
|
||||||
const sub_scope = blk: {
|
const sub_scope = blk: {
|
||||||
const payload_token = case.payload_token orelse break :blk &case_scope.base;
|
const payload_token = case.payload_token orelse break :blk &case_scope.base;
|
||||||
const ident = if (token_tags[payload_token] == .asterisk)
|
const ident = if (token_tags[payload_token] == .asterisk)
|
||||||
payload_token + 1
|
payload_token + 1
|
||||||
else
|
else
|
||||||
payload_token;
|
payload_token;
|
||||||
|
|
||||||
const is_ptr = ident != payload_token;
|
const is_ptr = ident != payload_token;
|
||||||
|
capture = if (is_ptr) .by_ref else .by_val;
|
||||||
|
|
||||||
const ident_slice = tree.tokenSlice(ident);
|
const ident_slice = tree.tokenSlice(ident);
|
||||||
var payload_sub_scope: *Scope = undefined;
|
var payload_sub_scope: *Scope = undefined;
|
||||||
if (mem.eql(u8, ident_slice, "_")) {
|
if (mem.eql(u8, ident_slice, "_")) {
|
||||||
@ -6899,53 +6914,18 @@ fn switchExpr(
|
|||||||
}
|
}
|
||||||
payload_sub_scope = &case_scope.base;
|
payload_sub_scope = &case_scope.base;
|
||||||
} else {
|
} else {
|
||||||
if (case_node == special_node) {
|
|
||||||
const capture_tag: Zir.Inst.Tag = if (is_ptr)
|
|
||||||
.switch_capture_ref
|
|
||||||
else
|
|
||||||
.switch_capture;
|
|
||||||
capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
|
|
||||||
try astgen.instructions.append(gpa, .{
|
|
||||||
.tag = capture_tag,
|
|
||||||
.data = .{
|
|
||||||
.switch_capture = .{
|
|
||||||
.switch_inst = switch_block,
|
|
||||||
// Max int communicates that this is the else/underscore prong.
|
|
||||||
.prong_index = std.math.maxInt(u32),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
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_case_index else scalar_case_index;
|
|
||||||
capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
|
|
||||||
try astgen.instructions.append(gpa, .{
|
|
||||||
.tag = capture_tag,
|
|
||||||
.data = .{ .switch_capture = .{
|
|
||||||
.switch_inst = switch_block,
|
|
||||||
.prong_index = capture_index,
|
|
||||||
} },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const capture_name = try astgen.identAsString(ident);
|
const capture_name = try astgen.identAsString(ident);
|
||||||
try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
|
try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
|
||||||
capture_val_scope = .{
|
capture_val_scope = .{
|
||||||
.parent = &case_scope.base,
|
.parent = &case_scope.base,
|
||||||
.gen_zir = &case_scope,
|
.gen_zir = &case_scope,
|
||||||
.name = capture_name,
|
.name = capture_name,
|
||||||
.inst = indexToRef(capture_inst),
|
.inst = indexToRef(switch_block),
|
||||||
.token_src = payload_token,
|
.token_src = payload_token,
|
||||||
.id_cat = .capture,
|
.id_cat = .capture,
|
||||||
};
|
};
|
||||||
dbg_var_name = capture_name;
|
dbg_var_name = capture_name;
|
||||||
dbg_var_inst = indexToRef(capture_inst);
|
dbg_var_inst = indexToRef(switch_block);
|
||||||
payload_sub_scope = &capture_val_scope.base;
|
payload_sub_scope = &capture_val_scope.base;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6961,14 +6941,9 @@ fn switchExpr(
|
|||||||
}
|
}
|
||||||
const tag_name = try astgen.identAsString(tag_token);
|
const tag_name = try astgen.identAsString(tag_token);
|
||||||
try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
|
try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
|
||||||
tag_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
|
|
||||||
try astgen.instructions.append(gpa, .{
|
assert(any_has_tag_capture);
|
||||||
.tag = .switch_capture_tag,
|
has_tag_capture = true;
|
||||||
.data = .{ .un_tok = .{
|
|
||||||
.operand = cond,
|
|
||||||
.src_tok = case_scope.tokenIndexToRelative(tag_token),
|
|
||||||
} },
|
|
||||||
});
|
|
||||||
|
|
||||||
tag_scope = .{
|
tag_scope = .{
|
||||||
.parent = payload_sub_scope,
|
.parent = payload_sub_scope,
|
||||||
@ -7034,8 +7009,6 @@ fn switchExpr(
|
|||||||
case_scope.instructions_top = parent_gz.instructions.items.len;
|
case_scope.instructions_top = parent_gz.instructions.items.len;
|
||||||
defer case_scope.unstack();
|
defer case_scope.unstack();
|
||||||
|
|
||||||
if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);
|
|
||||||
if (tag_inst != 0) try case_scope.instructions.append(gpa, tag_inst);
|
|
||||||
try case_scope.addDbgBlockBegin();
|
try case_scope.addDbgBlockBegin();
|
||||||
if (dbg_var_name) |some| {
|
if (dbg_var_name) |some| {
|
||||||
try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
|
try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
|
||||||
@ -7053,10 +7026,42 @@ fn switchExpr(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const case_slice = case_scope.instructionsSlice();
|
const case_slice = case_scope.instructionsSlice();
|
||||||
const body_len = astgen.countBodyLenAfterFixups(case_slice);
|
// Since we use the switch_block instruction itself to refer to the
|
||||||
|
// capture, which will not be added to the child block, we need to
|
||||||
|
// handle ref_table manually, and the same for the inline tag
|
||||||
|
// capture instruction.
|
||||||
|
const refs_len = refs: {
|
||||||
|
var n: usize = 0;
|
||||||
|
var check_inst = switch_block;
|
||||||
|
while (astgen.ref_table.get(check_inst)) |ref_inst| {
|
||||||
|
n += 1;
|
||||||
|
check_inst = ref_inst;
|
||||||
|
}
|
||||||
|
if (has_tag_capture) {
|
||||||
|
check_inst = tag_inst;
|
||||||
|
while (astgen.ref_table.get(check_inst)) |ref_inst| {
|
||||||
|
n += 1;
|
||||||
|
check_inst = ref_inst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break :refs n;
|
||||||
|
};
|
||||||
|
const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
|
||||||
try payloads.ensureUnusedCapacity(gpa, body_len);
|
try payloads.ensureUnusedCapacity(gpa, body_len);
|
||||||
const inline_bit = @as(u32, @boolToInt(case.inline_token != null)) << 31;
|
payloads.items[body_len_index] = @bitCast(u32, Zir.Inst.SwitchBlock.ProngInfo{
|
||||||
payloads.items[body_len_index] = body_len | inline_bit;
|
.body_len = @intCast(u28, body_len),
|
||||||
|
.capture = capture,
|
||||||
|
.is_inline = case.inline_token != null,
|
||||||
|
.has_tag_capture = has_tag_capture,
|
||||||
|
});
|
||||||
|
if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
|
||||||
|
appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
|
||||||
|
}
|
||||||
|
if (has_tag_capture) {
|
||||||
|
if (astgen.ref_table.fetchRemove(tag_inst)) |kv| {
|
||||||
|
appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
|
appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7065,14 +7070,16 @@ fn switchExpr(
|
|||||||
|
|
||||||
try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
|
try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
|
||||||
@boolToInt(multi_cases_len != 0) +
|
@boolToInt(multi_cases_len != 0) +
|
||||||
|
@boolToInt(any_has_tag_capture) +
|
||||||
payloads.items.len - case_table_end);
|
payloads.items.len - case_table_end);
|
||||||
|
|
||||||
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
|
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
|
||||||
.operand = cond,
|
.operand = raw_operand,
|
||||||
.bits = Zir.Inst.SwitchBlock.Bits{
|
.bits = Zir.Inst.SwitchBlock.Bits{
|
||||||
.has_multi_cases = multi_cases_len != 0,
|
.has_multi_cases = multi_cases_len != 0,
|
||||||
.has_else = special_prong == .@"else",
|
.has_else = special_prong == .@"else",
|
||||||
.has_under = special_prong == .under,
|
.has_under = special_prong == .under,
|
||||||
|
.any_has_tag_capture = any_has_tag_capture,
|
||||||
.scalar_cases_len = @intCast(Zir.Inst.SwitchBlock.Bits.ScalarCasesLen, scalar_cases_len),
|
.scalar_cases_len = @intCast(Zir.Inst.SwitchBlock.Bits.ScalarCasesLen, scalar_cases_len),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -7081,6 +7088,10 @@ fn switchExpr(
|
|||||||
astgen.extra.appendAssumeCapacity(multi_cases_len);
|
astgen.extra.appendAssumeCapacity(multi_cases_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (any_has_tag_capture) {
|
||||||
|
astgen.extra.appendAssumeCapacity(tag_inst);
|
||||||
|
}
|
||||||
|
|
||||||
const zir_datas = astgen.instructions.items(.data);
|
const zir_datas = astgen.instructions.items(.data);
|
||||||
const zir_tags = astgen.instructions.items(.tag);
|
const zir_tags = astgen.instructions.items(.tag);
|
||||||
|
|
||||||
@ -7103,7 +7114,7 @@ fn switchExpr(
|
|||||||
end_index += 3 + items_len + 2 * ranges_len;
|
end_index += 3 + items_len + 2 * ranges_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
const body_len = @truncate(u31, payloads.items[body_len_index]);
|
const body_len = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, payloads.items[body_len_index]).body_len;
|
||||||
end_index += body_len;
|
end_index += body_len;
|
||||||
|
|
||||||
switch (strat.tag) {
|
switch (strat.tag) {
|
||||||
|
|||||||
@ -1993,31 +1993,6 @@ fn walkInstruction(
|
|||||||
.expr = .{ .switchIndex = switch_index },
|
.expr = .{ .switchIndex = switch_index },
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
.switch_cond => {
|
|
||||||
const un_node = data[inst_index].un_node;
|
|
||||||
const operand = try self.walkRef(
|
|
||||||
file,
|
|
||||||
parent_scope,
|
|
||||||
parent_src,
|
|
||||||
un_node.operand,
|
|
||||||
need_type,
|
|
||||||
);
|
|
||||||
const operand_index = self.exprs.items.len;
|
|
||||||
try self.exprs.append(self.arena, operand.expr);
|
|
||||||
|
|
||||||
// const ast_index = self.ast_nodes.items.len;
|
|
||||||
// const sep = "=" ** 200;
|
|
||||||
// log.debug("{s}", .{sep});
|
|
||||||
// log.debug("SWITCH COND", .{});
|
|
||||||
// log.debug("ast index = {}", .{ast_index});
|
|
||||||
// log.debug("ast previous = {}", .{self.ast_nodes.items[ast_index - 1]});
|
|
||||||
// log.debug("{s}", .{sep});
|
|
||||||
|
|
||||||
return DocData.WalkResult{
|
|
||||||
.typeRef = operand.typeRef,
|
|
||||||
.expr = .{ .typeOf = operand_index },
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
.typeof => {
|
.typeof => {
|
||||||
const un_node = data[inst_index].un_node;
|
const un_node = data[inst_index].un_node;
|
||||||
|
|||||||
154
src/Module.zig
154
src/Module.zig
@ -2471,12 +2471,23 @@ pub const SrcLoc = struct {
|
|||||||
}
|
}
|
||||||
} else unreachable;
|
} else unreachable;
|
||||||
},
|
},
|
||||||
.node_offset_switch_prong_capture => |node_off| {
|
.node_offset_switch_prong_capture,
|
||||||
|
.node_offset_switch_prong_tag_capture,
|
||||||
|
=> |node_off| {
|
||||||
const tree = try src_loc.file_scope.getTree(gpa);
|
const tree = try src_loc.file_scope.getTree(gpa);
|
||||||
const case_node = src_loc.declRelativeToNodeIndex(node_off);
|
const case_node = src_loc.declRelativeToNodeIndex(node_off);
|
||||||
const case = tree.fullSwitchCase(case_node).?;
|
const case = tree.fullSwitchCase(case_node).?;
|
||||||
const start_tok = case.payload_token.?;
|
|
||||||
const token_tags = tree.tokens.items(.tag);
|
const token_tags = tree.tokens.items(.tag);
|
||||||
|
const start_tok = switch (src_loc.lazy) {
|
||||||
|
.node_offset_switch_prong_capture => case.payload_token.?,
|
||||||
|
.node_offset_switch_prong_tag_capture => blk: {
|
||||||
|
var tok = case.payload_token.?;
|
||||||
|
if (token_tags[tok] == .asterisk) tok += 1;
|
||||||
|
tok += 2; // skip over comma
|
||||||
|
break :blk tok;
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
const end_tok = switch (token_tags[start_tok]) {
|
const end_tok = switch (token_tags[start_tok]) {
|
||||||
.asterisk => start_tok + 1,
|
.asterisk => start_tok + 1,
|
||||||
else => start_tok,
|
else => start_tok,
|
||||||
@ -2957,6 +2968,9 @@ pub const LazySrcLoc = union(enum) {
|
|||||||
/// The source location points to the capture of a switch_prong.
|
/// The source location points to the capture of a switch_prong.
|
||||||
/// The Decl is determined contextually.
|
/// The Decl is determined contextually.
|
||||||
node_offset_switch_prong_capture: i32,
|
node_offset_switch_prong_capture: i32,
|
||||||
|
/// The source location points to the tag capture of a switch_prong.
|
||||||
|
/// The Decl is determined contextually.
|
||||||
|
node_offset_switch_prong_tag_capture: i32,
|
||||||
/// The source location points to the align expr of a function type
|
/// The source location points to the align expr of a function type
|
||||||
/// expression, found by taking this AST node index offset from the containing
|
/// expression, found by taking this AST node index offset from the containing
|
||||||
/// Decl AST node, which points to a function type AST node. Next, navigate to
|
/// Decl AST node, which points to a function type AST node. Next, navigate to
|
||||||
@ -3130,6 +3144,7 @@ pub const LazySrcLoc = union(enum) {
|
|||||||
.node_offset_switch_special_prong,
|
.node_offset_switch_special_prong,
|
||||||
.node_offset_switch_range,
|
.node_offset_switch_range,
|
||||||
.node_offset_switch_prong_capture,
|
.node_offset_switch_prong_capture,
|
||||||
|
.node_offset_switch_prong_tag_capture,
|
||||||
.node_offset_fn_type_align,
|
.node_offset_fn_type_align,
|
||||||
.node_offset_fn_type_addrspace,
|
.node_offset_fn_type_addrspace,
|
||||||
.node_offset_fn_type_section,
|
.node_offset_fn_type_section,
|
||||||
@ -5867,10 +5882,26 @@ fn lockAndClearFileCompileError(mod: *Module, file: *File) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub const SwitchProngSrc = union(enum) {
|
pub const SwitchProngSrc = union(enum) {
|
||||||
|
/// The item for a scalar prong.
|
||||||
scalar: u32,
|
scalar: u32,
|
||||||
|
/// A given single item for a multi prong.
|
||||||
multi: Multi,
|
multi: Multi,
|
||||||
|
/// A given range item for a multi prong.
|
||||||
range: Multi,
|
range: Multi,
|
||||||
|
/// The item for the special prong.
|
||||||
|
special,
|
||||||
|
/// The main capture for a scalar prong.
|
||||||
|
scalar_capture: u32,
|
||||||
|
/// The main capture for a multi prong.
|
||||||
multi_capture: u32,
|
multi_capture: u32,
|
||||||
|
/// The main capture for the special prong.
|
||||||
|
special_capture,
|
||||||
|
/// The tag capture for a scalar prong.
|
||||||
|
scalar_tag_capture: u32,
|
||||||
|
/// The tag capture for a multi prong.
|
||||||
|
multi_tag_capture: u32,
|
||||||
|
/// The tag capture for the special prong.
|
||||||
|
special_tag_capture,
|
||||||
|
|
||||||
pub const Multi = struct {
|
pub const Multi = struct {
|
||||||
prong: u32,
|
prong: u32,
|
||||||
@ -5886,6 +5917,7 @@ pub const SwitchProngSrc = union(enum) {
|
|||||||
mod: *Module,
|
mod: *Module,
|
||||||
decl: *Decl,
|
decl: *Decl,
|
||||||
switch_node_offset: i32,
|
switch_node_offset: i32,
|
||||||
|
/// Ignored if `prong_src` is not `.range`
|
||||||
range_expand: RangeExpand,
|
range_expand: RangeExpand,
|
||||||
) LazySrcLoc {
|
) LazySrcLoc {
|
||||||
@setCold(true);
|
@setCold(true);
|
||||||
@ -5906,63 +5938,97 @@ pub const SwitchProngSrc = union(enum) {
|
|||||||
|
|
||||||
var multi_i: u32 = 0;
|
var multi_i: u32 = 0;
|
||||||
var scalar_i: u32 = 0;
|
var scalar_i: u32 = 0;
|
||||||
for (case_nodes) |case_node| {
|
const case_node = for (case_nodes) |case_node| {
|
||||||
const case = tree.fullSwitchCase(case_node).?;
|
const case = tree.fullSwitchCase(case_node).?;
|
||||||
if (case.ast.values.len == 0)
|
|
||||||
continue;
|
const is_special = special: {
|
||||||
if (case.ast.values.len == 1 and
|
if (case.ast.values.len == 0) break :special true;
|
||||||
node_tags[case.ast.values[0]] == .identifier and
|
if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .identifier) {
|
||||||
mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
|
break :special mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_");
|
||||||
{
|
}
|
||||||
continue;
|
break :special false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (is_special) {
|
||||||
|
switch (prong_src) {
|
||||||
|
.special, .special_capture, .special_tag_capture => break case_node,
|
||||||
|
else => continue,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const is_multi = case.ast.values.len != 1 or
|
const is_multi = case.ast.values.len != 1 or
|
||||||
node_tags[case.ast.values[0]] == .switch_range;
|
node_tags[case.ast.values[0]] == .switch_range;
|
||||||
|
|
||||||
switch (prong_src) {
|
switch (prong_src) {
|
||||||
.scalar => |i| if (!is_multi and i == scalar_i) return LazySrcLoc.nodeOffset(
|
.scalar,
|
||||||
decl.nodeIndexToRelative(case.ast.values[0]),
|
.scalar_capture,
|
||||||
),
|
.scalar_tag_capture,
|
||||||
.multi_capture => |i| if (is_multi and i == multi_i) {
|
=> |i| if (!is_multi and i == scalar_i) break case_node,
|
||||||
return LazySrcLoc{ .node_offset_switch_prong_capture = decl.nodeIndexToRelative(case_node) };
|
|
||||||
},
|
|
||||||
.multi => |s| if (is_multi and s.prong == multi_i) {
|
|
||||||
var item_i: u32 = 0;
|
|
||||||
for (case.ast.values) |item_node| {
|
|
||||||
if (node_tags[item_node] == .switch_range) continue;
|
|
||||||
|
|
||||||
if (item_i == s.item) return LazySrcLoc.nodeOffset(
|
.multi_capture,
|
||||||
decl.nodeIndexToRelative(item_node),
|
.multi_tag_capture,
|
||||||
);
|
=> |i| if (is_multi and i == multi_i) break case_node,
|
||||||
item_i += 1;
|
|
||||||
} else unreachable;
|
|
||||||
},
|
|
||||||
.range => |s| if (is_multi and s.prong == multi_i) {
|
|
||||||
var range_i: u32 = 0;
|
|
||||||
for (case.ast.values) |range| {
|
|
||||||
if (node_tags[range] != .switch_range) continue;
|
|
||||||
|
|
||||||
if (range_i == s.item) switch (range_expand) {
|
.multi,
|
||||||
.none => return LazySrcLoc.nodeOffset(
|
.range,
|
||||||
decl.nodeIndexToRelative(range),
|
=> |m| if (is_multi and m.prong == multi_i) break case_node,
|
||||||
),
|
|
||||||
.first => return LazySrcLoc.nodeOffset(
|
.special,
|
||||||
decl.nodeIndexToRelative(node_datas[range].lhs),
|
.special_capture,
|
||||||
),
|
.special_tag_capture,
|
||||||
.last => return LazySrcLoc.nodeOffset(
|
=> {},
|
||||||
decl.nodeIndexToRelative(node_datas[range].rhs),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
range_i += 1;
|
|
||||||
} else unreachable;
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_multi) {
|
if (is_multi) {
|
||||||
multi_i += 1;
|
multi_i += 1;
|
||||||
} else {
|
} else {
|
||||||
scalar_i += 1;
|
scalar_i += 1;
|
||||||
}
|
}
|
||||||
} else unreachable;
|
} else unreachable;
|
||||||
|
|
||||||
|
const case = tree.fullSwitchCase(case_node).?;
|
||||||
|
|
||||||
|
switch (prong_src) {
|
||||||
|
.scalar, .special => return LazySrcLoc.nodeOffset(
|
||||||
|
decl.nodeIndexToRelative(case.ast.values[0]),
|
||||||
|
),
|
||||||
|
.multi => |m| {
|
||||||
|
var item_i: u32 = 0;
|
||||||
|
for (case.ast.values) |item_node| {
|
||||||
|
if (node_tags[item_node] == .switch_range) continue;
|
||||||
|
if (item_i == m.item) return LazySrcLoc.nodeOffset(
|
||||||
|
decl.nodeIndexToRelative(item_node),
|
||||||
|
);
|
||||||
|
item_i += 1;
|
||||||
|
}
|
||||||
|
unreachable;
|
||||||
|
},
|
||||||
|
.range => |m| {
|
||||||
|
var range_i: u32 = 0;
|
||||||
|
for (case.ast.values) |range| {
|
||||||
|
if (node_tags[range] != .switch_range) continue;
|
||||||
|
if (range_i == m.item) switch (range_expand) {
|
||||||
|
.none => return LazySrcLoc.nodeOffset(
|
||||||
|
decl.nodeIndexToRelative(range),
|
||||||
|
),
|
||||||
|
.first => return LazySrcLoc.nodeOffset(
|
||||||
|
decl.nodeIndexToRelative(node_datas[range].lhs),
|
||||||
|
),
|
||||||
|
.last => return LazySrcLoc.nodeOffset(
|
||||||
|
decl.nodeIndexToRelative(node_datas[range].rhs),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
range_i += 1;
|
||||||
|
}
|
||||||
|
unreachable;
|
||||||
|
},
|
||||||
|
.scalar_capture, .multi_capture, .special_capture => {
|
||||||
|
return .{ .node_offset_switch_prong_capture = decl.nodeIndexToRelative(case_node) };
|
||||||
|
},
|
||||||
|
.scalar_tag_capture, .multi_tag_capture, .special_tag_capture => {
|
||||||
|
return .{ .node_offset_switch_prong_tag_capture = decl.nodeIndexToRelative(case_node) };
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
1393
src/Sema.zig
1393
src/Sema.zig
File diff suppressed because it is too large
Load Diff
216
src/Zir.zig
216
src/Zir.zig
@ -667,38 +667,9 @@ pub const Inst = struct {
|
|||||||
/// A switch expression. Uses the `pl_node` union field.
|
/// A switch expression. Uses the `pl_node` union field.
|
||||||
/// AST node is the switch, payload is `SwitchBlock`.
|
/// AST node is the switch, payload is `SwitchBlock`.
|
||||||
switch_block,
|
switch_block,
|
||||||
/// Produces the value that will be switched on. For example, for
|
/// A switch expression. Uses the `pl_node` union field.
|
||||||
/// integers, it returns the integer with no modifications. For tagged unions, it
|
/// AST node is the switch, payload is `SwitchBlock`. Operand is a pointer.
|
||||||
/// returns the active enum tag.
|
switch_block_ref,
|
||||||
/// Uses the `un_node` union field.
|
|
||||||
switch_cond,
|
|
||||||
/// Same as `switch_cond`, except the input operand is a pointer to
|
|
||||||
/// what will be switched on.
|
|
||||||
/// Uses the `un_node` union field.
|
|
||||||
switch_cond_ref,
|
|
||||||
/// Produces the capture value for a switch prong.
|
|
||||||
/// Uses the `switch_capture` field.
|
|
||||||
/// If the `prong_index` field is max int, it means this is the capture
|
|
||||||
/// for the else/`_` prong.
|
|
||||||
switch_capture,
|
|
||||||
/// Produces the capture value for a switch prong.
|
|
||||||
/// Result is a pointer to the value.
|
|
||||||
/// Uses the `switch_capture` field.
|
|
||||||
/// If the `prong_index` field is max int, it means this is the capture
|
|
||||||
/// for the else/`_` prong.
|
|
||||||
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 an inline switch prong tag capture.
|
|
||||||
/// Uses the `un_tok` field.
|
|
||||||
switch_capture_tag,
|
|
||||||
/// Given a
|
/// Given a
|
||||||
/// *A returns *A
|
/// *A returns *A
|
||||||
/// *E!A returns *A
|
/// *E!A returns *A
|
||||||
@ -1144,14 +1115,8 @@ pub const Inst = struct {
|
|||||||
.typeof_log2_int_type,
|
.typeof_log2_int_type,
|
||||||
.resolve_inferred_alloc,
|
.resolve_inferred_alloc,
|
||||||
.set_eval_branch_quota,
|
.set_eval_branch_quota,
|
||||||
.switch_capture,
|
|
||||||
.switch_capture_ref,
|
|
||||||
.switch_capture_multi,
|
|
||||||
.switch_capture_multi_ref,
|
|
||||||
.switch_capture_tag,
|
|
||||||
.switch_block,
|
.switch_block,
|
||||||
.switch_cond,
|
.switch_block_ref,
|
||||||
.switch_cond_ref,
|
|
||||||
.array_base_ptr,
|
.array_base_ptr,
|
||||||
.field_base_ptr,
|
.field_base_ptr,
|
||||||
.validate_array_init_ty,
|
.validate_array_init_ty,
|
||||||
@ -1438,14 +1403,8 @@ pub const Inst = struct {
|
|||||||
.slice_length,
|
.slice_length,
|
||||||
.import,
|
.import,
|
||||||
.typeof_log2_int_type,
|
.typeof_log2_int_type,
|
||||||
.switch_capture,
|
|
||||||
.switch_capture_ref,
|
|
||||||
.switch_capture_multi,
|
|
||||||
.switch_capture_multi_ref,
|
|
||||||
.switch_capture_tag,
|
|
||||||
.switch_block,
|
.switch_block,
|
||||||
.switch_cond,
|
.switch_block_ref,
|
||||||
.switch_cond_ref,
|
|
||||||
.array_base_ptr,
|
.array_base_ptr,
|
||||||
.field_base_ptr,
|
.field_base_ptr,
|
||||||
.struct_init_empty,
|
.struct_init_empty,
|
||||||
@ -1696,13 +1655,7 @@ pub const Inst = struct {
|
|||||||
.err_union_code_ptr = .un_node,
|
.err_union_code_ptr = .un_node,
|
||||||
.enum_literal = .str_tok,
|
.enum_literal = .str_tok,
|
||||||
.switch_block = .pl_node,
|
.switch_block = .pl_node,
|
||||||
.switch_cond = .un_node,
|
.switch_block_ref = .pl_node,
|
||||||
.switch_cond_ref = .un_node,
|
|
||||||
.switch_capture = .switch_capture,
|
|
||||||
.switch_capture_ref = .switch_capture,
|
|
||||||
.switch_capture_multi = .switch_capture,
|
|
||||||
.switch_capture_multi_ref = .switch_capture,
|
|
||||||
.switch_capture_tag = .un_tok,
|
|
||||||
.array_base_ptr = .un_node,
|
.array_base_ptr = .un_node,
|
||||||
.field_base_ptr = .un_node,
|
.field_base_ptr = .un_node,
|
||||||
.validate_array_init_ty = .pl_node,
|
.validate_array_init_ty = .pl_node,
|
||||||
@ -2028,9 +1981,10 @@ pub const Inst = struct {
|
|||||||
/// Implements the `@inComptime` builtin.
|
/// Implements the `@inComptime` builtin.
|
||||||
/// `operand` is `src_node: i32`.
|
/// `operand` is `src_node: i32`.
|
||||||
in_comptime,
|
in_comptime,
|
||||||
/// Used as a placeholder for the capture of an `errdefer`.
|
/// Used as a placeholder instruction which is just a dummy index for Sema to replace
|
||||||
/// This is replaced by Sema with the captured value.
|
/// with a specific value. For instance, this is used for the capture of an `errdefer`.
|
||||||
errdefer_err_code,
|
/// This should never appear in a body.
|
||||||
|
value_placeholder,
|
||||||
|
|
||||||
pub const InstData = struct {
|
pub const InstData = struct {
|
||||||
opcode: Extended,
|
opcode: Extended,
|
||||||
@ -2269,10 +2223,6 @@ pub const Inst = struct {
|
|||||||
operand: Ref,
|
operand: Ref,
|
||||||
payload_index: u32,
|
payload_index: u32,
|
||||||
},
|
},
|
||||||
switch_capture: struct {
|
|
||||||
switch_inst: Index,
|
|
||||||
prong_index: u32,
|
|
||||||
},
|
|
||||||
dbg_stmt: LineColumn,
|
dbg_stmt: LineColumn,
|
||||||
/// Used for unary operators which reference an inst,
|
/// Used for unary operators which reference an inst,
|
||||||
/// with an AST node source location.
|
/// with an AST node source location.
|
||||||
@ -2342,7 +2292,6 @@ pub const Inst = struct {
|
|||||||
bool_br,
|
bool_br,
|
||||||
@"unreachable",
|
@"unreachable",
|
||||||
@"break",
|
@"break",
|
||||||
switch_capture,
|
|
||||||
dbg_stmt,
|
dbg_stmt,
|
||||||
inst_node,
|
inst_node,
|
||||||
str_op,
|
str_op,
|
||||||
@ -2681,37 +2630,53 @@ pub const Inst = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// 0. multi_cases_len: u32 // If has_multi_cases is set.
|
/// 0. multi_cases_len: u32 // If has_multi_cases is set.
|
||||||
/// 1. else_body { // If has_else or has_under is set.
|
/// 1. tag_capture_inst: u32 // If any_has_tag_capture is set. Index of instruction prongs use to refer to the inline tag capture.
|
||||||
/// body_len: u32,
|
/// 2. else_body { // If has_else or has_under is set.
|
||||||
/// body member Index for every body_len
|
/// info: ProngInfo,
|
||||||
|
/// body member Index for every info.body_len
|
||||||
/// }
|
/// }
|
||||||
/// 2. scalar_cases: { // for every scalar_cases_len
|
/// 3. scalar_cases: { // for every scalar_cases_len
|
||||||
/// item: Ref,
|
/// item: Ref,
|
||||||
/// body_len: u32,
|
/// info: ProngInfo,
|
||||||
/// body member Index for every body_len
|
/// body member Index for every info.body_len
|
||||||
/// }
|
/// }
|
||||||
/// 3. multi_cases: { // for every multi_cases_len
|
/// 4. multi_cases: { // for every multi_cases_len
|
||||||
/// items_len: u32,
|
/// items_len: u32,
|
||||||
/// ranges_len: u32,
|
/// ranges_len: u32,
|
||||||
/// body_len: u32,
|
/// info: ProngInfo,
|
||||||
/// item: Ref // for every items_len
|
/// item: Ref // for every items_len
|
||||||
/// ranges: { // for every ranges_len
|
/// ranges: { // for every ranges_len
|
||||||
/// item_first: Ref,
|
/// item_first: Ref,
|
||||||
/// item_last: Ref,
|
/// item_last: Ref,
|
||||||
/// }
|
/// }
|
||||||
/// body member Index for every body_len
|
/// body member Index for every info.body_len
|
||||||
/// }
|
/// }
|
||||||
|
///
|
||||||
|
/// When analyzing a case body, the switch instruction itself refers to the
|
||||||
|
/// captured payload. Whether this is captured by reference or by value
|
||||||
|
/// depends on whether the `byref` bit is set for the corresponding body.
|
||||||
pub const SwitchBlock = struct {
|
pub const SwitchBlock = struct {
|
||||||
/// This is always a `switch_cond` or `switch_cond_ref` instruction.
|
/// The operand passed to the `switch` expression. If this is a
|
||||||
/// If it is a `switch_cond_ref` instruction, bits.is_ref is always true.
|
/// `switch_block`, this is the operand value; if `switch_block_ref` it
|
||||||
/// If it is a `switch_cond` instruction, bits.is_ref is always false.
|
/// is a pointer to the operand. `switch_block_ref` is always used if
|
||||||
/// Both `switch_cond` and `switch_cond_ref` return a value, not a pointer,
|
/// any prong has a byref capture.
|
||||||
/// that is useful for the case items, but cannot be used for capture values.
|
|
||||||
/// For the capture values, Sema is expected to find the operand of this operand
|
|
||||||
/// and use that.
|
|
||||||
operand: Ref,
|
operand: Ref,
|
||||||
bits: Bits,
|
bits: Bits,
|
||||||
|
|
||||||
|
/// These are stored in trailing data in `extra` for each prong.
|
||||||
|
pub const ProngInfo = packed struct(u32) {
|
||||||
|
body_len: u28,
|
||||||
|
capture: Capture,
|
||||||
|
is_inline: bool,
|
||||||
|
has_tag_capture: bool,
|
||||||
|
|
||||||
|
pub const Capture = enum(u2) {
|
||||||
|
none,
|
||||||
|
by_val,
|
||||||
|
by_ref,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
pub const Bits = packed struct {
|
pub const Bits = packed struct {
|
||||||
/// If true, one or more prongs have multiple items.
|
/// If true, one or more prongs have multiple items.
|
||||||
has_multi_cases: bool,
|
has_multi_cases: bool,
|
||||||
@ -2719,9 +2684,11 @@ pub const Inst = struct {
|
|||||||
has_else: bool,
|
has_else: bool,
|
||||||
/// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
|
/// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
|
||||||
has_under: bool,
|
has_under: bool,
|
||||||
|
/// If true, at least one prong has an inline tag capture.
|
||||||
|
any_has_tag_capture: bool,
|
||||||
scalar_cases_len: ScalarCasesLen,
|
scalar_cases_len: ScalarCasesLen,
|
||||||
|
|
||||||
pub const ScalarCasesLen = u29;
|
pub const ScalarCasesLen = u28;
|
||||||
|
|
||||||
pub fn specialProng(bits: Bits) SpecialProng {
|
pub fn specialProng(bits: Bits) SpecialProng {
|
||||||
const has_else: u2 = @boolToInt(bits.has_else);
|
const has_else: u2 = @boolToInt(bits.has_else);
|
||||||
@ -2735,103 +2702,10 @@ pub const Inst = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ScalarProng = struct {
|
|
||||||
item: Ref,
|
|
||||||
body: []const Index,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// TODO performance optimization: instead of having this helper method
|
|
||||||
/// change the definition of switch_capture instruction to store extra_index
|
|
||||||
/// instead of prong_index. This way, Sema won't be doing O(N^2) iterations
|
|
||||||
/// over the switch prongs.
|
|
||||||
pub fn getScalarProng(
|
|
||||||
self: SwitchBlock,
|
|
||||||
zir: Zir,
|
|
||||||
extra_end: usize,
|
|
||||||
prong_index: usize,
|
|
||||||
) ScalarProng {
|
|
||||||
var extra_index: usize = extra_end;
|
|
||||||
|
|
||||||
if (self.bits.has_multi_cases) {
|
|
||||||
extra_index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.bits.specialProng() != .none) {
|
|
||||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
|
||||||
extra_index += 1;
|
|
||||||
const body = zir.extra[extra_index..][0..body_len];
|
|
||||||
extra_index += body.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
var scalar_i: usize = 0;
|
|
||||||
while (true) : (scalar_i += 1) {
|
|
||||||
const item = @intToEnum(Ref, zir.extra[extra_index]);
|
|
||||||
extra_index += 1;
|
|
||||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
|
||||||
extra_index += 1;
|
|
||||||
const body = zir.extra[extra_index..][0..body_len];
|
|
||||||
extra_index += body.len;
|
|
||||||
|
|
||||||
if (scalar_i < prong_index) continue;
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.item = item,
|
|
||||||
.body = body,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MultiProng = struct {
|
pub const MultiProng = struct {
|
||||||
items: []const Ref,
|
items: []const Ref,
|
||||||
body: []const Index,
|
body: []const Index,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn getMultiProng(
|
|
||||||
self: SwitchBlock,
|
|
||||||
zir: Zir,
|
|
||||||
extra_end: usize,
|
|
||||||
prong_index: usize,
|
|
||||||
) MultiProng {
|
|
||||||
// +1 for self.bits.has_multi_cases == true
|
|
||||||
var extra_index: usize = extra_end + 1;
|
|
||||||
|
|
||||||
if (self.bits.specialProng() != .none) {
|
|
||||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
|
||||||
extra_index += 1;
|
|
||||||
const body = zir.extra[extra_index..][0..body_len];
|
|
||||||
extra_index += body.len;
|
|
||||||
}
|
|
||||||
|
|
||||||
var scalar_i: usize = 0;
|
|
||||||
while (scalar_i < self.bits.scalar_cases_len) : (scalar_i += 1) {
|
|
||||||
extra_index += 1;
|
|
||||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
|
||||||
extra_index += 1;
|
|
||||||
extra_index += body_len;
|
|
||||||
}
|
|
||||||
var multi_i: u32 = 0;
|
|
||||||
while (true) : (multi_i += 1) {
|
|
||||||
const items_len = zir.extra[extra_index];
|
|
||||||
extra_index += 1;
|
|
||||||
const ranges_len = zir.extra[extra_index];
|
|
||||||
extra_index += 1;
|
|
||||||
const body_len = @truncate(u31, zir.extra[extra_index]);
|
|
||||||
extra_index += 1;
|
|
||||||
const items = zir.refSlice(extra_index, items_len);
|
|
||||||
extra_index += items_len;
|
|
||||||
// Each range has a start and an end.
|
|
||||||
extra_index += 2 * ranges_len;
|
|
||||||
|
|
||||||
const body = zir.extra[extra_index..][0..body_len];
|
|
||||||
extra_index += body_len;
|
|
||||||
|
|
||||||
if (multi_i < prong_index) continue;
|
|
||||||
return .{
|
|
||||||
.items = items,
|
|
||||||
.body = body,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Field = struct {
|
pub const Field = struct {
|
||||||
|
|||||||
@ -222,8 +222,6 @@ const Writer = struct {
|
|||||||
.bit_reverse,
|
.bit_reverse,
|
||||||
.@"resume",
|
.@"resume",
|
||||||
.@"await",
|
.@"await",
|
||||||
.switch_cond,
|
|
||||||
.switch_cond_ref,
|
|
||||||
.array_base_ptr,
|
.array_base_ptr,
|
||||||
.field_base_ptr,
|
.field_base_ptr,
|
||||||
.validate_struct_init_ty,
|
.validate_struct_init_ty,
|
||||||
@ -235,7 +233,6 @@ const Writer = struct {
|
|||||||
.ref,
|
.ref,
|
||||||
.ret_implicit,
|
.ret_implicit,
|
||||||
.closure_capture,
|
.closure_capture,
|
||||||
.switch_capture_tag,
|
|
||||||
=> try self.writeUnTok(stream, inst),
|
=> try self.writeUnTok(stream, inst),
|
||||||
|
|
||||||
.bool_br_and,
|
.bool_br_and,
|
||||||
@ -389,7 +386,9 @@ const Writer = struct {
|
|||||||
.error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
|
.error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
|
||||||
.error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
|
.error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
|
||||||
|
|
||||||
.switch_block => try self.writeSwitchBlock(stream, inst),
|
.switch_block,
|
||||||
|
.switch_block_ref,
|
||||||
|
=> try self.writeSwitchBlock(stream, inst),
|
||||||
|
|
||||||
.field_ptr,
|
.field_ptr,
|
||||||
.field_ptr_init,
|
.field_ptr_init,
|
||||||
@ -436,12 +435,6 @@ const Writer = struct {
|
|||||||
|
|
||||||
.@"unreachable" => try self.writeUnreachable(stream, inst),
|
.@"unreachable" => try self.writeUnreachable(stream, inst),
|
||||||
|
|
||||||
.switch_capture,
|
|
||||||
.switch_capture_ref,
|
|
||||||
.switch_capture_multi,
|
|
||||||
.switch_capture_multi_ref,
|
|
||||||
=> try self.writeSwitchCapture(stream, inst),
|
|
||||||
|
|
||||||
.dbg_stmt => try self.writeDbgStmt(stream, inst),
|
.dbg_stmt => try self.writeDbgStmt(stream, inst),
|
||||||
|
|
||||||
.dbg_block_begin,
|
.dbg_block_begin,
|
||||||
@ -469,7 +462,7 @@ const Writer = struct {
|
|||||||
.breakpoint,
|
.breakpoint,
|
||||||
.c_va_start,
|
.c_va_start,
|
||||||
.in_comptime,
|
.in_comptime,
|
||||||
.errdefer_err_code,
|
.value_placeholder,
|
||||||
=> try self.writeExtNode(stream, extended),
|
=> try self.writeExtNode(stream, extended),
|
||||||
|
|
||||||
.builtin_src => {
|
.builtin_src => {
|
||||||
@ -1903,8 +1896,19 @@ const Writer = struct {
|
|||||||
break :blk multi_cases_len;
|
break :blk multi_cases_len;
|
||||||
} else 0;
|
} else 0;
|
||||||
|
|
||||||
|
const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
|
||||||
|
const tag_capture_inst = self.code.extra[extra_index];
|
||||||
|
extra_index += 1;
|
||||||
|
break :blk tag_capture_inst;
|
||||||
|
} else undefined;
|
||||||
|
|
||||||
try self.writeInstRef(stream, extra.data.operand);
|
try self.writeInstRef(stream, extra.data.operand);
|
||||||
|
|
||||||
|
if (extra.data.bits.any_has_tag_capture) {
|
||||||
|
try stream.writeAll(", tag_capture=");
|
||||||
|
try self.writeInstIndex(stream, tag_capture_inst);
|
||||||
|
}
|
||||||
|
|
||||||
self.indent += 2;
|
self.indent += 2;
|
||||||
|
|
||||||
else_prong: {
|
else_prong: {
|
||||||
@ -1915,15 +1919,20 @@ const Writer = struct {
|
|||||||
else => break :else_prong,
|
else => break :else_prong,
|
||||||
};
|
};
|
||||||
|
|
||||||
const body_len = @truncate(u31, self.code.extra[extra_index]);
|
const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, self.code.extra[extra_index]);
|
||||||
const inline_text = if (self.code.extra[extra_index] >> 31 != 0) "inline " else "";
|
const capture_text = switch (info.capture) {
|
||||||
|
.none => "",
|
||||||
|
.by_val => "by_val ",
|
||||||
|
.by_ref => "by_ref ",
|
||||||
|
};
|
||||||
|
const inline_text = if (info.is_inline) "inline " else "";
|
||||||
extra_index += 1;
|
extra_index += 1;
|
||||||
const body = self.code.extra[extra_index..][0..body_len];
|
const body = self.code.extra[extra_index..][0..info.body_len];
|
||||||
extra_index += body.len;
|
extra_index += body.len;
|
||||||
|
|
||||||
try stream.writeAll(",\n");
|
try stream.writeAll(",\n");
|
||||||
try stream.writeByteNTimes(' ', self.indent);
|
try stream.writeByteNTimes(' ', self.indent);
|
||||||
try stream.print("{s}{s} => ", .{ inline_text, prong_name });
|
try stream.print("{s}{s}{s} => ", .{ capture_text, inline_text, prong_name });
|
||||||
try self.writeBracedBody(stream, body);
|
try self.writeBracedBody(stream, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1933,15 +1942,19 @@ const Writer = struct {
|
|||||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||||
const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
|
const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
|
||||||
extra_index += 1;
|
extra_index += 1;
|
||||||
const body_len = @truncate(u31, self.code.extra[extra_index]);
|
const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, self.code.extra[extra_index]);
|
||||||
const is_inline = self.code.extra[extra_index] >> 31 != 0;
|
|
||||||
extra_index += 1;
|
extra_index += 1;
|
||||||
const body = self.code.extra[extra_index..][0..body_len];
|
const body = self.code.extra[extra_index..][0..info.body_len];
|
||||||
extra_index += body_len;
|
extra_index += info.body_len;
|
||||||
|
|
||||||
try stream.writeAll(",\n");
|
try stream.writeAll(",\n");
|
||||||
try stream.writeByteNTimes(' ', self.indent);
|
try stream.writeByteNTimes(' ', self.indent);
|
||||||
if (is_inline) try stream.writeAll("inline ");
|
switch (info.capture) {
|
||||||
|
.none => {},
|
||||||
|
.by_val => try stream.writeAll("by_val "),
|
||||||
|
.by_ref => try stream.writeAll("by_ref "),
|
||||||
|
}
|
||||||
|
if (info.is_inline) try stream.writeAll("inline ");
|
||||||
try self.writeInstRef(stream, item_ref);
|
try self.writeInstRef(stream, item_ref);
|
||||||
try stream.writeAll(" => ");
|
try stream.writeAll(" => ");
|
||||||
try self.writeBracedBody(stream, body);
|
try self.writeBracedBody(stream, body);
|
||||||
@ -1954,15 +1967,19 @@ const Writer = struct {
|
|||||||
extra_index += 1;
|
extra_index += 1;
|
||||||
const ranges_len = self.code.extra[extra_index];
|
const ranges_len = self.code.extra[extra_index];
|
||||||
extra_index += 1;
|
extra_index += 1;
|
||||||
const body_len = @truncate(u31, self.code.extra[extra_index]);
|
const info = @bitCast(Zir.Inst.SwitchBlock.ProngInfo, self.code.extra[extra_index]);
|
||||||
const is_inline = self.code.extra[extra_index] >> 31 != 0;
|
|
||||||
extra_index += 1;
|
extra_index += 1;
|
||||||
const items = self.code.refSlice(extra_index, items_len);
|
const items = self.code.refSlice(extra_index, items_len);
|
||||||
extra_index += items_len;
|
extra_index += items_len;
|
||||||
|
|
||||||
try stream.writeAll(",\n");
|
try stream.writeAll(",\n");
|
||||||
try stream.writeByteNTimes(' ', self.indent);
|
try stream.writeByteNTimes(' ', self.indent);
|
||||||
if (is_inline) try stream.writeAll("inline ");
|
switch (info.capture) {
|
||||||
|
.none => {},
|
||||||
|
.by_val => try stream.writeAll("by_val "),
|
||||||
|
.by_ref => try stream.writeAll("by_ref "),
|
||||||
|
}
|
||||||
|
if (info.is_inline) try stream.writeAll("inline ");
|
||||||
|
|
||||||
for (items, 0..) |item_ref, item_i| {
|
for (items, 0..) |item_ref, item_i| {
|
||||||
if (item_i != 0) try stream.writeAll(", ");
|
if (item_i != 0) try stream.writeAll(", ");
|
||||||
@ -1984,8 +2001,8 @@ const Writer = struct {
|
|||||||
try self.writeInstRef(stream, item_last);
|
try self.writeInstRef(stream, item_last);
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = self.code.extra[extra_index..][0..body_len];
|
const body = self.code.extra[extra_index..][0..info.body_len];
|
||||||
extra_index += body_len;
|
extra_index += info.body_len;
|
||||||
try stream.writeAll(" => ");
|
try stream.writeAll(" => ");
|
||||||
try self.writeBracedBody(stream, body);
|
try self.writeBracedBody(stream, body);
|
||||||
}
|
}
|
||||||
@ -2437,12 +2454,6 @@ const Writer = struct {
|
|||||||
try self.writeSrc(stream, src);
|
try self.writeSrc(stream, src);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeSwitchCapture(self: *Writer, stream: anytype, inst: Zir.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 writeDbgStmt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
fn writeDbgStmt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||||
const inst_data = self.code.instructions.items(.data)[inst].dbg_stmt;
|
const inst_data = self.code.instructions.items(.data)[inst].dbg_stmt;
|
||||||
try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 });
|
try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 });
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
const expect = std.testing.expect;
|
const expect = std.testing.expect;
|
||||||
const expectError = std.testing.expectError;
|
const expectError = std.testing.expectError;
|
||||||
const expectEqual = std.testing.expectEqual;
|
const expectEqual = std.testing.expectEqual;
|
||||||
@ -717,3 +718,70 @@ test "comptime inline switch" {
|
|||||||
|
|
||||||
try expectEqual(u32, value);
|
try expectEqual(u32, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "switch capture peer type resolution" {
|
||||||
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||||
|
|
||||||
|
const U = union(enum) {
|
||||||
|
a: u32,
|
||||||
|
b: u64,
|
||||||
|
fn innerVal(u: @This()) u64 {
|
||||||
|
switch (u) {
|
||||||
|
.a, .b => |x| return x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try expectEqual(@as(u64, 100), U.innerVal(.{ .a = 100 }));
|
||||||
|
try expectEqual(@as(u64, 200), U.innerVal(.{ .b = 200 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "switch capture peer type resolution for in-memory coercible payloads" {
|
||||||
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||||
|
|
||||||
|
const T1 = c_int;
|
||||||
|
const T2 = @Type(@typeInfo(T1));
|
||||||
|
|
||||||
|
comptime assert(T1 != T2);
|
||||||
|
|
||||||
|
const U = union(enum) {
|
||||||
|
a: T1,
|
||||||
|
b: T2,
|
||||||
|
fn innerVal(u: @This()) c_int {
|
||||||
|
switch (u) {
|
||||||
|
.a, .b => |x| return x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try expectEqual(@as(c_int, 100), U.innerVal(.{ .a = 100 }));
|
||||||
|
try expectEqual(@as(c_int, 200), U.innerVal(.{ .b = 200 }));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "switch pointer capture peer type resolution" {
|
||||||
|
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
|
||||||
|
|
||||||
|
const T1 = c_int;
|
||||||
|
const T2 = @Type(@typeInfo(T1));
|
||||||
|
|
||||||
|
comptime assert(T1 != T2);
|
||||||
|
|
||||||
|
const U = union(enum) {
|
||||||
|
a: T1,
|
||||||
|
b: T2,
|
||||||
|
fn innerVal(u: *@This()) *c_int {
|
||||||
|
switch (u.*) {
|
||||||
|
.a, .b => |*ptr| return ptr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var ua: U = .{ .a = 100 };
|
||||||
|
var ub: U = .{ .b = 200 };
|
||||||
|
|
||||||
|
ua.innerVal().* = 111;
|
||||||
|
ub.innerVal().* = 222;
|
||||||
|
|
||||||
|
try expectEqual(U{ .a = 111 }, ua);
|
||||||
|
try expectEqual(U{ .b = 222 }, ub);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
const Union = union(enum) {
|
|
||||||
A: usize,
|
|
||||||
B: isize,
|
|
||||||
};
|
|
||||||
comptime {
|
|
||||||
var u = Union{ .A = 8 };
|
|
||||||
switch (u) {
|
|
||||||
.A, .B => |e| {
|
|
||||||
_ = e;
|
|
||||||
unreachable;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// error
|
|
||||||
// backend=stage2
|
|
||||||
// target=native
|
|
||||||
//
|
|
||||||
// :8:20: error: capture group with incompatible types
|
|
||||||
// :8:10: note: type 'usize' here
|
|
||||||
// :8:14: note: type 'isize' here
|
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
export fn f() void {
|
||||||
|
const U = union(enum) { a: u32, b: *u8 };
|
||||||
|
var u: U = undefined;
|
||||||
|
switch (u) {
|
||||||
|
.a, .b => |val| _ = val,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn g() void {
|
||||||
|
const U = union(enum) { a: u64, b: u32 };
|
||||||
|
var u: U = undefined;
|
||||||
|
switch (u) {
|
||||||
|
.a, .b => |*ptr| _ = ptr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// error
|
||||||
|
// backend=stage2
|
||||||
|
// target=native
|
||||||
|
//
|
||||||
|
// :5:20: error: capture group with incompatible types
|
||||||
|
// :5:20: note: incompatible types: 'u32' and '*u8'
|
||||||
|
// :5:10: note: type 'u32' here
|
||||||
|
// :5:14: note: type '*u8' here
|
||||||
|
// :13:20: error: capture group with incompatible types
|
||||||
|
// :13:14: note: pointer type child 'u32' cannot cast into resolved pointer type child 'u64'
|
||||||
|
// :13:20: note: this coercion is only possible when capturing by value
|
||||||
@ -4,12 +4,12 @@ const Payload = union {
|
|||||||
C: bool,
|
C: bool,
|
||||||
};
|
};
|
||||||
export fn entry() void {
|
export fn entry() void {
|
||||||
const a = Payload { .A = 1234 };
|
const a = Payload{ .A = 1234 };
|
||||||
foo(&a);
|
foo(&a);
|
||||||
}
|
}
|
||||||
fn foo(a: *const Payload) void {
|
fn foo(a: *const Payload) void {
|
||||||
switch (a.*) {
|
switch (a.*) {
|
||||||
Payload.A => {},
|
.A => {},
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user