authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-19 20:14:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-19 20:22:47-07:00
logdfb3231959bb340d260ddbec2b8eabfb5063c1bf
tree1f91b36fc9ed4b0aaa35fd8f8657efb365f3ec74
parent4a76523b92bcf0e9b48438cb22f49e67e0ab3fa1

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.

8 files changed, 422 insertions(+), 474 deletions(-)

src/AstGen.zig+36-66
......@@ -11,6 +11,8 @@ const StringIndexAdapter = std.hash_map.StringIndexAdapter;
1111const StringIndexContext = std.hash_map.StringIndexContext;
1212
1313const Zir = @import("Zir.zig");
14const refToIndex = Zir.refToIndex;
15const indexToRef = Zir.indexToRef;
1416const trace = @import("tracy.zig").trace;
1517const BuiltinFn = @import("BuiltinFn.zig");
1618
......@@ -57,6 +59,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
5759 Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
5860 i32 => @bitCast(u32, @field(extra, field.name)),
5961 Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),
62 Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)),
6063 else => @compileError("bad field type"),
6164 });
6265 }
......@@ -2133,17 +2136,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
21332136 .slice_sentinel,
21342137 .import,
21352138 .switch_block,
2136 .switch_block_multi,
2137 .switch_block_else,
2138 .switch_block_else_multi,
2139 .switch_block_under,
2140 .switch_block_under_multi,
2141 .switch_block_ref,
2142 .switch_block_ref_multi,
2143 .switch_block_ref_else,
2144 .switch_block_ref_else_multi,
2145 .switch_block_ref_under,
2146 .switch_block_ref_under_multi,
2139 .switch_cond,
2140 .switch_cond_ref,
21472141 .switch_capture,
21482142 .switch_capture_ref,
21492143 .switch_capture_multi,
......@@ -5127,11 +5121,12 @@ fn fieldAccess(
51275121 rl: ResultLoc,
51285122 node: Ast.Node.Index,
51295123) InnerError!Zir.Inst.Ref {
5130 if (rl == .ref) {
5131 return addFieldAccess(.field_ptr, gz, scope, .ref, node);
5132 } else {
5133 const access = try addFieldAccess(.field_val, gz, scope, .none, node);
5134 return rvalue(gz, rl, access, node);
5124 switch (rl) {
5125 .ref => return addFieldAccess(.field_ptr, gz, scope, .ref, node),
5126 else => {
5127 const access = try addFieldAccess(.field_val, gz, scope, .none, node);
5128 return rvalue(gz, rl, access, node);
5129 },
51355130 }
51365131}
51375132
......@@ -6028,11 +6023,13 @@ fn switchExpr(
60286023 }
60296024
60306025 const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
6031 const operand = try expr(parent_gz, scope, operand_rl, operand_node);
6026 const raw_operand = try expr(parent_gz, scope, operand_rl, operand_node);
6027 const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
6028 const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node);
60326029 // We need the type of the operand to use as the result location for all the prong items.
60336030 const typeof_tag: Zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof;
6034 const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node);
6035 const item_rl: ResultLoc = .{ .ty = operand_ty_inst };
6031 const cond_ty_inst = try parent_gz.addUnNode(typeof_tag, cond, operand_node);
6032 const item_rl: ResultLoc = .{ .ty = cond_ty_inst };
60366033
60376034 // These contain the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti.
60386035 // This is the optional else prong body.
......@@ -6050,7 +6047,7 @@ fn switchExpr(
60506047 defer block_scope.instructions.deinit(gpa);
60516048
60526049 // This gets added to the parent block later, after the item expressions.
6053 const switch_block = try parent_gz.addBlock(undefined, switch_node);
6050 const switch_block = try parent_gz.addBlock(.switch_block, switch_node);
60546051
60556052 // We re-use this same scope for all cases, including the special prong, if any.
60566053 var case_scope = parent_gz.makeSubBlock(&block_scope.base);
......@@ -6203,44 +6200,32 @@ fn switchExpr(
62036200 // Now that the item expressions are generated we can add this.
62046201 try parent_gz.instructions.append(gpa, switch_block);
62056202
6206 const ref_bit: u4 = @boolToInt(any_payload_is_ref);
6207 const multi_bit: u4 = @boolToInt(multi_cases_len != 0);
6208 const special_prong_bits: u4 = @enumToInt(special_prong);
6209 comptime {
6210 assert(@enumToInt(Zir.SpecialProng.none) == 0b00);
6211 assert(@enumToInt(Zir.SpecialProng.@"else") == 0b01);
6212 assert(@enumToInt(Zir.SpecialProng.under) == 0b10);
6213 }
6214 const zir_tags = astgen.instructions.items(.tag);
6215 zir_tags[switch_block] = switch ((ref_bit << 3) | (special_prong_bits << 1) | multi_bit) {
6216 0b0_00_0 => .switch_block,
6217 0b0_00_1 => .switch_block_multi,
6218 0b0_01_0 => .switch_block_else,
6219 0b0_01_1 => .switch_block_else_multi,
6220 0b0_10_0 => .switch_block_under,
6221 0b0_10_1 => .switch_block_under_multi,
6222 0b1_00_0 => .switch_block_ref,
6223 0b1_00_1 => .switch_block_ref_multi,
6224 0b1_01_0 => .switch_block_ref_else,
6225 0b1_01_1 => .switch_block_ref_else_multi,
6226 0b1_10_0 => .switch_block_ref_under,
6227 0b1_10_1 => .switch_block_ref_under_multi,
6228 else => unreachable,
6229 };
6230 const payload_index = astgen.extra.items.len;
6231 const zir_datas = astgen.instructions.items(.data);
6232 zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index);
6233 // Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`.
6234 try astgen.extra.ensureUnusedCapacity(gpa, @as(usize, 2) + // operand, scalar_cases_len
6203 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
62356204 @boolToInt(multi_cases_len != 0) +
62366205 special_case_payload.items.len +
62376206 scalar_cases_payload.items.len +
62386207 multi_cases_payload.items.len);
6239 astgen.extra.appendAssumeCapacity(@enumToInt(operand));
6240 astgen.extra.appendAssumeCapacity(scalar_cases_len);
6208
6209 const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
6210 .operand = cond,
6211 .bits = Zir.Inst.SwitchBlock.Bits{
6212 .is_ref = any_payload_is_ref,
6213 .has_multi_cases = multi_cases_len != 0,
6214 .has_else = special_prong == .@"else",
6215 .has_under = special_prong == .under,
6216 .scalar_cases_len = @intCast(u28, scalar_cases_len),
6217 },
6218 });
6219
6220 const zir_datas = astgen.instructions.items(.data);
6221 const zir_tags = astgen.instructions.items(.tag);
6222
6223 zir_datas[switch_block].pl_node.payload_index = payload_index;
6224
62416225 if (multi_cases_len != 0) {
62426226 astgen.extra.appendAssumeCapacity(multi_cases_len);
62436227 }
6228
62446229 const strat = rl.strategy(&block_scope);
62456230 switch (strat.tag) {
62466231 .break_operand => {
......@@ -10622,21 +10607,6 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {
1062210607 astgen.source_column = column;
1062310608}
1062410609
10625const ref_start_index: u32 = Zir.Inst.Ref.typed_value_map.len;
10626
10627fn indexToRef(inst: Zir.Inst.Index) Zir.Inst.Ref {
10628 return @intToEnum(Zir.Inst.Ref, ref_start_index + inst);
10629}
10630
10631fn refToIndex(inst: Zir.Inst.Ref) ?Zir.Inst.Index {
10632 const ref_int = @enumToInt(inst);
10633 if (ref_int >= ref_start_index) {
10634 return ref_int - ref_start_index;
10635 } else {
10636 return null;
10637 }
10638}
10639
1064010610fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !void {
1064110611 const gpa = astgen.gpa;
1064210612 const tree = astgen.tree;
src/Sema.zig+206-125
......@@ -550,18 +550,9 @@ pub fn analyzeBody(
550550 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
551551 .slice_start => try sema.zirSliceStart(block, inst),
552552 .str => try sema.zirStr(block, inst),
553 .switch_block => try sema.zirSwitchBlock(block, inst, false, .none),
554 .switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),
555 .switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),
556 .switch_block_else_multi => try sema.zirSwitchBlockMulti(block, inst, false, .@"else"),
557 .switch_block_under => try sema.zirSwitchBlock(block, inst, false, .under),
558 .switch_block_under_multi => try sema.zirSwitchBlockMulti(block, inst, false, .under),
559 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true, .none),
560 .switch_block_ref_multi => try sema.zirSwitchBlockMulti(block, inst, true, .none),
561 .switch_block_ref_else => try sema.zirSwitchBlock(block, inst, true, .@"else"),
562 .switch_block_ref_else_multi => try sema.zirSwitchBlockMulti(block, inst, true, .@"else"),
563 .switch_block_ref_under => try sema.zirSwitchBlock(block, inst, true, .under),
564 .switch_block_ref_under_multi => try sema.zirSwitchBlockMulti(block, inst, true, .under),
553 .switch_block => try sema.zirSwitchBlock(block, inst),
554 .switch_cond => try sema.zirSwitchCond(block, inst, false),
555 .switch_cond_ref => try sema.zirSwitchCond(block, inst, true),
565556 .switch_capture => try sema.zirSwitchCapture(block, inst, false, false),
566557 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
567558 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
......@@ -5433,11 +5424,80 @@ fn zirSwitchCapture(
54335424 const zir_datas = sema.code.instructions.items(.data);
54345425 const capture_info = zir_datas[inst].switch_capture;
54355426 const switch_info = zir_datas[capture_info.switch_inst].pl_node;
5436 const src = switch_info.src();
5427 const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);
5428 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };
5429 const switch_src = switch_info.src();
5430 const operand_is_ref = switch_extra.data.bits.is_ref;
5431 const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;
5432 const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node;
5433 const operand_ptr = sema.resolveInst(cond_info.operand);
5434 const operand_ptr_ty = sema.typeOf(operand_ptr);
5435 const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
5436
5437 if (is_multi) {
5438 return sema.fail(block, switch_src, "TODO implement Sema for switch capture multi", .{});
5439 }
5440 const scalar_prong = switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index);
5441 const item = sema.resolveInst(scalar_prong.item);
5442 // Previous switch validation ensured this will succeed
5443 const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable;
54375444
5438 _ = is_ref;
5439 _ = is_multi;
5440 return sema.fail(block, src, "TODO implement Sema for zirSwitchCapture", .{});
5445 switch (operand_ty.zigTypeTag()) {
5446 .Union => {
5447 const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
5448 const enum_ty = union_obj.tag_ty;
5449
5450 const field_index_usize = enum_ty.enumTagFieldIndex(item_val).?;
5451 const field_index = @intCast(u32, field_index_usize);
5452 const field = union_obj.fields.values()[field_index];
5453
5454 // TODO handle multiple union tags which have compatible types
5455
5456 if (is_ref) {
5457 assert(operand_is_ref);
5458
5459 const field_ty_ptr = try Type.ptr(sema.arena, .{
5460 .pointee_type = field.ty,
5461 .@"addrspace" = .generic,
5462 .mutable = operand_ptr_ty.ptrIsMutable(),
5463 });
5464
5465 if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| {
5466 return sema.addConstant(
5467 field_ty_ptr,
5468 try Value.Tag.field_ptr.create(sema.arena, .{
5469 .container_ptr = op_ptr_val,
5470 .field_index = field_index,
5471 }),
5472 );
5473 }
5474 try sema.requireRuntimeBlock(block, operand_src);
5475 return block.addStructFieldPtr(operand_ptr, field_index, field.ty);
5476 }
5477
5478 const operand = if (operand_is_ref)
5479 try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
5480 else
5481 operand_ptr;
5482
5483 if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
5484 return sema.addConstant(
5485 field.ty,
5486 operand_val.castTag(.@"union").?.data.val,
5487 );
5488 }
5489 try sema.requireRuntimeBlock(block, operand_src);
5490 return block.addStructFieldVal(operand, field_index, field.ty);
5491 },
5492 .ErrorSet => {
5493 return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCapture for error sets", .{});
5494 },
5495 else => {
5496 return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{
5497 operand_ty,
5498 });
5499 },
5500 }
54415501}
54425502
54435503fn zirSwitchCaptureElse(
......@@ -5452,96 +5512,108 @@ fn zirSwitchCaptureElse(
54525512 const zir_datas = sema.code.instructions.items(.data);
54535513 const capture_info = zir_datas[inst].switch_capture;
54545514 const switch_info = zir_datas[capture_info.switch_inst].pl_node;
5515 const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index).data;
54555516 const src = switch_info.src();
5517 const operand_is_ref = switch_extra.bits.is_ref;
5518 assert(!is_ref or operand_is_ref);
54565519
5457 _ = is_ref;
54585520 return sema.fail(block, src, "TODO implement Sema for zirSwitchCaptureElse", .{});
54595521}
54605522
5461fn zirSwitchBlock(
5523fn zirSwitchCond(
54625524 sema: *Sema,
54635525 block: *Block,
54645526 inst: Zir.Inst.Index,
54655527 is_ref: bool,
5466 special_prong: Zir.SpecialProng,
54675528) CompileError!Air.Inst.Ref {
5468 const tracy = trace(@src());
5469 defer tracy.end();
5470
5471 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5529 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
54725530 const src = inst_data.src();
5473 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
5474 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
5531 const operand_ptr = sema.resolveInst(inst_data.operand);
5532 const operand = if (is_ref) try sema.analyzeLoad(block, src, operand_ptr, src) else operand_ptr;
5533 const operand_ty = sema.typeOf(operand);
54755534
5476 const operand_ptr = sema.resolveInst(extra.data.operand);
5477 const operand = if (is_ref)
5478 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
5479 else
5480 operand_ptr;
5535 switch (operand_ty.zigTypeTag()) {
5536 .Type,
5537 .Void,
5538 .Bool,
5539 .Int,
5540 .Float,
5541 .ComptimeFloat,
5542 .ComptimeInt,
5543 .EnumLiteral,
5544 .Pointer,
5545 .Fn,
5546 .ErrorSet,
5547 .Enum,
5548 => {
5549 if ((try sema.typeHasOnePossibleValue(block, src, operand_ty))) |opv| {
5550 return sema.addConstant(operand_ty, opv);
5551 }
5552 return operand;
5553 },
54815554
5482 return sema.analyzeSwitch(
5483 block,
5484 operand,
5485 extra.end,
5486 special_prong,
5487 extra.data.cases_len,
5488 0,
5489 inst,
5490 inst_data.src_node,
5491 );
5555 .Union => {
5556 const enum_ty = operand_ty.unionTagType() orelse {
5557 const msg = msg: {
5558 const msg = try sema.errMsg(block, src, "switch on untagged union", .{});
5559 errdefer msg.destroy(sema.gpa);
5560 try sema.addDeclaredHereNote(msg, operand_ty);
5561 break :msg msg;
5562 };
5563 return sema.failWithOwnedErrorMsg(msg);
5564 };
5565 return sema.unionToTag(block, enum_ty, operand, src);
5566 },
5567
5568 .ErrorUnion,
5569 .NoReturn,
5570 .Array,
5571 .Struct,
5572 .Undefined,
5573 .Null,
5574 .Optional,
5575 .BoundFn,
5576 .Opaque,
5577 .Vector,
5578 .Frame,
5579 .AnyFrame,
5580 => return sema.fail(block, src, "switch on type '{}'", .{operand_ty}),
5581 }
54925582}
54935583
5494fn zirSwitchBlockMulti(
5495 sema: *Sema,
5496 block: *Block,
5497 inst: Zir.Inst.Index,
5498 is_ref: bool,
5499 special_prong: Zir.SpecialProng,
5500) CompileError!Air.Inst.Ref {
5584fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
55015585 const tracy = trace(@src());
55025586 defer tracy.end();
55035587
5588 const gpa = sema.gpa;
55045589 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
55055590 const src = inst_data.src();
5506 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
5507 const extra = sema.code.extraData(Zir.Inst.SwitchBlockMulti, inst_data.payload_index);
5591 const src_node_offset = inst_data.src_node;
5592 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
5593 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
5594 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
55085595
55095596 const operand_ptr = sema.resolveInst(extra.data.operand);
5510 const operand = if (is_ref)
5597 const operand = if (extra.data.bits.is_ref)
55115598 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
55125599 else
55135600 operand_ptr;
55145601
5515 return sema.analyzeSwitch(
5516 block,
5517 operand,
5518 extra.end,
5519 special_prong,
5520 extra.data.scalar_cases_len,
5521 extra.data.multi_cases_len,
5522 inst,
5523 inst_data.src_node,
5524 );
5525}
5602 var header_extra_index: usize = extra.end;
55265603
5527fn analyzeSwitch(
5528 sema: *Sema,
5529 block: *Block,
5530 operand: Air.Inst.Ref,
5531 extra_end: usize,
5532 special_prong: Zir.SpecialProng,
5533 scalar_cases_len: usize,
5534 multi_cases_len: usize,
5535 switch_inst: Zir.Inst.Index,
5536 src_node_offset: i32,
5537) CompileError!Air.Inst.Ref {
5538 const gpa = sema.gpa;
5604 const scalar_cases_len = extra.data.bits.scalar_cases_len;
5605 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
5606 const multi_cases_len = sema.code.extra[header_extra_index];
5607 header_extra_index += 1;
5608 break :blk multi_cases_len;
5609 } else 0;
55395610
5611 const special_prong = extra.data.bits.specialProng();
55405612 const special: struct { body: []const Zir.Inst.Index, end: usize } = switch (special_prong) {
5541 .none => .{ .body = &.{}, .end = extra_end },
5613 .none => .{ .body = &.{}, .end = header_extra_index },
55425614 .under, .@"else" => blk: {
5543 const body_len = sema.code.extra[extra_end];
5544 const extra_body_start = extra_end + 1;
5615 const body_len = sema.code.extra[header_extra_index];
5616 const extra_body_start = header_extra_index + 1;
55455617 break :blk .{
55465618 .body = sema.code.extra[extra_body_start..][0..body_len],
55475619 .end = extra_body_start + body_len,
......@@ -5549,9 +5621,6 @@ fn analyzeSwitch(
55495621 },
55505622 };
55515623
5552 const src: LazySrcLoc = .{ .node_offset = src_node_offset };
5553 const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
5554 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
55555624 const operand_ty = sema.typeOf(operand);
55565625
55575626 // Validate usage of '_' prongs.
......@@ -5945,7 +6014,7 @@ fn analyzeSwitch(
59456014 .data = undefined,
59466015 });
59476016 var label: Block.Label = .{
5948 .zir_block = switch_inst,
6017 .zir_block = inst,
59496018 .merges = .{
59506019 .results = .{},
59516020 .br_list = .{},
......@@ -8934,8 +9003,9 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
89349003 const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
89359004 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
89369005 const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
8937 const field_index = union_obj.fields.getIndex(field_name) orelse
9006 const field_index_usize = union_obj.fields.getIndex(field_name) orelse
89389007 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
9008 const field_index = @intCast(u32, field_index_usize);
89399009
89409010 if (is_ref) {
89419011 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)
89439013
89449014 const init_inst = sema.resolveInst(item.data.init);
89459015 if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| {
9016 const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
89469017 return sema.addConstant(
89479018 resolved_ty,
8948 try Value.Tag.@"union".create(sema.arena, .{
8949 .tag = try Value.Tag.int_u64.create(sema.arena, field_index),
8950 .val = val,
8951 }),
9019 try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, .val = val }),
89529020 );
89539021 }
89549022 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
91529220 const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
91539221 const val = try sema.resolveConstValue(block, operand_src, type_info);
91549222 const union_val = val.cast(Value.Payload.Union).?.data;
9155 const TypeInfoTag = std.meta.Tag(std.builtin.TypeInfo);
9156 const tag_index = @intCast(std.meta.Tag(TypeInfoTag), union_val.tag.toUnsignedInt());
9223 const tag_ty = type_info_ty.unionTagType().?;
9224 const tag_index = tag_ty.enumTagFieldIndex(union_val.tag).?;
91579225 switch (@intToEnum(std.builtin.TypeId, tag_index)) {
91589226 .Type => return Air.Inst.Ref.type_type,
91599227 .Void => return Air.Inst.Ref.void_type,
......@@ -10819,10 +10887,39 @@ fn fieldVal(
1081910887 try Value.Tag.@"error".create(arena, .{ .name = name }),
1082010888 );
1082110889 },
10822 .Struct, .Opaque, .Union => {
10890 .Union => {
1082310891 if (child_type.getNamespace()) |namespace| {
10824 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
10825 return sema.analyzeLoad(block, src, inst, src);
10892 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
10893 return inst;
10894 }
10895 }
10896 if (child_type.unionTagType()) |enum_ty| {
10897 if (enum_ty.enumFieldIndex(field_name)) |field_index_usize| {
10898 const field_index = @intCast(u32, field_index_usize);
10899 return sema.addConstant(
10900 enum_ty,
10901 try Value.Tag.enum_field_index.create(sema.arena, field_index),
10902 );
10903 }
10904 }
10905 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
10906 },
10907 .Enum => {
10908 if (child_type.getNamespace()) |namespace| {
10909 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
10910 return inst;
10911 }
10912 }
10913 const field_index_usize = child_type.enumFieldIndex(field_name) orelse
10914 return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
10915 const field_index = @intCast(u32, field_index_usize);
10916 const enum_val = try Value.Tag.enum_field_index.create(arena, field_index);
10917 return sema.addConstant(try child_type.copy(arena), enum_val);
10918 },
10919 .Struct, .Opaque => {
10920 if (child_type.getNamespace()) |namespace| {
10921 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
10922 return inst;
1082610923 }
1082710924 }
1082810925 // TODO add note: declared here
......@@ -10836,35 +10933,6 @@ fn fieldVal(
1083610933 kw_name, child_type, field_name,
1083710934 });
1083810935 },
10839 .Enum => {
10840 if (child_type.getNamespace()) |namespace| {
10841 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
10842 return sema.analyzeLoad(block, src, inst, src);
10843 }
10844 }
10845 const field_index = child_type.enumFieldIndex(field_name) orelse {
10846 const msg = msg: {
10847 const msg = try sema.errMsg(
10848 block,
10849 src,
10850 "enum '{}' has no member named '{s}'",
10851 .{ child_type, field_name },
10852 );
10853 errdefer msg.destroy(sema.gpa);
10854 try sema.mod.errNoteNonLazy(
10855 child_type.declSrcLoc(),
10856 msg,
10857 "enum declared here",
10858 .{},
10859 );
10860 break :msg msg;
10861 };
10862 return sema.failWithOwnedErrorMsg(msg);
10863 };
10864 const field_index_u32 = @intCast(u32, field_index);
10865 const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);
10866 return sema.addConstant(try child_type.copy(arena), enum_val);
10867 },
1086810936 else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
1086910937 }
1087010938 },
......@@ -11244,6 +11312,17 @@ fn namespaceLookupRef(
1124411312 return try sema.analyzeDeclRef(decl);
1124511313}
1124611314
11315fn namespaceLookupVal(
11316 sema: *Sema,
11317 block: *Block,
11318 src: LazySrcLoc,
11319 namespace: *Namespace,
11320 decl_name: []const u8,
11321) CompileError!?Air.Inst.Ref {
11322 const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
11323 return try sema.analyzeDeclVal(block, src, decl);
11324}
11325
1124711326fn structFieldPtr(
1124811327 sema: *Sema,
1124911328 block: *Block,
......@@ -11370,10 +11449,9 @@ fn unionFieldVal(
1137011449 const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
1137111450 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
1137211451
11373 const field_index_big = union_obj.fields.getIndex(field_name) orelse
11452 const field_index_usize = union_obj.fields.getIndex(field_name) orelse
1137411453 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
11375 const field_index = @intCast(u32, field_index_big);
11376
11454 const field_index = @intCast(u32, field_index_usize);
1137711455 const field = union_obj.fields.values()[field_index];
1137811456
1137911457 if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| {
......@@ -12960,15 +13038,18 @@ fn wrapErrorUnion(
1296013038fn unionToTag(
1296113039 sema: *Sema,
1296213040 block: *Block,
12963 dest_ty: Type,
13041 enum_ty: Type,
1296413042 un: Air.Inst.Ref,
1296513043 un_src: LazySrcLoc,
1296613044) !Air.Inst.Ref {
13045 if ((try sema.typeHasOnePossibleValue(block, un_src, enum_ty))) |opv| {
13046 return sema.addConstant(enum_ty, opv);
13047 }
1296713048 if (try sema.resolveMaybeUndefVal(block, un_src, un)) |un_val| {
12968 return sema.addConstant(dest_ty, un_val.unionTag());
13049 return sema.addConstant(enum_ty, un_val.unionTag());
1296913050 }
1297013051 try sema.requireRuntimeBlock(block, un_src);
12971 return block.addTyOp(.get_union_tag, dest_ty, un);
13052 return block.addTyOp(.get_union_tag, enum_ty, un);
1297213053}
1297313054
1297413055fn resolvePeerTypes(
src/Zir.zig+123-149
......@@ -72,6 +72,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en
7272 Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
7373 i32 => @bitCast(i32, code.extra[i]),
7474 Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]),
75 Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]),
7576 else => @compileError("bad field type"),
7677 };
7778 i += 1;
......@@ -618,39 +619,16 @@ pub const Inst = struct {
618619 enum_literal,
619620 /// A switch expression. Uses the `pl_node` union field.
620621 /// AST node is the switch, payload is `SwitchBlock`.
621 /// All prongs of target handled.
622622 switch_block,
623 /// Same as switch_block, except one or more prongs have multiple items.
624 /// Payload is `SwitchBlockMulti`
625 switch_block_multi,
626 /// Same as switch_block, except has an else prong.
627 switch_block_else,
628 /// Same as switch_block_else, except one or more prongs have multiple items.
629 /// Payload is `SwitchBlockMulti`
630 switch_block_else_multi,
631 /// Same as switch_block, except has an underscore prong.
632 switch_block_under,
633 /// Same as switch_block, except one or more prongs have multiple items.
634 /// Payload is `SwitchBlockMulti`
635 switch_block_under_multi,
636 /// Same as `switch_block` but the target is a pointer to the value being switched on.
637 switch_block_ref,
638 /// Same as `switch_block_multi` but the target is a pointer to the value being switched on.
639 /// Payload is `SwitchBlockMulti`
640 switch_block_ref_multi,
641 /// Same as `switch_block_else` but the target is a pointer to the value being switched on.
642 switch_block_ref_else,
643 /// Same as `switch_block_else_multi` but the target is a pointer to the
644 /// value being switched on.
645 /// Payload is `SwitchBlockMulti`
646 switch_block_ref_else_multi,
647 /// Same as `switch_block_under` but the target is a pointer to the value
648 /// being switched on.
649 switch_block_ref_under,
650 /// Same as `switch_block_under_multi` but the target is a pointer to
651 /// the value being switched on.
652 /// Payload is `SwitchBlockMulti`
653 switch_block_ref_under_multi,
623 /// Produces the value that will be switched on. For example, for
624 /// integers, it returns the integer with no modifications. For tagged unions, it
625 /// returns the active enum tag.
626 /// Uses the `un_node` union field.
627 switch_cond,
628 /// Same as `switch_cond`, except the input operand is a pointer to
629 /// what will be switched on.
630 /// Uses the `un_node` union field.
631 switch_cond_ref,
654632 /// Produces the capture value for a switch prong.
655633 /// Uses the `switch_capture` field.
656634 switch_capture,
......@@ -1109,17 +1087,8 @@ pub const Inst = struct {
11091087 .switch_capture_else,
11101088 .switch_capture_else_ref,
11111089 .switch_block,
1112 .switch_block_multi,
1113 .switch_block_else,
1114 .switch_block_else_multi,
1115 .switch_block_under,
1116 .switch_block_under_multi,
1117 .switch_block_ref,
1118 .switch_block_ref_multi,
1119 .switch_block_ref_else,
1120 .switch_block_ref_else_multi,
1121 .switch_block_ref_under,
1122 .switch_block_ref_under_multi,
1090 .switch_cond,
1091 .switch_cond_ref,
11231092 .validate_struct_init,
11241093 .validate_array_init,
11251094 .struct_init_empty,
......@@ -1367,17 +1336,8 @@ pub const Inst = struct {
13671336 .ensure_err_payload_void = .un_tok,
13681337 .enum_literal = .str_tok,
13691338 .switch_block = .pl_node,
1370 .switch_block_multi = .pl_node,
1371 .switch_block_else = .pl_node,
1372 .switch_block_else_multi = .pl_node,
1373 .switch_block_under = .pl_node,
1374 .switch_block_under_multi = .pl_node,
1375 .switch_block_ref = .pl_node,
1376 .switch_block_ref_multi = .pl_node,
1377 .switch_block_ref_else = .pl_node,
1378 .switch_block_ref_else_multi = .pl_node,
1379 .switch_block_ref_under = .pl_node,
1380 .switch_block_ref_under_multi = .pl_node,
1339 .switch_cond = .un_node,
1340 .switch_cond_ref = .un_node,
13811341 .switch_capture = .switch_capture,
13821342 .switch_capture_ref = .switch_capture,
13831343 .switch_capture_multi = .switch_capture,
......@@ -2466,37 +2426,17 @@ pub const Inst = struct {
24662426 index: u32,
24672427 };
24682428
2469 /// This form is supported when there are no ranges, and exactly 1 item per block.
2470 /// Depending on zir tag and len fields, extra fields trail
2471 /// this one in the extra array.
2472 /// 0. else_body { // If the tag has "_else" or "_under" in it.
2473 /// body_len: u32,
2474 /// body member Index for every body_len
2475 /// }
2476 /// 1. cases: {
2477 /// item: Ref,
2478 /// body_len: u32,
2479 /// body member Index for every body_len
2480 /// } for every cases_len
2481 pub const SwitchBlock = struct {
2482 operand: Ref,
2483 cases_len: u32,
2484 };
2485
2486 /// This form is required when there exists a block which has more than one item,
2487 /// or a range.
2488 /// Depending on zir tag and len fields, extra fields trail
2489 /// this one in the extra array.
2490 /// 0. else_body { // If the tag has "_else" or "_under" in it.
2429 /// 0. multi_cases_len: u32 // If has_multi_cases is set.
2430 /// 1. else_body { // If has_else or has_under is set.
24912431 /// body_len: u32,
24922432 /// body member Index for every body_len
24932433 /// }
2494 /// 1. scalar_cases: { // for every scalar_cases_len
2434 /// 2. scalar_cases: { // for every scalar_cases_len
24952435 /// item: Ref,
24962436 /// body_len: u32,
24972437 /// body member Index for every body_len
24982438 /// }
2499 /// 2. multi_cases: { // for every multi_cases_len
2439 /// 3. multi_cases: { // for every multi_cases_len
25002440 /// items_len: u32,
25012441 /// ranges_len: u32,
25022442 /// body_len: u32,
......@@ -2507,10 +2447,78 @@ pub const Inst = struct {
25072447 /// }
25082448 /// body member Index for every body_len
25092449 /// }
2510 pub const SwitchBlockMulti = struct {
2450 pub const SwitchBlock = struct {
25112451 operand: Ref,
2512 scalar_cases_len: u32,
2513 multi_cases_len: u32,
2452 bits: Bits,
2453
2454 pub const Bits = packed struct {
2455 /// If true, one or more prongs have multiple items.
2456 has_multi_cases: bool,
2457 /// If true, there is an else prong. This is mutually exclusive with `has_under`.
2458 has_else: bool,
2459 /// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
2460 has_under: bool,
2461 /// If true, the `operand` is a pointer to the value being switched on.
2462 is_ref: bool,
2463 scalar_cases_len: u28,
2464
2465 pub fn specialProng(bits: Bits) SpecialProng {
2466 const has_else: u2 = @boolToInt(bits.has_else);
2467 const has_under: u2 = @boolToInt(bits.has_under);
2468 return switch ((has_else << 1) | has_under) {
2469 0b00 => .none,
2470 0b01 => .under,
2471 0b10 => .@"else",
2472 0b11 => unreachable,
2473 };
2474 }
2475 };
2476
2477 pub const ScalarProng = struct {
2478 item: Ref,
2479 body: []const Index,
2480 };
2481
2482 /// TODO performance optimization: instead of having this helper method
2483 /// change the definition of switch_capture instruction to store extra_index
2484 /// instead of prong_index. This way, Sema won't be doing O(N^2) iterations
2485 /// over the switch prongs.
2486 pub fn getScalarProng(
2487 self: SwitchBlock,
2488 zir: Zir,
2489 extra_end: usize,
2490 prong_index: usize,
2491 ) ScalarProng {
2492 var extra_index: usize = extra_end;
2493
2494 if (self.bits.has_multi_cases) {
2495 extra_index += 1;
2496 }
2497
2498 if (self.bits.specialProng() != .none) {
2499 const body_len = zir.extra[extra_index];
2500 extra_index += 1;
2501 const body = zir.extra[extra_index..][0..body_len];
2502 extra_index += body.len;
2503 }
2504
2505 var scalar_i: usize = 0;
2506 while (true) : (scalar_i += 1) {
2507 const item = @intToEnum(Ref, zir.extra[extra_index]);
2508 extra_index += 1;
2509 const body_len = zir.extra[extra_index];
2510 extra_index += 1;
2511 const body = zir.extra[extra_index..][0..body_len];
2512 extra_index += body.len;
2513
2514 if (scalar_i < prong_index) continue;
2515
2516 return .{
2517 .item = item,
2518 .body = body,
2519 };
2520 }
2521 }
25142522 };
25152523
25162524 pub const Field = struct {
......@@ -2934,7 +2942,7 @@ pub const Inst = struct {
29342942
29352943 /// Trailing: for each `imports_len` there is an Item
29362944 pub const Imports = struct {
2937 imports_len: Zir.Inst.Index,
2945 imports_len: Inst.Index,
29382946
29392947 pub const Item = struct {
29402948 /// null terminated string index
......@@ -3077,7 +3085,7 @@ pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclItera
30773085
30783086/// The iterator would have to allocate memory anyway to iterate. So here we populate
30793087/// an ArrayList as the result.
3080pub fn findDecls(zir: Zir, list: *std.ArrayList(Zir.Inst.Index), decl_sub_index: u32) !void {
3088pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_sub_index: u32) !void {
30813089 const block_inst = zir.extra[decl_sub_index + 6];
30823090 list.clearRetainingCapacity();
30833091
......@@ -3086,8 +3094,8 @@ pub fn findDecls(zir: Zir, list: *std.ArrayList(Zir.Inst.Index), decl_sub_index:
30863094
30873095fn findDeclsInner(
30883096 zir: Zir,
3089 list: *std.ArrayList(Zir.Inst.Index),
3090 inst: Zir.Inst.Index,
3097 list: *std.ArrayList(Inst.Index),
3098 inst: Inst.Index,
30913099) Allocator.Error!void {
30923100 const tags = zir.instructions.items(.tag);
30933101 const datas = zir.instructions.items(.data);
......@@ -3148,19 +3156,7 @@ fn findDeclsInner(
31483156 try zir.findDeclsBody(list, then_body);
31493157 try zir.findDeclsBody(list, else_body);
31503158 },
3151 .switch_block => return findDeclsSwitch(zir, list, inst, .none),
3152 .switch_block_else => return findDeclsSwitch(zir, list, inst, .@"else"),
3153 .switch_block_under => return findDeclsSwitch(zir, list, inst, .under),
3154 .switch_block_ref => return findDeclsSwitch(zir, list, inst, .none),
3155 .switch_block_ref_else => return findDeclsSwitch(zir, list, inst, .@"else"),
3156 .switch_block_ref_under => return findDeclsSwitch(zir, list, inst, .under),
3157
3158 .switch_block_multi => return findDeclsSwitchMulti(zir, list, inst, .none),
3159 .switch_block_else_multi => return findDeclsSwitchMulti(zir, list, inst, .@"else"),
3160 .switch_block_under_multi => return findDeclsSwitchMulti(zir, list, inst, .under),
3161 .switch_block_ref_multi => return findDeclsSwitchMulti(zir, list, inst, .none),
3162 .switch_block_ref_else_multi => return findDeclsSwitchMulti(zir, list, inst, .@"else"),
3163 .switch_block_ref_under_multi => return findDeclsSwitchMulti(zir, list, inst, .under),
3159 .switch_block => return findDeclsSwitch(zir, list, inst),
31643160
31653161 .suspend_block => @panic("TODO iterate suspend block"),
31663162
......@@ -3170,71 +3166,34 @@ fn findDeclsInner(
31703166
31713167fn findDeclsSwitch(
31723168 zir: Zir,
3173 list: *std.ArrayList(Zir.Inst.Index),
3174 inst: Zir.Inst.Index,
3175 special_prong: SpecialProng,
3169 list: *std.ArrayList(Inst.Index),
3170 inst: Inst.Index,
31763171) Allocator.Error!void {
31773172 const inst_data = zir.instructions.items(.data)[inst].pl_node;
31783173 const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index);
3179 const special: struct {
3180 body: []const Inst.Index,
3181 end: usize,
3182 } = switch (special_prong) {
3183 .none => .{ .body = &.{}, .end = extra.end },
3184 .under, .@"else" => blk: {
3185 const body_len = zir.extra[extra.end];
3186 const extra_body_start = extra.end + 1;
3187 break :blk .{
3188 .body = zir.extra[extra_body_start..][0..body_len],
3189 .end = extra_body_start + body_len,
3190 };
3191 },
3192 };
31933174
3194 try zir.findDeclsBody(list, special.body);
3175 var extra_index: usize = extra.end;
31953176
3196 var extra_index: usize = special.end;
3197 var scalar_i: usize = 0;
3198 while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
3177 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
3178 const multi_cases_len = zir.extra[extra_index];
31993179 extra_index += 1;
3180 break :blk multi_cases_len;
3181 } else 0;
3182
3183 const special_prong = extra.data.bits.specialProng();
3184 if (special_prong != .none) {
32003185 const body_len = zir.extra[extra_index];
32013186 extra_index += 1;
32023187 const body = zir.extra[extra_index..][0..body_len];
3203 extra_index += body_len;
3188 extra_index += body.len;
32043189
32053190 try zir.findDeclsBody(list, body);
32063191 }
3207}
32083192
3209fn findDeclsSwitchMulti(
3210 zir: Zir,
3211 list: *std.ArrayList(Zir.Inst.Index),
3212 inst: Zir.Inst.Index,
3213 special_prong: SpecialProng,
3214) Allocator.Error!void {
3215 const inst_data = zir.instructions.items(.data)[inst].pl_node;
3216 const extra = zir.extraData(Inst.SwitchBlockMulti, inst_data.payload_index);
3217 const special: struct {
3218 body: []const Inst.Index,
3219 end: usize,
3220 } = switch (special_prong) {
3221 .none => .{ .body = &.{}, .end = extra.end },
3222 .under, .@"else" => blk: {
3223 const body_len = zir.extra[extra.end];
3224 const extra_body_start = extra.end + 1;
3225 break :blk .{
3226 .body = zir.extra[extra_body_start..][0..body_len],
3227 .end = extra_body_start + body_len,
3228 };
3229 },
3230 };
3231
3232 try zir.findDeclsBody(list, special.body);
3233
3234 var extra_index: usize = special.end;
32353193 {
3194 const scalar_cases_len = extra.data.bits.scalar_cases_len;
32363195 var scalar_i: usize = 0;
3237 while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
3196 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
32383197 extra_index += 1;
32393198 const body_len = zir.extra[extra_index];
32403199 extra_index += 1;
......@@ -3246,7 +3205,7 @@ fn findDeclsSwitchMulti(
32463205 }
32473206 {
32483207 var multi_i: usize = 0;
3249 while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) {
3208 while (multi_i < multi_cases_len) : (multi_i += 1) {
32503209 const items_len = zir.extra[extra_index];
32513210 extra_index += 1;
32523211 const ranges_len = zir.extra[extra_index];
......@@ -3353,3 +3312,18 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
33533312 .total_params_len = total_params_len,
33543313 };
33553314}
3315
3316const ref_start_index: u32 = Inst.Ref.typed_value_map.len;
3317
3318pub fn indexToRef(inst: Inst.Index) Inst.Ref {
3319 return @intToEnum(Inst.Ref, ref_start_index + inst);
3320}
3321
3322pub fn refToIndex(inst: Inst.Ref) ?Inst.Index {
3323 const ref_int = @enumToInt(inst);
3324 if (ref_int >= ref_start_index) {
3325 return ref_int - ref_start_index;
3326 } else {
3327 return null;
3328 }
3329}
src/print_zir.zig+24-101
......@@ -234,6 +234,8 @@ const Writer = struct {
234234 .@"await",
235235 .await_nosuspend,
236236 .fence,
237 .switch_cond,
238 .switch_cond_ref,
237239 => try self.writeUnNode(stream, inst),
238240
239241 .ref,
......@@ -379,19 +381,7 @@ const Writer = struct {
379381 .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
380382 .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
381383
382 .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none),
383 .switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
384 .switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under),
385 .switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none),
386 .switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
387 .switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under),
388
389 .switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
390 .switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
391 .switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
392 .switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
393 .switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
394 .switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
384 .switch_block => try self.writePlNodeSwitchBlock(stream, inst),
395385
396386 .field_ptr,
397387 .field_val,
......@@ -1649,113 +1639,46 @@ const Writer = struct {
16491639 try self.writeSrc(stream, inst_data.src());
16501640 }
16511641
1652 fn writePlNodeSwitchBr(
1653 self: *Writer,
1654 stream: anytype,
1655 inst: Zir.Inst.Index,
1656 special_prong: Zir.SpecialProng,
1657 ) !void {
1642 fn writePlNodeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
16581643 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
16591644 const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
1660 const special: struct {
1661 body: []const Zir.Inst.Index,
1662 end: usize,
1663 } = switch (special_prong) {
1664 .none => .{ .body = &.{}, .end = extra.end },
1665 .under, .@"else" => blk: {
1666 const body_len = self.code.extra[extra.end];
1667 const extra_body_start = extra.end + 1;
1668 break :blk .{
1669 .body = self.code.extra[extra_body_start..][0..body_len],
1670 .end = extra_body_start + body_len,
1671 };
1672 },
1673 };
1674
1675 try self.writeInstRef(stream, extra.data.operand);
1676
1677 self.indent += 2;
1678
1679 if (special.body.len != 0) {
1680 const prong_name = switch (special_prong) {
1681 .@"else" => "else",
1682 .under => "_",
1683 else => unreachable,
1684 };
1685 try stream.writeAll(",\n");
1686 try stream.writeByteNTimes(' ', self.indent);
1687 try stream.print("{s} => ", .{prong_name});
1688 try self.writeBracedBody(stream, special.body);
1689 }
1690
1691 var extra_index: usize = special.end;
1692 {
1693 var scalar_i: usize = 0;
1694 while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
1695 const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1696 extra_index += 1;
1697 const body_len = self.code.extra[extra_index];
1698 extra_index += 1;
1699 const body = self.code.extra[extra_index..][0..body_len];
1700 extra_index += body_len;
1701
1702 try stream.writeAll(",\n");
1703 try stream.writeByteNTimes(' ', self.indent);
1704 try self.writeInstRef(stream, item_ref);
1705 try stream.writeAll(" => ");
1706 try self.writeBracedBody(stream, body);
1707 }
1708 }
1709
1710 self.indent -= 2;
17111645
1712 try stream.writeAll(") ");
1713 try self.writeSrc(stream, inst_data.src());
1714 }
1646 var extra_index: usize = extra.end;
17151647
1716 fn writePlNodeSwitchBlockMulti(
1717 self: *Writer,
1718 stream: anytype,
1719 inst: Zir.Inst.Index,
1720 special_prong: Zir.SpecialProng,
1721 ) !void {
1722 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1723 const extra = self.code.extraData(Zir.Inst.SwitchBlockMulti, inst_data.payload_index);
1724 const special: struct {
1725 body: []const Zir.Inst.Index,
1726 end: usize,
1727 } = switch (special_prong) {
1728 .none => .{ .body = &.{}, .end = extra.end },
1729 .under, .@"else" => blk: {
1730 const body_len = self.code.extra[extra.end];
1731 const extra_body_start = extra.end + 1;
1732 break :blk .{
1733 .body = self.code.extra[extra_body_start..][0..body_len],
1734 .end = extra_body_start + body_len,
1735 };
1736 },
1737 };
1648 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
1649 const multi_cases_len = self.code.extra[extra_index];
1650 extra_index += 1;
1651 break :blk multi_cases_len;
1652 } else 0;
17381653
17391654 try self.writeInstRef(stream, extra.data.operand);
1655 try self.writeFlag(stream, ", ref", extra.data.bits.is_ref);
17401656
17411657 self.indent += 2;
17421658
1743 if (special.body.len != 0) {
1659 else_prong: {
1660 const special_prong = extra.data.bits.specialProng();
17441661 const prong_name = switch (special_prong) {
17451662 .@"else" => "else",
17461663 .under => "_",
1747 else => unreachable,
1664 else => break :else_prong,
17481665 };
1666
1667 const body_len = self.code.extra[extra_index];
1668 extra_index += 1;
1669 const body = self.code.extra[extra_index..][0..body_len];
1670 extra_index += body.len;
1671
17491672 try stream.writeAll(",\n");
17501673 try stream.writeByteNTimes(' ', self.indent);
17511674 try stream.print("{s} => ", .{prong_name});
1752 try self.writeBracedBody(stream, special.body);
1675 try self.writeBracedBody(stream, body);
17531676 }
17541677
1755 var extra_index: usize = special.end;
17561678 {
1679 const scalar_cases_len = extra.data.bits.scalar_cases_len;
17571680 var scalar_i: usize = 0;
1758 while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
1681 while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
17591682 const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
17601683 extra_index += 1;
17611684 const body_len = self.code.extra[extra_index];
......@@ -1772,7 +1695,7 @@ const Writer = struct {
17721695 }
17731696 {
17741697 var multi_i: usize = 0;
1775 while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) {
1698 while (multi_i < multi_cases_len) : (multi_i += 1) {
17761699 const items_len = self.code.extra[extra_index];
17771700 extra_index += 1;
17781701 const ranges_len = self.code.extra[extra_index];
src/value.zig+1-1
......@@ -116,7 +116,7 @@ pub const Value = extern union {
116116 decl_ref_mut,
117117 /// Pointer to a specific element of an array.
118118 elem_ptr,
119 /// Pointer to a specific field of a struct.
119 /// Pointer to a specific field of a struct or union.
120120 field_ptr,
121121 /// A slice of u8 whose memory is managed externally.
122122 bytes,
test/behavior/union.zig+31
......@@ -71,3 +71,34 @@ test "0-sized extern union definition" {
7171
7272 try expect(U.f == 1);
7373}
74
75const Value = union(enum) {
76 Int: u64,
77 Array: [9]u8,
78};
79
80const Agg = struct {
81 val1: Value,
82 val2: Value,
83};
84
85const v1 = Value{ .Int = 1234 };
86const v2 = Value{ .Array = [_]u8{3} ** 9 };
87
88const err = @as(anyerror!Agg, Agg{
89 .val1 = v1,
90 .val2 = v2,
91});
92
93const array = [_]Value{ v1, v2, v1, v2 };
94
95test "unions embedded in aggregate types" {
96 switch (array[1]) {
97 Value.Array => |arr| try expect(arr[4] == 3),
98 else => unreachable,
99 }
100 switch ((err catch unreachable).val1) {
101 Value.Int => |x| try expect(x == 1234),
102 else => unreachable,
103 }
104}
test/behavior/union_stage1.zig-31
......@@ -3,37 +3,6 @@ const expect = std.testing.expect;
33const expectEqual = std.testing.expectEqual;
44const Tag = std.meta.Tag;
55
6const Value = union(enum) {
7 Int: u64,
8 Array: [9]u8,
9};
10
11const Agg = struct {
12 val1: Value,
13 val2: Value,
14};
15
16const v1 = Value{ .Int = 1234 };
17const v2 = Value{ .Array = [_]u8{3} ** 9 };
18
19const err = @as(anyerror!Agg, Agg{
20 .val1 = v1,
21 .val2 = v2,
22});
23
24const array = [_]Value{ v1, v2, v1, v2 };
25
26test "unions embedded in aggregate types" {
27 switch (array[1]) {
28 Value.Array => |arr| try expect(arr[4] == 3),
29 else => unreachable,
30 }
31 switch ((err catch unreachable).val1) {
32 Value.Int => |x| try expect(x == 1234),
33 else => unreachable,
34 }
35}
36
376const Letter = enum { A, B, C };
387const Payload = union(Letter) {
398 A: i32,
test/stage2/cbe.zig+1-1
......@@ -852,7 +852,7 @@ pub fn addCases(ctx: *TestContext) !void {
852852 \\ _ = E.d;
853853 \\}
854854 , &.{
855 ":3:10: error: enum 'tmp.E' has no member named 'd'",
855 ":3:11: error: enum 'tmp.E' has no member named 'd'",
856856 ":1:11: note: enum declared here",
857857 });
858858