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;...@@ -11,6 +11,8 @@ const StringIndexAdapter = std.hash_map.StringIndexAdapter;
11const StringIndexContext = std.hash_map.StringIndexContext;11const StringIndexContext = std.hash_map.StringIndexContext;
1212
13const Zir = @import("Zir.zig");13const Zir = @import("Zir.zig");
14const refToIndex = Zir.refToIndex;
15const indexToRef = Zir.indexToRef;
14const trace = @import("tracy.zig").trace;16const trace = @import("tracy.zig").trace;
15const BuiltinFn = @import("BuiltinFn.zig");17const BuiltinFn = @import("BuiltinFn.zig");
1618
...@@ -57,6 +59,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {...@@ -57,6 +59,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
57 Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),59 Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
58 i32 => @bitCast(u32, @field(extra, field.name)),60 i32 => @bitCast(u32, @field(extra, field.name)),
59 Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),61 Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),
62 Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)),
60 else => @compileError("bad field type"),63 else => @compileError("bad field type"),
61 });64 });
62 }65 }
...@@ -2133,17 +2136,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2133,17 +2136,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2133 .slice_sentinel,2136 .slice_sentinel,
2134 .import,2137 .import,
2135 .switch_block,2138 .switch_block,
2136 .switch_block_multi,2139 .switch_cond,
2137 .switch_block_else,2140 .switch_cond_ref,
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,
2147 .switch_capture,2141 .switch_capture,
2148 .switch_capture_ref,2142 .switch_capture_ref,
2149 .switch_capture_multi,2143 .switch_capture_multi,
...@@ -5127,11 +5121,12 @@ fn fieldAccess(...@@ -5127,11 +5121,12 @@ fn fieldAccess(
5127 rl: ResultLoc,5121 rl: ResultLoc,
5128 node: Ast.Node.Index,5122 node: Ast.Node.Index,
5129) InnerError!Zir.Inst.Ref {5123) InnerError!Zir.Inst.Ref {
5130 if (rl == .ref) {5124 switch (rl) {
5131 return addFieldAccess(.field_ptr, gz, scope, .ref, node);5125 .ref => return addFieldAccess(.field_ptr, gz, scope, .ref, node),
5132 } else {5126 else => {
5133 const access = try addFieldAccess(.field_val, gz, scope, .none, node);5127 const access = try addFieldAccess(.field_val, gz, scope, .none, node);
5134 return rvalue(gz, rl, access, node);5128 return rvalue(gz, rl, access, node);
5129 },
5135 }5130 }
5136}5131}
51375132
...@@ -6028,11 +6023,13 @@ fn switchExpr(...@@ -6028,11 +6023,13 @@ fn switchExpr(
6028 }6023 }
60296024
6030 const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;6025 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);
6032 // We need the type of the operand to use as the result location for all the prong items.6029 // We need the type of the operand to use as the result location for all the prong items.
6033 const typeof_tag: Zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof;6030 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);6031 const cond_ty_inst = try parent_gz.addUnNode(typeof_tag, cond, operand_node);
6035 const item_rl: ResultLoc = .{ .ty = operand_ty_inst };6032 const item_rl: ResultLoc = .{ .ty = cond_ty_inst };
60366033
6037 // These contain the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti.6034 // These contain the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti.
6038 // This is the optional else prong body.6035 // This is the optional else prong body.
...@@ -6050,7 +6047,7 @@ fn switchExpr(...@@ -6050,7 +6047,7 @@ fn switchExpr(
6050 defer block_scope.instructions.deinit(gpa);6047 defer block_scope.instructions.deinit(gpa);
60516048
6052 // This gets added to the parent block later, after the item expressions.6049 // 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
6055 // We re-use this same scope for all cases, including the special prong, if any.6052 // We re-use this same scope for all cases, including the special prong, if any.
6056 var case_scope = parent_gz.makeSubBlock(&block_scope.base);6053 var case_scope = parent_gz.makeSubBlock(&block_scope.base);
...@@ -6203,44 +6200,32 @@ fn switchExpr(...@@ -6203,44 +6200,32 @@ fn switchExpr(
6203 // Now that the item expressions are generated we can add this.6200 // Now that the item expressions are generated we can add this.
6204 try parent_gz.instructions.append(gpa, switch_block);6201 try parent_gz.instructions.append(gpa, switch_block);
62056202
6206 const ref_bit: u4 = @boolToInt(any_payload_is_ref);6203 try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
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
6235 @boolToInt(multi_cases_len != 0) +6204 @boolToInt(multi_cases_len != 0) +
6236 special_case_payload.items.len +6205 special_case_payload.items.len +
6237 scalar_cases_payload.items.len +6206 scalar_cases_payload.items.len +
6238 multi_cases_payload.items.len);6207 multi_cases_payload.items.len);
6239 astgen.extra.appendAssumeCapacity(@enumToInt(operand));6208
6240 astgen.extra.appendAssumeCapacity(scalar_cases_len);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
6241 if (multi_cases_len != 0) {6225 if (multi_cases_len != 0) {
6242 astgen.extra.appendAssumeCapacity(multi_cases_len);6226 astgen.extra.appendAssumeCapacity(multi_cases_len);
6243 }6227 }
6228
6244 const strat = rl.strategy(&block_scope);6229 const strat = rl.strategy(&block_scope);
6245 switch (strat.tag) {6230 switch (strat.tag) {
6246 .break_operand => {6231 .break_operand => {
...@@ -10622,21 +10607,6 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {...@@ -10622,21 +10607,6 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {
10622 astgen.source_column = column;10607 astgen.source_column = column;
10623}10608}
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
10640fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !void {10610fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !void {
10641 const gpa = astgen.gpa;10611 const gpa = astgen.gpa;
10642 const tree = astgen.tree;10612 const tree = astgen.tree;
src/Sema.zig+206-125
...@@ -550,18 +550,9 @@ pub fn analyzeBody(...@@ -550,18 +550,9 @@ pub fn analyzeBody(
550 .slice_sentinel => try sema.zirSliceSentinel(block, inst),550 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
551 .slice_start => try sema.zirSliceStart(block, inst),551 .slice_start => try sema.zirSliceStart(block, inst),
552 .str => try sema.zirStr(block, inst),552 .str => try sema.zirStr(block, inst),
553 .switch_block => try sema.zirSwitchBlock(block, inst, false, .none),553 .switch_block => try sema.zirSwitchBlock(block, inst),
554 .switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),554 .switch_cond => try sema.zirSwitchCond(block, inst, false),
555 .switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),555 .switch_cond_ref => try sema.zirSwitchCond(block, inst, true),
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),
565 .switch_capture => try sema.zirSwitchCapture(block, inst, false, false),556 .switch_capture => try sema.zirSwitchCapture(block, inst, false, false),
566 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),557 .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
567 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),558 .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
...@@ -5433,11 +5424,80 @@ fn zirSwitchCapture(...@@ -5433,11 +5424,80 @@ fn zirSwitchCapture(
5433 const zir_datas = sema.code.instructions.items(.data);5424 const zir_datas = sema.code.instructions.items(.data);
5434 const capture_info = zir_datas[inst].switch_capture;5425 const capture_info = zir_datas[inst].switch_capture;
5435 const switch_info = zir_datas[capture_info.switch_inst].pl_node;5426 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;5445 switch (operand_ty.zigTypeTag()) {
5439 _ = is_multi;5446 .Union => {
5440 return sema.fail(block, src, "TODO implement Sema for zirSwitchCapture", .{});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 }
5441}5501}
54425502
5443fn zirSwitchCaptureElse(5503fn zirSwitchCaptureElse(
...@@ -5452,96 +5512,108 @@ fn zirSwitchCaptureElse(...@@ -5452,96 +5512,108 @@ fn zirSwitchCaptureElse(
5452 const zir_datas = sema.code.instructions.items(.data);5512 const zir_datas = sema.code.instructions.items(.data);
5453 const capture_info = zir_datas[inst].switch_capture;5513 const capture_info = zir_datas[inst].switch_capture;
5454 const switch_info = zir_datas[capture_info.switch_inst].pl_node;5514 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;
5455 const src = switch_info.src();5516 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;
5458 return sema.fail(block, src, "TODO implement Sema for zirSwitchCaptureElse", .{});5520 return sema.fail(block, src, "TODO implement Sema for zirSwitchCaptureElse", .{});
5459}5521}
54605522
5461fn zirSwitchBlock(5523fn zirSwitchCond(
5462 sema: *Sema,5524 sema: *Sema,
5463 block: *Block,5525 block: *Block,
5464 inst: Zir.Inst.Index,5526 inst: Zir.Inst.Index,
5465 is_ref: bool,5527 is_ref: bool,
5466 special_prong: Zir.SpecialProng,
5467) CompileError!Air.Inst.Ref {5528) CompileError!Air.Inst.Ref {
5468 const tracy = trace(@src());5529 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
5469 defer tracy.end();
5470
5471 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5472 const src = inst_data.src();5530 const src = inst_data.src();
5473 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };5531 const operand_ptr = sema.resolveInst(inst_data.operand);
5474 const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);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);5535 switch (operand_ty.zigTypeTag()) {
5477 const operand = if (is_ref)5536 .Type,
5478 try sema.analyzeLoad(block, src, operand_ptr, operand_src)5537 .Void,
5479 else5538 .Bool,
5480 operand_ptr;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(5555 .Union => {
5483 block,5556 const enum_ty = operand_ty.unionTagType() orelse {
5484 operand,5557 const msg = msg: {
5485 extra.end,5558 const msg = try sema.errMsg(block, src, "switch on untagged union", .{});
5486 special_prong,5559 errdefer msg.destroy(sema.gpa);
5487 extra.data.cases_len,5560 try sema.addDeclaredHereNote(msg, operand_ty);
5488 0,5561 break :msg msg;
5489 inst,5562 };
5490 inst_data.src_node,5563 return sema.failWithOwnedErrorMsg(msg);
5491 );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 }
5492}5582}
54935583
5494fn zirSwitchBlockMulti(5584fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
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 {
5501 const tracy = trace(@src());5585 const tracy = trace(@src());
5502 defer tracy.end();5586 defer tracy.end();
55035587
5588 const gpa = sema.gpa;
5504 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;5589 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
5505 const src = inst_data.src();5590 const src = inst_data.src();
5506 const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };5591 const src_node_offset = inst_data.src_node;
5507 const extra = sema.code.extraData(Zir.Inst.SwitchBlockMulti, inst_data.payload_index);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
5509 const operand_ptr = sema.resolveInst(extra.data.operand);5596 const operand_ptr = sema.resolveInst(extra.data.operand);
5510 const operand = if (is_ref)5597 const operand = if (extra.data.bits.is_ref)
5511 try sema.analyzeLoad(block, src, operand_ptr, operand_src)5598 try sema.analyzeLoad(block, src, operand_ptr, operand_src)
5512 else5599 else
5513 operand_ptr;5600 operand_ptr;
55145601
5515 return sema.analyzeSwitch(5602 var header_extra_index: usize = extra.end;
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}
55265603
5527fn analyzeSwitch(5604 const scalar_cases_len = extra.data.bits.scalar_cases_len;
5528 sema: *Sema,5605 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
5529 block: *Block,5606 const multi_cases_len = sema.code.extra[header_extra_index];
5530 operand: Air.Inst.Ref,5607 header_extra_index += 1;
5531 extra_end: usize,5608 break :blk multi_cases_len;
5532 special_prong: Zir.SpecialProng,5609 } else 0;
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;
55395610
5611 const special_prong = extra.data.bits.specialProng();
5540 const special: struct { body: []const Zir.Inst.Index, end: usize } = switch (special_prong) {5612 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 },
5542 .under, .@"else" => blk: {5614 .under, .@"else" => blk: {
5543 const body_len = sema.code.extra[extra_end];5615 const body_len = sema.code.extra[header_extra_index];
5544 const extra_body_start = extra_end + 1;5616 const extra_body_start = header_extra_index + 1;
5545 break :blk .{5617 break :blk .{
5546 .body = sema.code.extra[extra_body_start..][0..body_len],5618 .body = sema.code.extra[extra_body_start..][0..body_len],
5547 .end = extra_body_start + body_len,5619 .end = extra_body_start + body_len,
...@@ -5549,9 +5621,6 @@ fn analyzeSwitch(...@@ -5549,9 +5621,6 @@ fn analyzeSwitch(
5549 },5621 },
5550 };5622 };
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 };
5555 const operand_ty = sema.typeOf(operand);5624 const operand_ty = sema.typeOf(operand);
55565625
5557 // Validate usage of '_' prongs.5626 // Validate usage of '_' prongs.
...@@ -5945,7 +6014,7 @@ fn analyzeSwitch(...@@ -5945,7 +6014,7 @@ fn analyzeSwitch(
5945 .data = undefined,6014 .data = undefined,
5946 });6015 });
5947 var label: Block.Label = .{6016 var label: Block.Label = .{
5948 .zir_block = switch_inst,6017 .zir_block = inst,
5949 .merges = .{6018 .merges = .{
5950 .results = .{},6019 .results = .{},
5951 .br_list = .{},6020 .br_list = .{},
...@@ -8934,8 +9003,9 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)...@@ -8934,8 +9003,9 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
8934 const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };9003 const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
8935 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;9004 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
8936 const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);9005 const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
8937 const field_index = union_obj.fields.getIndex(field_name) orelse9006 const field_index_usize = union_obj.fields.getIndex(field_name) orelse
8938 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);9007 return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
9008 const field_index = @intCast(u32, field_index_usize);
89399009
8940 if (is_ref) {9010 if (is_ref) {
8941 return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true union", .{});9011 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)...@@ -8943,12 +9013,10 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
89439013
8944 const init_inst = sema.resolveInst(item.data.init);9014 const init_inst = sema.resolveInst(item.data.init);
8945 if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| {9015 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);
8946 return sema.addConstant(9017 return sema.addConstant(
8947 resolved_ty,9018 resolved_ty,
8948 try Value.Tag.@"union".create(sema.arena, .{9019 try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, .val = val }),
8949 .tag = try Value.Tag.int_u64.create(sema.arena, field_index),
8950 .val = val,
8951 }),
8952 );9020 );
8953 }9021 }
8954 return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known union values", .{});9022 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...@@ -9152,8 +9220,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
9152 const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);9220 const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
9153 const val = try sema.resolveConstValue(block, operand_src, type_info);9221 const val = try sema.resolveConstValue(block, operand_src, type_info);
9154 const union_val = val.cast(Value.Payload.Union).?.data;9222 const union_val = val.cast(Value.Payload.Union).?.data;
9155 const TypeInfoTag = std.meta.Tag(std.builtin.TypeInfo);9223 const tag_ty = type_info_ty.unionTagType().?;
9156 const tag_index = @intCast(std.meta.Tag(TypeInfoTag), union_val.tag.toUnsignedInt());9224 const tag_index = tag_ty.enumTagFieldIndex(union_val.tag).?;
9157 switch (@intToEnum(std.builtin.TypeId, tag_index)) {9225 switch (@intToEnum(std.builtin.TypeId, tag_index)) {
9158 .Type => return Air.Inst.Ref.type_type,9226 .Type => return Air.Inst.Ref.type_type,
9159 .Void => return Air.Inst.Ref.void_type,9227 .Void => return Air.Inst.Ref.void_type,
...@@ -10819,10 +10887,39 @@ fn fieldVal(...@@ -10819,10 +10887,39 @@ fn fieldVal(
10819 try Value.Tag.@"error".create(arena, .{ .name = name }),10887 try Value.Tag.@"error".create(arena, .{ .name = name }),
10820 );10888 );
10821 },10889 },
10822 .Struct, .Opaque, .Union => {10890 .Union => {
10823 if (child_type.getNamespace()) |namespace| {10891 if (child_type.getNamespace()) |namespace| {
10824 if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {10892 if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
10825 return sema.analyzeLoad(block, src, inst, src);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;
10826 }10923 }
10827 }10924 }
10828 // TODO add note: declared here10925 // TODO add note: declared here
...@@ -10836,35 +10933,6 @@ fn fieldVal(...@@ -10836,35 +10933,6 @@ fn fieldVal(
10836 kw_name, child_type, field_name,10933 kw_name, child_type, field_name,
10837 });10934 });
10838 },10935 },
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 },
10868 else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),10936 else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
10869 }10937 }
10870 },10938 },
...@@ -11244,6 +11312,17 @@ fn namespaceLookupRef(...@@ -11244,6 +11312,17 @@ fn namespaceLookupRef(
11244 return try sema.analyzeDeclRef(decl);11312 return try sema.analyzeDeclRef(decl);
11245}11313}
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
11247fn structFieldPtr(11326fn structFieldPtr(
11248 sema: *Sema,11327 sema: *Sema,
11249 block: *Block,11328 block: *Block,
...@@ -11370,10 +11449,9 @@ fn unionFieldVal(...@@ -11370,10 +11449,9 @@ fn unionFieldVal(
11370 const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);11449 const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
11371 const union_obj = union_ty.cast(Type.Payload.Union).?.data;11450 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
1137211451
11373 const field_index_big = union_obj.fields.getIndex(field_name) orelse11452 const field_index_usize = union_obj.fields.getIndex(field_name) orelse
11374 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);11453 return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
11375 const field_index = @intCast(u32, field_index_big);11454 const field_index = @intCast(u32, field_index_usize);
11376
11377 const field = union_obj.fields.values()[field_index];11455 const field = union_obj.fields.values()[field_index];
1137811456
11379 if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| {11457 if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| {
...@@ -12960,15 +13038,18 @@ fn wrapErrorUnion(...@@ -12960,15 +13038,18 @@ fn wrapErrorUnion(
12960fn unionToTag(13038fn unionToTag(
12961 sema: *Sema,13039 sema: *Sema,
12962 block: *Block,13040 block: *Block,
12963 dest_ty: Type,13041 enum_ty: Type,
12964 un: Air.Inst.Ref,13042 un: Air.Inst.Ref,
12965 un_src: LazySrcLoc,13043 un_src: LazySrcLoc,
12966) !Air.Inst.Ref {13044) !Air.Inst.Ref {
13045 if ((try sema.typeHasOnePossibleValue(block, un_src, enum_ty))) |opv| {
13046 return sema.addConstant(enum_ty, opv);
13047 }
12967 if (try sema.resolveMaybeUndefVal(block, un_src, un)) |un_val| {13048 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());
12969 }13050 }
12970 try sema.requireRuntimeBlock(block, un_src);13051 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);
12972}13053}
1297313054
12974fn resolvePeerTypes(13055fn resolvePeerTypes(
src/Zir.zig+123-149
...@@ -72,6 +72,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en...@@ -72,6 +72,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en
72 Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),72 Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
73 i32 => @bitCast(i32, code.extra[i]),73 i32 => @bitCast(i32, code.extra[i]),
74 Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]),74 Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]),
75 Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]),
75 else => @compileError("bad field type"),76 else => @compileError("bad field type"),
76 };77 };
77 i += 1;78 i += 1;
...@@ -618,39 +619,16 @@ pub const Inst = struct {...@@ -618,39 +619,16 @@ pub const Inst = struct {
618 enum_literal,619 enum_literal,
619 /// A switch expression. Uses the `pl_node` union field.620 /// A switch expression. Uses the `pl_node` union field.
620 /// AST node is the switch, payload is `SwitchBlock`.621 /// AST node is the switch, payload is `SwitchBlock`.
621 /// All prongs of target handled.
622 switch_block,622 switch_block,
623 /// Same as switch_block, except one or more prongs have multiple items.623 /// Produces the value that will be switched on. For example, for
624 /// Payload is `SwitchBlockMulti`624 /// integers, it returns the integer with no modifications. For tagged unions, it
625 switch_block_multi,625 /// returns the active enum tag.
626 /// Same as switch_block, except has an else prong.626 /// Uses the `un_node` union field.
627 switch_block_else,627 switch_cond,
628 /// Same as switch_block_else, except one or more prongs have multiple items.628 /// Same as `switch_cond`, except the input operand is a pointer to
629 /// Payload is `SwitchBlockMulti`629 /// what will be switched on.
630 switch_block_else_multi,630 /// Uses the `un_node` union field.
631 /// Same as switch_block, except has an underscore prong.631 switch_cond_ref,
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,
654 /// Produces the capture value for a switch prong.632 /// Produces the capture value for a switch prong.
655 /// Uses the `switch_capture` field.633 /// Uses the `switch_capture` field.
656 switch_capture,634 switch_capture,
...@@ -1109,17 +1087,8 @@ pub const Inst = struct {...@@ -1109,17 +1087,8 @@ pub const Inst = struct {
1109 .switch_capture_else,1087 .switch_capture_else,
1110 .switch_capture_else_ref,1088 .switch_capture_else_ref,
1111 .switch_block,1089 .switch_block,
1112 .switch_block_multi,1090 .switch_cond,
1113 .switch_block_else,1091 .switch_cond_ref,
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,
1123 .validate_struct_init,1092 .validate_struct_init,
1124 .validate_array_init,1093 .validate_array_init,
1125 .struct_init_empty,1094 .struct_init_empty,
...@@ -1367,17 +1336,8 @@ pub const Inst = struct {...@@ -1367,17 +1336,8 @@ pub const Inst = struct {
1367 .ensure_err_payload_void = .un_tok,1336 .ensure_err_payload_void = .un_tok,
1368 .enum_literal = .str_tok,1337 .enum_literal = .str_tok,
1369 .switch_block = .pl_node,1338 .switch_block = .pl_node,
1370 .switch_block_multi = .pl_node,1339 .switch_cond = .un_node,
1371 .switch_block_else = .pl_node,1340 .switch_cond_ref = .un_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,
1381 .switch_capture = .switch_capture,1341 .switch_capture = .switch_capture,
1382 .switch_capture_ref = .switch_capture,1342 .switch_capture_ref = .switch_capture,
1383 .switch_capture_multi = .switch_capture,1343 .switch_capture_multi = .switch_capture,
...@@ -2466,37 +2426,17 @@ pub const Inst = struct {...@@ -2466,37 +2426,17 @@ pub const Inst = struct {
2466 index: u32,2426 index: u32,
2467 };2427 };
24682428
2469 /// This form is supported when there are no ranges, and exactly 1 item per block.2429 /// 0. multi_cases_len: u32 // If has_multi_cases is set.
2470 /// Depending on zir tag and len fields, extra fields trail2430 /// 1. else_body { // If has_else or has_under is set.
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.
2491 /// body_len: u32,2431 /// body_len: u32,
2492 /// body member Index for every body_len2432 /// body member Index for every body_len
2493 /// }2433 /// }
2494 /// 1. scalar_cases: { // for every scalar_cases_len2434 /// 2. scalar_cases: { // for every scalar_cases_len
2495 /// item: Ref,2435 /// item: Ref,
2496 /// body_len: u32,2436 /// body_len: u32,
2497 /// body member Index for every body_len2437 /// body member Index for every body_len
2498 /// }2438 /// }
2499 /// 2. multi_cases: { // for every multi_cases_len2439 /// 3. multi_cases: { // for every multi_cases_len
2500 /// items_len: u32,2440 /// items_len: u32,
2501 /// ranges_len: u32,2441 /// ranges_len: u32,
2502 /// body_len: u32,2442 /// body_len: u32,
...@@ -2507,10 +2447,78 @@ pub const Inst = struct {...@@ -2507,10 +2447,78 @@ pub const Inst = struct {
2507 /// }2447 /// }
2508 /// body member Index for every body_len2448 /// body member Index for every body_len
2509 /// }2449 /// }
2510 pub const SwitchBlockMulti = struct {2450 pub const SwitchBlock = struct {
2511 operand: Ref,2451 operand: Ref,
2512 scalar_cases_len: u32,2452 bits: Bits,
2513 multi_cases_len: u32,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 }
2514 };2522 };
25152523
2516 pub const Field = struct {2524 pub const Field = struct {
...@@ -2934,7 +2942,7 @@ pub const Inst = struct {...@@ -2934,7 +2942,7 @@ pub const Inst = struct {
29342942
2935 /// Trailing: for each `imports_len` there is an Item2943 /// Trailing: for each `imports_len` there is an Item
2936 pub const Imports = struct {2944 pub const Imports = struct {
2937 imports_len: Zir.Inst.Index,2945 imports_len: Inst.Index,
29382946
2939 pub const Item = struct {2947 pub const Item = struct {
2940 /// null terminated string index2948 /// null terminated string index
...@@ -3077,7 +3085,7 @@ pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclItera...@@ -3077,7 +3085,7 @@ pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclItera
30773085
3078/// The iterator would have to allocate memory anyway to iterate. So here we populate3086/// The iterator would have to allocate memory anyway to iterate. So here we populate
3079/// an ArrayList as the result.3087/// 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 {
3081 const block_inst = zir.extra[decl_sub_index + 6];3089 const block_inst = zir.extra[decl_sub_index + 6];
3082 list.clearRetainingCapacity();3090 list.clearRetainingCapacity();
30833091
...@@ -3086,8 +3094,8 @@ pub fn findDecls(zir: Zir, list: *std.ArrayList(Zir.Inst.Index), decl_sub_index:...@@ -3086,8 +3094,8 @@ pub fn findDecls(zir: Zir, list: *std.ArrayList(Zir.Inst.Index), decl_sub_index:
30863094
3087fn findDeclsInner(3095fn findDeclsInner(
3088 zir: Zir,3096 zir: Zir,
3089 list: *std.ArrayList(Zir.Inst.Index),3097 list: *std.ArrayList(Inst.Index),
3090 inst: Zir.Inst.Index,3098 inst: Inst.Index,
3091) Allocator.Error!void {3099) Allocator.Error!void {
3092 const tags = zir.instructions.items(.tag);3100 const tags = zir.instructions.items(.tag);
3093 const datas = zir.instructions.items(.data);3101 const datas = zir.instructions.items(.data);
...@@ -3148,19 +3156,7 @@ fn findDeclsInner(...@@ -3148,19 +3156,7 @@ fn findDeclsInner(
3148 try zir.findDeclsBody(list, then_body);3156 try zir.findDeclsBody(list, then_body);
3149 try zir.findDeclsBody(list, else_body);3157 try zir.findDeclsBody(list, else_body);
3150 },3158 },
3151 .switch_block => return findDeclsSwitch(zir, list, inst, .none),3159 .switch_block => return findDeclsSwitch(zir, list, inst),
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),
31643160
3165 .suspend_block => @panic("TODO iterate suspend block"),3161 .suspend_block => @panic("TODO iterate suspend block"),
31663162
...@@ -3170,71 +3166,34 @@ fn findDeclsInner(...@@ -3170,71 +3166,34 @@ fn findDeclsInner(
31703166
3171fn findDeclsSwitch(3167fn findDeclsSwitch(
3172 zir: Zir,3168 zir: Zir,
3173 list: *std.ArrayList(Zir.Inst.Index),3169 list: *std.ArrayList(Inst.Index),
3174 inst: Zir.Inst.Index,3170 inst: Inst.Index,
3175 special_prong: SpecialProng,
3176) Allocator.Error!void {3171) Allocator.Error!void {
3177 const inst_data = zir.instructions.items(.data)[inst].pl_node;3172 const inst_data = zir.instructions.items(.data)[inst].pl_node;
3178 const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index);3173 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;3177 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
3197 var scalar_i: usize = 0;3178 const multi_cases_len = zir.extra[extra_index];
3198 while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
3199 extra_index += 1;3179 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) {
3200 const body_len = zir.extra[extra_index];3185 const body_len = zir.extra[extra_index];
3201 extra_index += 1;3186 extra_index += 1;
3202 const body = zir.extra[extra_index..][0..body_len];3187 const body = zir.extra[extra_index..][0..body_len];
3203 extra_index += body_len;3188 extra_index += body.len;
32043189
3205 try zir.findDeclsBody(list, body);3190 try zir.findDeclsBody(list, body);
3206 }3191 }
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;
3235 {3193 {
3194 const scalar_cases_len = extra.data.bits.scalar_cases_len;
3236 var scalar_i: usize = 0;3195 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) {
3238 extra_index += 1;3197 extra_index += 1;
3239 const body_len = zir.extra[extra_index];3198 const body_len = zir.extra[extra_index];
3240 extra_index += 1;3199 extra_index += 1;
...@@ -3246,7 +3205,7 @@ fn findDeclsSwitchMulti(...@@ -3246,7 +3205,7 @@ fn findDeclsSwitchMulti(
3246 }3205 }
3247 {3206 {
3248 var multi_i: usize = 0;3207 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) {
3250 const items_len = zir.extra[extra_index];3209 const items_len = zir.extra[extra_index];
3251 extra_index += 1;3210 extra_index += 1;
3252 const ranges_len = zir.extra[extra_index];3211 const ranges_len = zir.extra[extra_index];
...@@ -3353,3 +3312,18 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {...@@ -3353,3 +3312,18 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
3353 .total_params_len = total_params_len,3312 .total_params_len = total_params_len,
3354 };3313 };
3355}3314}
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 {...@@ -234,6 +234,8 @@ const Writer = struct {
234 .@"await",234 .@"await",
235 .await_nosuspend,235 .await_nosuspend,
236 .fence,236 .fence,
237 .switch_cond,
238 .switch_cond_ref,
237 => try self.writeUnNode(stream, inst),239 => try self.writeUnNode(stream, inst),
238240
239 .ref,241 .ref,
...@@ -379,19 +381,7 @@ const Writer = struct {...@@ -379,19 +381,7 @@ const Writer = struct {
379 .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),381 .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
380 .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),382 .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
381383
382 .switch_block => try self.writePlNodeSwitchBr(stream, inst, .none),384 .switch_block => try self.writePlNodeSwitchBlock(stream, inst),
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),
395385
396 .field_ptr,386 .field_ptr,
397 .field_val,387 .field_val,
...@@ -1649,113 +1639,46 @@ const Writer = struct {...@@ -1649,113 +1639,46 @@ const Writer = struct {
1649 try self.writeSrc(stream, inst_data.src());1639 try self.writeSrc(stream, inst_data.src());
1650 }1640 }
16511641
1652 fn writePlNodeSwitchBr(1642 fn writePlNodeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1653 self: *Writer,
1654 stream: anytype,
1655 inst: Zir.Inst.Index,
1656 special_prong: Zir.SpecialProng,
1657 ) !void {
1658 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1643 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1659 const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);1644 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(") ");1646 var extra_index: usize = extra.end;
1713 try self.writeSrc(stream, inst_data.src());
1714 }
17151647
1716 fn writePlNodeSwitchBlockMulti(1648 const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
1717 self: *Writer,1649 const multi_cases_len = self.code.extra[extra_index];
1718 stream: anytype,1650 extra_index += 1;
1719 inst: Zir.Inst.Index,1651 break :blk multi_cases_len;
1720 special_prong: Zir.SpecialProng,1652 } else 0;
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 };
17381653
1739 try self.writeInstRef(stream, extra.data.operand);1654 try self.writeInstRef(stream, extra.data.operand);
1655 try self.writeFlag(stream, ", ref", extra.data.bits.is_ref);
17401656
1741 self.indent += 2;1657 self.indent += 2;
17421658
1743 if (special.body.len != 0) {1659 else_prong: {
1660 const special_prong = extra.data.bits.specialProng();
1744 const prong_name = switch (special_prong) {1661 const prong_name = switch (special_prong) {
1745 .@"else" => "else",1662 .@"else" => "else",
1746 .under => "_",1663 .under => "_",
1747 else => unreachable,1664 else => break :else_prong,
1748 };1665 };
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
1749 try stream.writeAll(",\n");1672 try stream.writeAll(",\n");
1750 try stream.writeByteNTimes(' ', self.indent);1673 try stream.writeByteNTimes(' ', self.indent);
1751 try stream.print("{s} => ", .{prong_name});1674 try stream.print("{s} => ", .{prong_name});
1752 try self.writeBracedBody(stream, special.body);1675 try self.writeBracedBody(stream, body);
1753 }1676 }
17541677
1755 var extra_index: usize = special.end;
1756 {1678 {
1679 const scalar_cases_len = extra.data.bits.scalar_cases_len;
1757 var scalar_i: usize = 0;1680 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) {
1759 const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);1682 const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
1760 extra_index += 1;1683 extra_index += 1;
1761 const body_len = self.code.extra[extra_index];1684 const body_len = self.code.extra[extra_index];
...@@ -1772,7 +1695,7 @@ const Writer = struct {...@@ -1772,7 +1695,7 @@ const Writer = struct {
1772 }1695 }
1773 {1696 {
1774 var multi_i: usize = 0;1697 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) {
1776 const items_len = self.code.extra[extra_index];1699 const items_len = self.code.extra[extra_index];
1777 extra_index += 1;1700 extra_index += 1;
1778 const ranges_len = self.code.extra[extra_index];1701 const ranges_len = self.code.extra[extra_index];
src/value.zig+1-1
...@@ -116,7 +116,7 @@ pub const Value = extern union {...@@ -116,7 +116,7 @@ pub const Value = extern union {
116 decl_ref_mut,116 decl_ref_mut,
117 /// Pointer to a specific element of an array.117 /// Pointer to a specific element of an array.
118 elem_ptr,118 elem_ptr,
119 /// Pointer to a specific field of a struct.119 /// Pointer to a specific field of a struct or union.
120 field_ptr,120 field_ptr,
121 /// A slice of u8 whose memory is managed externally.121 /// A slice of u8 whose memory is managed externally.
122 bytes,122 bytes,
test/behavior/union.zig+31
...@@ -71,3 +71,34 @@ test "0-sized extern union definition" {...@@ -71,3 +71,34 @@ test "0-sized extern union definition" {
7171
72 try expect(U.f == 1);72 try expect(U.f == 1);
73}73}
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;...@@ -3,37 +3,6 @@ const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;3const expectEqual = std.testing.expectEqual;
4const Tag = std.meta.Tag;4const 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
37const Letter = enum { A, B, C };6const Letter = enum { A, B, C };
38const Payload = union(Letter) {7const Payload = union(Letter) {
39 A: i32,8 A: i32,
test/stage2/cbe.zig+1-1
...@@ -852,7 +852,7 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -852,7 +852,7 @@ pub fn addCases(ctx: *TestContext) !void {
852 \\ _ = E.d;852 \\ _ = E.d;
853 \\}853 \\}
854 , &.{854 , &.{
855 ":3:10: error: enum 'tmp.E' has no member named 'd'",855 ":3:11: error: enum 'tmp.E' has no member named 'd'",
856 ":1:11: note: enum declared here",856 ":1:11: note: enum declared here",
857 });857 });
858858