aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-10-19 20:14:10 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-10-19 20:22:47 -0700
commitdfb3231959bb340d260ddbec2b8eabfb5063c1bf (patch)
tree1f91b36fc9ed4b0aaa35fd8f8657efb365f3ec74 /src
parent4a76523b92bcf0e9b48438cb22f49e67e0ab3fa1 (diff)
downloadzig-dfb3231959bb340d260ddbec2b8eabfb5063c1bf.tar.gz
zig-dfb3231959bb340d260ddbec2b8eabfb5063c1bf.zip
stage2: implement switching on unions
* AstGen: Move `refToIndex` and `indexToRef` to Zir * ZIR: the switch_block_*_* instruction tags are collapsed into one switch_block tag which uses 4 bits for flags, and reduces the scalar_cases_len field from 32 to 28 bits. This freed up more ZIR tags, 2 of which are now used for `switch_cond` and `switch_cond_ref` for producing the switch condition value. For example, for union values it returns the corresponding enum value. * switching with multiple cases and ranges is not yet supported because I want to change the ZIR encoding to store index pointers into the extra array rather than storing prong indexes. This will avoid O(N^2) iteration over prongs. * AstGen now adds a `switch_cond` on the operand and then passes the result of that to the `switch_block` instruction. * Sema: partially implement `switch_capture_*` instructions. * Sema: `unionToTag` notices if the enum type has only one possible value.
Diffstat (limited to 'src')
-rw-r--r--src/AstGen.zig102
-rw-r--r--src/Sema.zig331
-rw-r--r--src/Zir.zig272
-rw-r--r--src/print_zir.zig125
-rw-r--r--src/value.zig2
5 files changed, 390 insertions, 442 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig
index 83d82f4083..d1f65b75ba 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -11,6 +11,8 @@ const StringIndexAdapter = std.hash_map.StringIndexAdapter;
const StringIndexContext = std.hash_map.StringIndexContext;
const Zir = @import("Zir.zig");
+const refToIndex = Zir.refToIndex;
+const indexToRef = Zir.indexToRef;
const trace = @import("tracy.zig").trace;
const BuiltinFn = @import("BuiltinFn.zig");
@@ -57,6 +59,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
i32 => @bitCast(u32, @field(extra, field.name)),
Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),
+ Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)),
else => @compileError("bad field type"),
});
}
@@ -2133,17 +2136,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.slice_sentinel,
.import,
.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_cond,
+ .switch_cond_ref,
.switch_capture,
.switch_capture_ref,
.switch_capture_multi,
@@ -5127,11 +5121,12 @@ fn fieldAccess(
rl: ResultLoc,
node: Ast.Node.Index,
) InnerError!Zir.Inst.Ref {
- if (rl == .ref) {
- return addFieldAccess(.field_ptr, gz, scope, .ref, node);
- } else {
- const access = try addFieldAccess(.field_val, gz, scope, .none, node);
- return rvalue(gz, rl, access, node);
+ switch (rl) {
+ .ref => return addFieldAccess(.field_ptr, gz, scope, .ref, node),
+ else => {
+ const access = try addFieldAccess(.field_val, gz, scope, .none, node);
+ return rvalue(gz, rl, access, node);
+ },
}
}
@@ -6028,11 +6023,13 @@ fn switchExpr(
}
const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
- const operand = try expr(parent_gz, scope, operand_rl, operand_node);
+ const raw_operand = try expr(parent_gz, scope, operand_rl, operand_node);
+ const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
+ const cond = try parent_gz.addUnNode(cond_tag, raw_operand, 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 cond_ty_inst = try parent_gz.addUnNode(typeof_tag, cond, operand_node);
+ const item_rl: ResultLoc = .{ .ty = cond_ty_inst };
// These contain the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti.
// This is the optional else prong body.
@@ -6050,7 +6047,7 @@ fn switchExpr(
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);
+ const switch_block = try parent_gz.addBlock(.switch_block, switch_node);
// We re-use this same scope for all cases, including the special prong, if any.
var case_scope = parent_gz.makeSubBlock(&block_scope.base);
@@ -6203,44 +6200,32 @@ fn switchExpr(
// 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 payload_index = astgen.extra.items.len;
- const zir_datas = astgen.instructions.items(.data);
- zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index);
- // Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`.
- try astgen.extra.ensureUnusedCapacity(gpa, @as(usize, 2) + // operand, scalar_cases_len
+ try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
@boolToInt(multi_cases_len != 0) +
special_case_payload.items.len +
scalar_cases_payload.items.len +
multi_cases_payload.items.len);
- astgen.extra.appendAssumeCapacity(@enumToInt(operand));
- astgen.extra.appendAssumeCapacity(scalar_cases_len);
+
+ const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
+ .operand = cond,
+ .bits = Zir.Inst.SwitchBlock.Bits{
+ .is_ref = any_payload_is_ref,
+ .has_multi_cases = multi_cases_len != 0,
+ .has_else = special_prong == .@"else",
+ .has_under = special_prong == .under,
+ .scalar_cases_len = @intCast(u28, scalar_cases_len),
+ },
+ });
+
+ const zir_datas = astgen.instructions.items(.data);
+ const zir_tags = astgen.instructions.items(.tag);
+
+ zir_datas[switch_block].pl_node.payload_index = payload_index;
+
if (multi_cases_len != 0) {
astgen.extra.appendAssumeCapacity(multi_cases_len);
}
+
const strat = rl.strategy(&block_scope);
switch (strat.tag) {
.break_operand => {
@@ -10622,21 +10607,6 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {
astgen.source_column = column;
}
-const ref_start_index: u32 = Zir.Inst.Ref.typed_value_map.len;
-
-fn indexToRef(inst: Zir.Inst.Index) Zir.Inst.Ref {
- return @intToEnum(Zir.Inst.Ref, ref_start_index + inst);
-}
-
-fn refToIndex(inst: Zir.Inst.Ref) ?Zir.Inst.Index {
- const ref_int = @enumToInt(inst);
- if (ref_int >= ref_start_index) {
- return ref_int - ref_start_index;
- } else {
- return null;
- }
-}
-
fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !void {
const gpa = astgen.gpa;
const tree = astgen.tree;
diff --git a/src/Sema.zig b/src/Sema.zig
index 12da483e24..f34b078af5 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -550,18 +550,9 @@ pub fn analyzeBody(
.slice_sentinel => try sema.zirSliceSentinel(block, inst),
.slice_start => try sema.zirSliceStart(block, inst),
.str => try sema.zirStr(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_block => try sema.zirSwitchBlock(block, inst),
+ .switch_cond => try sema.zirSwitchCond(block, inst, false),
+ .switch_cond_ref => try sema.zirSwitchCond(block, inst, true),
.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),
@@ -5433,11 +5424,80 @@ fn zirSwitchCapture(
const zir_datas = sema.code.instructions.items(.data);
const capture_info = zir_datas[inst].switch_capture;
const switch_info = zir_datas[capture_info.switch_inst].pl_node;
- const src = switch_info.src();
+ const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);
+ const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };
+ const switch_src = switch_info.src();
+ const operand_is_ref = switch_extra.data.bits.is_ref;
+ const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;
+ const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node;
+ const operand_ptr = sema.resolveInst(cond_info.operand);
+ const operand_ptr_ty = sema.typeOf(operand_ptr);
+ const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
+
+ if (is_multi) {
+ return sema.fail(block, switch_src, "TODO implement Sema for switch capture multi", .{});
+ }
+ const scalar_prong = switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index);
+ const item = sema.resolveInst(scalar_prong.item);
+ // Previous switch validation ensured this will succeed
+ const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable;
- _ = is_ref;
- _ = is_multi;
- return sema.fail(block, src, "TODO implement Sema for zirSwitchCapture", .{});
+ switch (operand_ty.zigTypeTag()) {
+ .Union => {
+ const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
+ const enum_ty = union_obj.tag_ty;
+
+ const field_index_usize = enum_ty.enumTagFieldIndex(item_val).?;
+ const field_index = @intCast(u32, field_index_usize);
+ const field = union_obj.fields.values()[field_index];
+
+ // TODO handle multiple union tags which have compatible types
+
+ if (is_ref) {
+ assert(operand_is_ref);
+
+ const field_ty_ptr = try Type.ptr(sema.arena, .{
+ .pointee_type = field.ty,
+ .@"addrspace" = .generic,
+ .mutable = operand_ptr_ty.ptrIsMutable(),
+ });
+
+ if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| {
+ return sema.addConstant(
+ field_ty_ptr,
+ try Value.Tag.field_ptr.create(sema.arena, .{
+ .container_ptr = op_ptr_val,
+ .field_index = field_index,
+ }),
+ );
+ }
+ try sema.requireRuntimeBlock(block, operand_src);
+ return block.addStructFieldPtr(operand_ptr, field_index, field.ty);
+ }
+
+ const operand = if (operand_is_ref)
+ try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
+ else
+ operand_ptr;
+
+ if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
+ return sema.addConstant(
+ field.ty,
+ operand_val.castTag(.@"union").?.data.val,
+ );
+ }
+ try sema.requireRuntimeBlock(block, operand_src);
+ return block.addStructFieldVal(operand, field_index, field.ty);
+ },
+ .ErrorSet => {
+ return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCapture for error sets", .{});
+ },
+ else => {
+ return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{
+ operand_ty,
+ });
+ },
+ }
}
fn zirSwitchCaptureElse(
@@ -5452,96 +5512,108 @@ fn zirSwitchCaptureElse(
const zir_datas = sema.code.instructions.items(.data);
const capture_info = zir_datas[inst].switch_capture;
const switch_info = zir_datas[capture_info.switch_inst].pl_node;
+ const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index).data;
const src = switch_info.src();
+ const operand_is_ref = switch_extra.bits.is_ref;
+ assert(!is_ref or operand_is_ref);
- _ = is_ref;
return sema.fail(block, src, "TODO implement Sema for zirSwitchCaptureElse", .{});
}
-fn zirSwitchBlock(
+fn zirSwitchCond(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
is_ref: bool,
- special_prong: Zir.SpecialProng,
) CompileError!Air.Inst.Ref {
- const tracy = trace(@src());
- defer tracy.end();
-
- const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+ const inst_data = sema.code.instructions.items(.data)[inst].un_node;
const src = inst_data.src();
- const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
- const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
+ const operand_ptr = sema.resolveInst(inst_data.operand);
+ const operand = if (is_ref) try sema.analyzeLoad(block, src, operand_ptr, src) else operand_ptr;
+ const operand_ty = sema.typeOf(operand);
- const operand_ptr = sema.resolveInst(extra.data.operand);
- const operand = if (is_ref)
- try sema.analyzeLoad(block, src, operand_ptr, operand_src)
- else
- operand_ptr;
+ switch (operand_ty.zigTypeTag()) {
+ .Type,
+ .Void,
+ .Bool,
+ .Int,
+ .Float,
+ .ComptimeFloat,
+ .ComptimeInt,
+ .EnumLiteral,
+ .Pointer,
+ .Fn,
+ .ErrorSet,
+ .Enum,
+ => {
+ if ((try sema.typeHasOnePossibleValue(block, src, operand_ty))) |opv| {
+ return sema.addConstant(operand_ty, opv);
+ }
+ return operand;
+ },
- return sema.analyzeSwitch(
- block,
- operand,
- extra.end,
- special_prong,
- extra.data.cases_len,
- 0,
- inst,
- inst_data.src_node,
- );
+ .Union => {
+ const enum_ty = operand_ty.unionTagType() orelse {
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "switch on untagged union", .{});
+ errdefer msg.destroy(sema.gpa);
+ try sema.addDeclaredHereNote(msg, operand_ty);
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+ };
+ return sema.unionToTag(block, enum_ty, operand, src);
+ },
+
+ .ErrorUnion,
+ .NoReturn,
+ .Array,
+ .Struct,
+ .Undefined,
+ .Null,
+ .Optional,
+ .BoundFn,
+ .Opaque,
+ .Vector,
+ .Frame,
+ .AnyFrame,
+ => return sema.fail(block, src, "switch on type '{}'", .{operand_ty}),
+ }
}
-fn zirSwitchBlockMulti(
- sema: *Sema,
- block: *Block,
- inst: Zir.Inst.Index,
- is_ref: bool,
- special_prong: Zir.SpecialProng,
-) CompileError!Air.Inst.Ref {
+fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const tracy = trace(@src());
defer tracy.end();
+ const gpa = sema.gpa;
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
const src = inst_data.src();
- const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
- const extra = sema.code.extraData(Zir.Inst.SwitchBlockMulti, inst_data.payload_index);
+ const src_node_offset = inst_data.src_node;
+ const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
+ const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
+ const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
const operand_ptr = sema.resolveInst(extra.data.operand);
- const operand = if (is_ref)
+ const operand = if (extra.data.bits.is_ref)
try sema.analyzeLoad(block, src, operand_ptr, operand_src)
else
operand_ptr;
- return sema.analyzeSwitch(
- block,
- operand,
- extra.end,
- special_prong,
- extra.data.scalar_cases_len,
- extra.data.multi_cases_len,
- inst,
- inst_data.src_node,
- );
-}
+ var header_extra_index: usize = extra.end;
-fn analyzeSwitch(
- sema: *Sema,
- block: *Block,
- operand: Air.Inst.Ref,
- extra_end: usize,
- special_prong: Zir.SpecialProng,
- scalar_cases_len: usize,
- multi_cases_len: usize,
- switch_inst: Zir.Inst.Index,
- src_node_offset: i32,
-) CompileError!Air.Inst.Ref {
- const gpa = sema.gpa;
+ const scalar_cases_len = extra.data.bits.scalar_cases_len;
+ const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
+ const multi_cases_len = sema.code.extra[header_extra_index];
+ header_extra_index += 1;
+ break :blk multi_cases_len;
+ } else 0;
+ const special_prong = extra.data.bits.specialProng();
const special: struct { body: []const Zir.Inst.Index, end: usize } = switch (special_prong) {
- .none => .{ .body = &.{}, .end = extra_end },
+ .none => .{ .body = &.{}, .end = header_extra_index },
.under, .@"else" => blk: {
- const body_len = sema.code.extra[extra_end];
- const extra_body_start = extra_end + 1;
+ const body_len = sema.code.extra[header_extra_index];
+ const extra_body_start = header_extra_index + 1;
break :blk .{
.body = sema.code.extra[extra_body_start..][0..body_len],
.end = extra_body_start + body_len,
@@ -5549,9 +5621,6 @@ fn analyzeSwitch(
},
};
- const src: LazySrcLoc = .{ .node_offset = src_node_offset };
- const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
- const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
const operand_ty = sema.typeOf(operand);
// Validate usage of '_' prongs.
@@ -5945,7 +6014,7 @@ fn analyzeSwitch(
.data = undefined,
});
var label: Block.Label = .{
- .zir_block = switch_inst,
+ .zir_block = inst,
.merges = .{
.results = .{},
.br_list = .{},
@@ -8934,8 +9003,9 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
- const field_index = union_obj.fields.getIndex(field_name) orelse
+ const field_index_usize = union_obj.fields.getIndex(field_name) orelse
return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
+ const field_index = @intCast(u32, field_index_usize);
if (is_ref) {
return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true union", .{});
@@ -8943,12 +9013,10 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
const init_inst = sema.resolveInst(item.data.init);
if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| {
+ const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
return sema.addConstant(
resolved_ty,
- try Value.Tag.@"union".create(sema.arena, .{
- .tag = try Value.Tag.int_u64.create(sema.arena, field_index),
- .val = val,
- }),
+ try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, .val = val }),
);
}
return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known union values", .{});
@@ -9152,8 +9220,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
const val = try sema.resolveConstValue(block, operand_src, type_info);
const union_val = val.cast(Value.Payload.Union).?.data;
- const TypeInfoTag = std.meta.Tag(std.builtin.TypeInfo);
- const tag_index = @intCast(std.meta.Tag(TypeInfoTag), union_val.tag.toUnsignedInt());
+ const tag_ty = type_info_ty.unionTagType().?;
+ const tag_index = tag_ty.enumTagFieldIndex(union_val.tag).?;
switch (@intToEnum(std.builtin.TypeId, tag_index)) {
.Type => return Air.Inst.Ref.type_type,
.Void => return Air.Inst.Ref.void_type,
@@ -10819,10 +10887,39 @@ fn fieldVal(
try Value.Tag.@"error".create(arena, .{ .name = name }),
);
},
- .Struct, .Opaque, .Union => {
+ .Union => {
if (child_type.getNamespace()) |namespace| {
- if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
- return sema.analyzeLoad(block, src, inst, src);
+ if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
+ return inst;
+ }
+ }
+ if (child_type.unionTagType()) |enum_ty| {
+ if (enum_ty.enumFieldIndex(field_name)) |field_index_usize| {
+ const field_index = @intCast(u32, field_index_usize);
+ return sema.addConstant(
+ enum_ty,
+ try Value.Tag.enum_field_index.create(sema.arena, field_index),
+ );
+ }
+ }
+ return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
+ },
+ .Enum => {
+ if (child_type.getNamespace()) |namespace| {
+ if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
+ return inst;
+ }
+ }
+ const field_index_usize = child_type.enumFieldIndex(field_name) orelse
+ return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
+ const field_index = @intCast(u32, field_index_usize);
+ const enum_val = try Value.Tag.enum_field_index.create(arena, field_index);
+ return sema.addConstant(try child_type.copy(arena), enum_val);
+ },
+ .Struct, .Opaque => {
+ if (child_type.getNamespace()) |namespace| {
+ if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
+ return inst;
}
}
// TODO add note: declared here
@@ -10836,35 +10933,6 @@ fn fieldVal(
kw_name, child_type, field_name,
});
},
- .Enum => {
- if (child_type.getNamespace()) |namespace| {
- if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
- return sema.analyzeLoad(block, src, inst, src);
- }
- }
- const field_index = child_type.enumFieldIndex(field_name) orelse {
- const msg = msg: {
- const msg = try sema.errMsg(
- block,
- src,
- "enum '{}' has no member named '{s}'",
- .{ child_type, field_name },
- );
- errdefer msg.destroy(sema.gpa);
- try sema.mod.errNoteNonLazy(
- child_type.declSrcLoc(),
- msg,
- "enum declared here",
- .{},
- );
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(msg);
- };
- const field_index_u32 = @intCast(u32, field_index);
- const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);
- return sema.addConstant(try child_type.copy(arena), enum_val);
- },
else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
}
},
@@ -11244,6 +11312,17 @@ fn namespaceLookupRef(
return try sema.analyzeDeclRef(decl);
}
+fn namespaceLookupVal(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ namespace: *Namespace,
+ decl_name: []const u8,
+) CompileError!?Air.Inst.Ref {
+ const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
+ return try sema.analyzeDeclVal(block, src, decl);
+}
+
fn structFieldPtr(
sema: *Sema,
block: *Block,
@@ -11370,10 +11449,9 @@ fn unionFieldVal(
const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
- const field_index_big = union_obj.fields.getIndex(field_name) orelse
+ const field_index_usize = union_obj.fields.getIndex(field_name) orelse
return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
- const field_index = @intCast(u32, field_index_big);
-
+ const field_index = @intCast(u32, field_index_usize);
const field = union_obj.fields.values()[field_index];
if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| {
@@ -12960,15 +13038,18 @@ fn wrapErrorUnion(
fn unionToTag(
sema: *Sema,
block: *Block,
- dest_ty: Type,
+ enum_ty: Type,
un: Air.Inst.Ref,
un_src: LazySrcLoc,
) !Air.Inst.Ref {
+ if ((try sema.typeHasOnePossibleValue(block, un_src, enum_ty))) |opv| {
+ return sema.addConstant(enum_ty, opv);
+ }
if (try sema.resolveMaybeUndefVal(block, un_src, un)) |un_val| {
- return sema.addConstant(dest_ty, un_val.unionTag());
+ return sema.addConstant(enum_ty, un_val.unionTag());
}
try sema.requireRuntimeBlock(block, un_src);
- return block.addTyOp(.get_union_tag, dest_ty, un);
+ return block.addTyOp(.get_union_tag, enum_ty, un);
}
fn resolvePeerTypes(
diff --git a/src/Zir.zig b/src/Zir.zig
index e45aac1a6f..4d8cd57947 100644
--- a/src/Zir.zig
+++ b/src/Zir.zig
@@ -72,6 +72,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en
Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
i32 => @bitCast(i32, code.extra[i]),
Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]),
+ Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]),
else => @compileError("bad field type"),
};
i += 1;
@@ -618,39 +619,16 @@ pub const Inst = struct {
enum_literal,
/// A switch expression. Uses the `pl_node` union field.
/// AST node is the switch, payload is `SwitchBlock`.
- /// All prongs of target handled.
switch_block,
- /// Same as switch_block, except one or more prongs have multiple items.
- /// Payload is `SwitchBlockMulti`
- 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.
- /// Payload is `SwitchBlockMulti`
- 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.
- /// Payload is `SwitchBlockMulti`
- 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.
- /// Payload is `SwitchBlockMulti`
- 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.
- /// Payload is `SwitchBlockMulti`
- switch_block_ref_else_multi,
- /// Same as `switch_block_under` but the target is a pointer to the value
- /// being switched on.
- switch_block_ref_under,
- /// Same as `switch_block_under_multi` but the target is a pointer to
- /// the value being switched on.
- /// Payload is `SwitchBlockMulti`
- switch_block_ref_under_multi,
+ /// Produces the value that will be switched on. For example, for
+ /// integers, it returns the integer with no modifications. For tagged unions, it
+ /// returns the active enum tag.
+ /// 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.
switch_capture,
@@ -1109,17 +1087,8 @@ pub const Inst = struct {
.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,
+ .switch_cond,
+ .switch_cond_ref,
.validate_struct_init,
.validate_array_init,
.struct_init_empty,
@@ -1367,17 +1336,8 @@ pub const Inst = struct {
.ensure_err_payload_void = .un_tok,
.enum_literal = .str_tok,
.switch_block = .pl_node,
- .switch_block_multi = .pl_node,
- .switch_block_else = .pl_node,
- .switch_block_else_multi = .pl_node,
- .switch_block_under = .pl_node,
- .switch_block_under_multi = .pl_node,
- .switch_block_ref = .pl_node,
- .switch_block_ref_multi = .pl_node,
- .switch_block_ref_else = .pl_node,
- .switch_block_ref_else_multi = .pl_node,
- .switch_block_ref_under = .pl_node,
- .switch_block_ref_under_multi = .pl_node,
+ .switch_cond = .un_node,
+ .switch_cond_ref = .un_node,
.switch_capture = .switch_capture,
.switch_capture_ref = .switch_capture,
.switch_capture_multi = .switch_capture,
@@ -2466,37 +2426,17 @@ pub const Inst = struct {
index: u32,
};
- /// This form is supported when there are no ranges, and exactly 1 item per block.
- /// Depending on zir tag and len fields, extra fields trail
- /// this one in the extra array.
- /// 0. else_body { // If the tag has "_else" or "_under" in it.
- /// body_len: u32,
- /// body member Index for every body_len
- /// }
- /// 1. cases: {
- /// item: Ref,
- /// body_len: u32,
- /// body member Index for every body_len
- /// } for every cases_len
- pub const SwitchBlock = struct {
- operand: Ref,
- cases_len: u32,
- };
-
- /// This form is required when there exists a block which has more than one item,
- /// or a range.
- /// Depending on zir tag and len fields, extra fields trail
- /// this one in the extra array.
- /// 0. else_body { // If the tag has "_else" or "_under" in it.
+ /// 0. multi_cases_len: u32 // If has_multi_cases is set.
+ /// 1. else_body { // If has_else or has_under is set.
/// body_len: u32,
/// body member Index for every body_len
/// }
- /// 1. scalar_cases: { // for every scalar_cases_len
+ /// 2. scalar_cases: { // for every scalar_cases_len
/// item: Ref,
/// body_len: u32,
/// body member Index for every body_len
/// }
- /// 2. multi_cases: { // for every multi_cases_len
+ /// 3. multi_cases: { // for every multi_cases_len
/// items_len: u32,
/// ranges_len: u32,
/// body_len: u32,
@@ -2507,10 +2447,78 @@ pub const Inst = struct {
/// }
/// body member Index for every body_len
/// }
- pub const SwitchBlockMulti = struct {
+ pub const SwitchBlock = struct {
operand: Ref,
- scalar_cases_len: u32,
- multi_cases_len: u32,
+ bits: Bits,
+
+ pub const Bits = packed struct {
+ /// If true, one or more prongs have multiple items.
+ has_multi_cases: bool,
+ /// If true, there is an else prong. This is mutually exclusive with `has_under`.
+ has_else: bool,
+ /// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
+ has_under: bool,
+ /// If true, the `operand` is a pointer to the value being switched on.
+ is_ref: bool,
+ scalar_cases_len: u28,
+
+ pub fn specialProng(bits: Bits) SpecialProng {
+ const has_else: u2 = @boolToInt(bits.has_else);
+ const has_under: u2 = @boolToInt(bits.has_under);
+ return switch ((has_else << 1) | has_under) {
+ 0b00 => .none,
+ 0b01 => .under,
+ 0b10 => .@"else",
+ 0b11 => unreachable,
+ };
+ }
+ };
+
+ 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 = 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 = 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 Field = struct {
@@ -2934,7 +2942,7 @@ pub const Inst = struct {
/// Trailing: for each `imports_len` there is an Item
pub const Imports = struct {
- imports_len: Zir.Inst.Index,
+ imports_len: Inst.Index,
pub const Item = struct {
/// null terminated string index
@@ -3077,7 +3085,7 @@ pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclItera
/// The iterator would have to allocate memory anyway to iterate. So here we populate
/// an ArrayList as the result.
-pub fn findDecls(zir: Zir, list: *std.ArrayList(Zir.Inst.Index), decl_sub_index: u32) !void {
+pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_sub_index: u32) !void {
const block_inst = zir.extra[decl_sub_index + 6];
list.clearRetainingCapacity();
@@ -3086,8 +3094,8 @@ pub fn findDecls(zir: Zir, list: *std.ArrayList(Zir.Inst.Index), decl_sub_index:
fn findDeclsInner(
zir: Zir,
- list: *std.ArrayList(Zir.Inst.Index),
- inst: Zir.Inst.Index,
+ list: *std.ArrayList(Inst.Index),
+ inst: Inst.Index,
) Allocator.Error!void {
const tags = zir.instructions.items(.tag);
const datas = zir.instructions.items(.data);
@@ -3148,19 +3156,7 @@ fn findDeclsInner(
try zir.findDeclsBody(list, then_body);
try zir.findDeclsBody(list, else_body);
},
- .switch_block => return findDeclsSwitch(zir, list, inst, .none),
- .switch_block_else => return findDeclsSwitch(zir, list, inst, .@"else"),
- .switch_block_under => return findDeclsSwitch(zir, list, inst, .under),
- .switch_block_ref => return findDeclsSwitch(zir, list, inst, .none),
- .switch_block_ref_else => return findDeclsSwitch(zir, list, inst, .@"else"),
- .switch_block_ref_under => return findDeclsSwitch(zir, list, inst, .under),
-
- .switch_block_multi => return findDeclsSwitchMulti(zir, list, inst, .none),
- .switch_block_else_multi => return findDeclsSwitchMulti(zir, list, inst, .@"else"),
- .switch_block_under_multi => return findDeclsSwitchMulti(zir, list, inst, .under),
- .switch_block_ref_multi => return findDeclsSwitchMulti(zir, list, inst, .none),
- .switch_block_ref_else_multi => return findDeclsSwitchMulti(zir, list, inst, .@"else"),
- .switch_block_ref_under_multi => return findDeclsSwitchMulti(zir, list, inst, .under),
+ .switch_block => return findDeclsSwitch(zir, list, inst),
.suspend_block => @panic("TODO iterate suspend block"),
@@ -3170,71 +3166,34 @@ fn findDeclsInner(
fn findDeclsSwitch(
zir: Zir,
- list: *std.ArrayList(Zir.Inst.Index),
- inst: Zir.Inst.Index,
- special_prong: SpecialProng,
+ list: *std.ArrayList(Inst.Index),
+ inst: Inst.Index,
) Allocator.Error!void {
const inst_data = zir.instructions.items(.data)[inst].pl_node;
const extra = zir.extraData(Inst.SwitchBlock, 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 = zir.extra[extra.end];
- const extra_body_start = extra.end + 1;
- break :blk .{
- .body = zir.extra[extra_body_start..][0..body_len],
- .end = extra_body_start + body_len,
- };
- },
- };
- try zir.findDeclsBody(list, special.body);
+ var extra_index: usize = extra.end;
- var extra_index: usize = special.end;
- var scalar_i: usize = 0;
- while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
+ const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
+ const multi_cases_len = zir.extra[extra_index];
extra_index += 1;
+ break :blk multi_cases_len;
+ } else 0;
+
+ const special_prong = extra.data.bits.specialProng();
+ if (special_prong != .none) {
const body_len = zir.extra[extra_index];
extra_index += 1;
const body = zir.extra[extra_index..][0..body_len];
- extra_index += body_len;
+ extra_index += body.len;
try zir.findDeclsBody(list, body);
}
-}
-fn findDeclsSwitchMulti(
- zir: Zir,
- list: *std.ArrayList(Zir.Inst.Index),
- inst: Zir.Inst.Index,
- special_prong: SpecialProng,
-) Allocator.Error!void {
- const inst_data = zir.instructions.items(.data)[inst].pl_node;
- const extra = zir.extraData(Inst.SwitchBlockMulti, 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 = zir.extra[extra.end];
- const extra_body_start = extra.end + 1;
- break :blk .{
- .body = zir.extra[extra_body_start..][0..body_len],
- .end = extra_body_start + body_len,
- };
- },
- };
-
- try zir.findDeclsBody(list, special.body);
-
- var extra_index: usize = special.end;
{
+ const scalar_cases_len = extra.data.bits.scalar_cases_len;
var scalar_i: usize = 0;
- while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
+ while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
extra_index += 1;
const body_len = zir.extra[extra_index];
extra_index += 1;
@@ -3246,7 +3205,7 @@ fn findDeclsSwitchMulti(
}
{
var multi_i: usize = 0;
- while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) {
+ while (multi_i < multi_cases_len) : (multi_i += 1) {
const items_len = zir.extra[extra_index];
extra_index += 1;
const ranges_len = zir.extra[extra_index];
@@ -3353,3 +3312,18 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
.total_params_len = total_params_len,
};
}
+
+const ref_start_index: u32 = Inst.Ref.typed_value_map.len;
+
+pub fn indexToRef(inst: Inst.Index) Inst.Ref {
+ return @intToEnum(Inst.Ref, ref_start_index + inst);
+}
+
+pub fn refToIndex(inst: Inst.Ref) ?Inst.Index {
+ const ref_int = @enumToInt(inst);
+ if (ref_int >= ref_start_index) {
+ return ref_int - ref_start_index;
+ } else {
+ return null;
+ }
+}
diff --git a/src/print_zir.zig b/src/print_zir.zig
index f0f282f55d..0bbd7e1655 100644
--- a/src/print_zir.zig
+++ b/src/print_zir.zig
@@ -234,6 +234,8 @@ const Writer = struct {
.@"await",
.await_nosuspend,
.fence,
+ .switch_cond,
+ .switch_cond_ref,
=> try self.writeUnNode(stream, inst),
.ref,
@@ -379,19 +381,7 @@ const Writer = struct {
.error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
.error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
- .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_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),
+ .switch_block => try self.writePlNodeSwitchBlock(stream, inst),
.field_ptr,
.field_val,
@@ -1649,113 +1639,46 @@ const Writer = struct {
try self.writeSrc(stream, inst_data.src());
}
- fn writePlNodeSwitchBr(
- self: *Writer,
- stream: anytype,
- inst: Zir.Inst.Index,
- special_prong: Zir.SpecialProng,
- ) !void {
+ fn writePlNodeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
- const special: struct {
- body: []const Zir.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);
-
- self.indent += 2;
-
- if (special.body.len != 0) {
- const prong_name = switch (special_prong) {
- .@"else" => "else",
- .under => "_",
- else => unreachable,
- };
- try stream.writeAll(",\n");
- try stream.writeByteNTimes(' ', self.indent);
- try stream.print("{s} => ", .{prong_name});
- try self.writeBracedBody(stream, special.body);
- }
-
- var extra_index: usize = special.end;
- {
- var scalar_i: usize = 0;
- while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
- const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
- extra_index += 1;
- const body_len = self.code.extra[extra_index];
- extra_index += 1;
- const body = self.code.extra[extra_index..][0..body_len];
- extra_index += body_len;
-
- try stream.writeAll(",\n");
- try stream.writeByteNTimes(' ', self.indent);
- try self.writeInstRef(stream, item_ref);
- try stream.writeAll(" => ");
- try self.writeBracedBody(stream, body);
- }
- }
-
- self.indent -= 2;
- try stream.writeAll(") ");
- try self.writeSrc(stream, inst_data.src());
- }
+ var extra_index: usize = extra.end;
- fn writePlNodeSwitchBlockMulti(
- self: *Writer,
- stream: anytype,
- inst: Zir.Inst.Index,
- special_prong: Zir.SpecialProng,
- ) !void {
- const inst_data = self.code.instructions.items(.data)[inst].pl_node;
- const extra = self.code.extraData(Zir.Inst.SwitchBlockMulti, inst_data.payload_index);
- const special: struct {
- body: []const Zir.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,
- };
- },
- };
+ const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
+ const multi_cases_len = self.code.extra[extra_index];
+ extra_index += 1;
+ break :blk multi_cases_len;
+ } else 0;
try self.writeInstRef(stream, extra.data.operand);
+ try self.writeFlag(stream, ", ref", extra.data.bits.is_ref);
self.indent += 2;
- if (special.body.len != 0) {
+ else_prong: {
+ const special_prong = extra.data.bits.specialProng();
const prong_name = switch (special_prong) {
.@"else" => "else",
.under => "_",
- else => unreachable,
+ else => break :else_prong,
};
+
+ const body_len = self.code.extra[extra_index];
+ extra_index += 1;
+ const body = self.code.extra[extra_index..][0..body_len];
+ extra_index += body.len;
+
try stream.writeAll(",\n");
try stream.writeByteNTimes(' ', self.indent);
try stream.print("{s} => ", .{prong_name});
- try self.writeBracedBody(stream, special.body);
+ try self.writeBracedBody(stream, body);
}
- var extra_index: usize = special.end;
{
+ const scalar_cases_len = extra.data.bits.scalar_cases_len;
var scalar_i: usize = 0;
- while (scalar_i < extra.data.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]);
extra_index += 1;
const body_len = self.code.extra[extra_index];
@@ -1772,7 +1695,7 @@ const Writer = struct {
}
{
var multi_i: usize = 0;
- while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) {
+ while (multi_i < multi_cases_len) : (multi_i += 1) {
const items_len = self.code.extra[extra_index];
extra_index += 1;
const ranges_len = self.code.extra[extra_index];
diff --git a/src/value.zig b/src/value.zig
index e59cc662a7..fce5e8afb1 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -116,7 +116,7 @@ pub const Value = extern union {
decl_ref_mut,
/// Pointer to a specific element of an array.
elem_ptr,
- /// Pointer to a specific field of a struct.
+ /// Pointer to a specific field of a struct or union.
field_ptr,
/// A slice of u8 whose memory is managed externally.
bytes,