| author | |
| committer | |
| log | dfb3231959bb340d260ddbec2b8eabfb5063c1bf |
| tree | 1f91b36fc9ed4b0aaa35fd8f8657efb365f3ec74 |
| parent | 4a76523b92bcf0e9b48438cb22f49e67e0ab3fa1 |
* 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 | 11 | const StringIndexContext = std.hash_map.StringIndexContext; |
| 12 | 12 | |
| 13 | 13 | const Zir = @import("Zir.zig"); |
| 14 | const refToIndex = Zir.refToIndex; | |
| 15 | const indexToRef = Zir.indexToRef; | |
| 14 | 16 | const trace = @import("tracy.zig").trace; |
| 15 | 17 | const BuiltinFn = @import("BuiltinFn.zig"); |
| 16 | 18 | |
| ... | ... | @@ -57,6 +59,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 { |
| 57 | 59 | Zir.Inst.Ref => @enumToInt(@field(extra, field.name)), |
| 58 | 60 | i32 => @bitCast(u32, @field(extra, field.name)), |
| 59 | 61 | Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)), |
| 62 | Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)), | |
| 60 | 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 | 2136 | .slice_sentinel, |
| 2134 | 2137 | .import, |
| 2135 | 2138 | .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, | |
| 2147 | 2141 | .switch_capture, |
| 2148 | 2142 | .switch_capture_ref, |
| 2149 | 2143 | .switch_capture_multi, |
| ... | ... | @@ -5127,11 +5121,12 @@ fn fieldAccess( |
| 5127 | 5121 | rl: ResultLoc, |
| 5128 | 5122 | node: Ast.Node.Index, |
| 5129 | 5123 | ) 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 | }, | |
| 5135 | 5130 | } |
| 5136 | 5131 | } |
| 5137 | 5132 | |
| ... | ... | @@ -6028,11 +6023,13 @@ fn switchExpr( |
| 6028 | 6023 | } |
| 6029 | 6024 | |
| 6030 | 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 | 6029 | // We need the type of the operand to use as the result location for all the prong items. |
| 6033 | 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); | |
| 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 }; | |
| 6036 | 6033 | |
| 6037 | 6034 | // These contain the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti. |
| 6038 | 6035 | // This is the optional else prong body. |
| ... | ... | @@ -6050,7 +6047,7 @@ fn switchExpr( |
| 6050 | 6047 | defer block_scope.instructions.deinit(gpa); |
| 6051 | 6048 | |
| 6052 | 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); | |
| 6054 | 6051 | |
| 6055 | 6052 | // We re-use this same scope for all cases, including the special prong, if any. |
| 6056 | 6053 | var case_scope = parent_gz.makeSubBlock(&block_scope.base); |
| ... | ... | @@ -6203,44 +6200,32 @@ fn switchExpr( |
| 6203 | 6200 | // Now that the item expressions are generated we can add this. |
| 6204 | 6201 | try parent_gz.instructions.append(gpa, switch_block); |
| 6205 | 6202 | |
| 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 + | |
| 6235 | 6204 | @boolToInt(multi_cases_len != 0) + |
| 6236 | 6205 | special_case_payload.items.len + |
| 6237 | 6206 | scalar_cases_payload.items.len + |
| 6238 | 6207 | 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 | ||
| 6241 | 6225 | if (multi_cases_len != 0) { |
| 6242 | 6226 | astgen.extra.appendAssumeCapacity(multi_cases_len); |
| 6243 | 6227 | } |
| 6228 | ||
| 6244 | 6229 | const strat = rl.strategy(&block_scope); |
| 6245 | 6230 | switch (strat.tag) { |
| 6246 | 6231 | .break_operand => { |
| ... | ... | @@ -10622,21 +10607,6 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void { |
| 10622 | 10607 | astgen.source_column = column; |
| 10623 | 10608 | } |
| 10624 | 10609 | |
| 10625 | const ref_start_index: u32 = Zir.Inst.Ref.typed_value_map.len; | |
| 10626 | ||
| 10627 | fn indexToRef(inst: Zir.Inst.Index) Zir.Inst.Ref { | |
| 10628 | return @intToEnum(Zir.Inst.Ref, ref_start_index + inst); | |
| 10629 | } | |
| 10630 | ||
| 10631 | fn 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 | ||
| 10640 | 10610 | fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !void { |
| 10641 | 10611 | const gpa = astgen.gpa; |
| 10642 | 10612 | const tree = astgen.tree; |
src/Sema.zig+206-125| ... | ... | @@ -550,18 +550,9 @@ pub fn analyzeBody( |
| 550 | 550 | .slice_sentinel => try sema.zirSliceSentinel(block, inst), |
| 551 | 551 | .slice_start => try sema.zirSliceStart(block, inst), |
| 552 | 552 | .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), | |
| 565 | 556 | .switch_capture => try sema.zirSwitchCapture(block, inst, false, false), |
| 566 | 557 | .switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true), |
| 567 | 558 | .switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false), |
| ... | ... | @@ -5433,11 +5424,80 @@ fn zirSwitchCapture( |
| 5433 | 5424 | const zir_datas = sema.code.instructions.items(.data); |
| 5434 | 5425 | const capture_info = zir_datas[inst].switch_capture; |
| 5435 | 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; | |
| 5437 | 5444 | |
| 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 | } | |
| 5441 | 5501 | } |
| 5442 | 5502 | |
| 5443 | 5503 | fn zirSwitchCaptureElse( |
| ... | ... | @@ -5452,96 +5512,108 @@ fn zirSwitchCaptureElse( |
| 5452 | 5512 | const zir_datas = sema.code.instructions.items(.data); |
| 5453 | 5513 | const capture_info = zir_datas[inst].switch_capture; |
| 5454 | 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 | 5516 | const src = switch_info.src(); |
| 5517 | const operand_is_ref = switch_extra.bits.is_ref; | |
| 5518 | assert(!is_ref or operand_is_ref); | |
| 5456 | 5519 | |
| 5457 | _ = is_ref; | |
| 5458 | 5520 | return sema.fail(block, src, "TODO implement Sema for zirSwitchCaptureElse", .{}); |
| 5459 | 5521 | } |
| 5460 | 5522 | |
| 5461 | fn zirSwitchBlock( | |
| 5523 | fn zirSwitchCond( | |
| 5462 | 5524 | sema: *Sema, |
| 5463 | 5525 | block: *Block, |
| 5464 | 5526 | inst: Zir.Inst.Index, |
| 5465 | 5527 | is_ref: bool, |
| 5466 | special_prong: Zir.SpecialProng, | |
| 5467 | 5528 | ) 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; | |
| 5472 | 5530 | 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); | |
| 5475 | 5534 | |
| 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 | }, | |
| 5481 | 5554 | |
| 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 | } | |
| 5492 | 5582 | } |
| 5493 | 5583 | |
| 5494 | fn 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 { | |
| 5584 | fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | |
| 5501 | 5585 | const tracy = trace(@src()); |
| 5502 | 5586 | defer tracy.end(); |
| 5503 | 5587 | |
| 5588 | const gpa = sema.gpa; | |
| 5504 | 5589 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 5505 | 5590 | 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); | |
| 5508 | 5595 | |
| 5509 | 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 | 5598 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) |
| 5512 | 5599 | else |
| 5513 | 5600 | operand_ptr; |
| 5514 | 5601 | |
| 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; | |
| 5526 | 5603 | |
| 5527 | fn 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; | |
| 5539 | 5610 | |
| 5611 | const special_prong = extra.data.bits.specialProng(); | |
| 5540 | 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 | 5614 | .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; | |
| 5545 | 5617 | break :blk .{ |
| 5546 | 5618 | .body = sema.code.extra[extra_body_start..][0..body_len], |
| 5547 | 5619 | .end = extra_body_start + body_len, |
| ... | ... | @@ -5549,9 +5621,6 @@ fn analyzeSwitch( |
| 5549 | 5621 | }, |
| 5550 | 5622 | }; |
| 5551 | 5623 | |
| 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 | 5624 | const operand_ty = sema.typeOf(operand); |
| 5556 | 5625 | |
| 5557 | 5626 | // Validate usage of '_' prongs. |
| ... | ... | @@ -5945,7 +6014,7 @@ fn analyzeSwitch( |
| 5945 | 6014 | .data = undefined, |
| 5946 | 6015 | }); |
| 5947 | 6016 | var label: Block.Label = .{ |
| 5948 | .zir_block = switch_inst, | |
| 6017 | .zir_block = inst, | |
| 5949 | 6018 | .merges = .{ |
| 5950 | 6019 | .results = .{}, |
| 5951 | 6020 | .br_list = .{}, |
| ... | ... | @@ -8934,8 +9003,9 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) |
| 8934 | 9003 | const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node }; |
| 8935 | 9004 | const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; |
| 8936 | 9005 | 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 | |
| 8938 | 9007 | return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); |
| 9008 | const field_index = @intCast(u32, field_index_usize); | |
| 8939 | 9009 | |
| 8940 | 9010 | if (is_ref) { |
| 8941 | 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 | 9013 | |
| 8944 | 9014 | const init_inst = sema.resolveInst(item.data.init); |
| 8945 | 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 | 9017 | return sema.addConstant( |
| 8947 | 9018 | 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 }), | |
| 8952 | 9020 | ); |
| 8953 | 9021 | } |
| 8954 | 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 | 9220 | const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src); |
| 9153 | 9221 | const val = try sema.resolveConstValue(block, operand_src, type_info); |
| 9154 | 9222 | 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).?; | |
| 9157 | 9225 | switch (@intToEnum(std.builtin.TypeId, tag_index)) { |
| 9158 | 9226 | .Type => return Air.Inst.Ref.type_type, |
| 9159 | 9227 | .Void => return Air.Inst.Ref.void_type, |
| ... | ... | @@ -10819,10 +10887,39 @@ fn fieldVal( |
| 10819 | 10887 | try Value.Tag.@"error".create(arena, .{ .name = name }), |
| 10820 | 10888 | ); |
| 10821 | 10889 | }, |
| 10822 | .Struct, .Opaque, .Union => { | |
| 10890 | .Union => { | |
| 10823 | 10891 | 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; | |
| 10826 | 10923 | } |
| 10827 | 10924 | } |
| 10828 | 10925 | // TODO add note: declared here |
| ... | ... | @@ -10836,35 +10933,6 @@ fn fieldVal( |
| 10836 | 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 | 10936 | else => return sema.fail(block, src, "type '{}' has no members", .{child_type}), |
| 10869 | 10937 | } |
| 10870 | 10938 | }, |
| ... | ... | @@ -11244,6 +11312,17 @@ fn namespaceLookupRef( |
| 11244 | 11312 | return try sema.analyzeDeclRef(decl); |
| 11245 | 11313 | } |
| 11246 | 11314 | |
| 11315 | fn 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 | ||
| 11247 | 11326 | fn structFieldPtr( |
| 11248 | 11327 | sema: *Sema, |
| 11249 | 11328 | block: *Block, |
| ... | ... | @@ -11370,10 +11449,9 @@ fn unionFieldVal( |
| 11370 | 11449 | const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty); |
| 11371 | 11450 | const union_obj = union_ty.cast(Type.Payload.Union).?.data; |
| 11372 | 11451 | |
| 11373 | const field_index_big = union_obj.fields.getIndex(field_name) orelse | |
| 11452 | const field_index_usize = union_obj.fields.getIndex(field_name) orelse | |
| 11374 | 11453 | 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); | |
| 11377 | 11455 | const field = union_obj.fields.values()[field_index]; |
| 11378 | 11456 | |
| 11379 | 11457 | if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| { |
| ... | ... | @@ -12960,15 +13038,18 @@ fn wrapErrorUnion( |
| 12960 | 13038 | fn unionToTag( |
| 12961 | 13039 | sema: *Sema, |
| 12962 | 13040 | block: *Block, |
| 12963 | dest_ty: Type, | |
| 13041 | enum_ty: Type, | |
| 12964 | 13042 | un: Air.Inst.Ref, |
| 12965 | 13043 | un_src: LazySrcLoc, |
| 12966 | 13044 | ) !Air.Inst.Ref { |
| 13045 | if ((try sema.typeHasOnePossibleValue(block, un_src, enum_ty))) |opv| { | |
| 13046 | return sema.addConstant(enum_ty, opv); | |
| 13047 | } | |
| 12967 | 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 | 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 | } |
| 12973 | 13054 | |
| 12974 | 13055 | fn 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 | 72 | Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]), |
| 73 | 73 | i32 => @bitCast(i32, code.extra[i]), |
| 74 | 74 | Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]), |
| 75 | Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]), | |
| 75 | 76 | else => @compileError("bad field type"), |
| 76 | 77 | }; |
| 77 | 78 | i += 1; |
| ... | ... | @@ -618,39 +619,16 @@ pub const Inst = struct { |
| 618 | 619 | enum_literal, |
| 619 | 620 | /// A switch expression. Uses the `pl_node` union field. |
| 620 | 621 | /// AST node is the switch, payload is `SwitchBlock`. |
| 621 | /// All prongs of target handled. | |
| 622 | 622 | 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, | |
| 654 | 632 | /// Produces the capture value for a switch prong. |
| 655 | 633 | /// Uses the `switch_capture` field. |
| 656 | 634 | switch_capture, |
| ... | ... | @@ -1109,17 +1087,8 @@ pub const Inst = struct { |
| 1109 | 1087 | .switch_capture_else, |
| 1110 | 1088 | .switch_capture_else_ref, |
| 1111 | 1089 | .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, | |
| 1123 | 1092 | .validate_struct_init, |
| 1124 | 1093 | .validate_array_init, |
| 1125 | 1094 | .struct_init_empty, |
| ... | ... | @@ -1367,17 +1336,8 @@ pub const Inst = struct { |
| 1367 | 1336 | .ensure_err_payload_void = .un_tok, |
| 1368 | 1337 | .enum_literal = .str_tok, |
| 1369 | 1338 | .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, | |
| 1381 | 1341 | .switch_capture = .switch_capture, |
| 1382 | 1342 | .switch_capture_ref = .switch_capture, |
| 1383 | 1343 | .switch_capture_multi = .switch_capture, |
| ... | ... | @@ -2466,37 +2426,17 @@ pub const Inst = struct { |
| 2466 | 2426 | index: u32, |
| 2467 | 2427 | }; |
| 2468 | 2428 | |
| 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. | |
| 2491 | 2431 | /// body_len: u32, |
| 2492 | 2432 | /// body member Index for every body_len |
| 2493 | 2433 | /// } |
| 2494 | /// 1. scalar_cases: { // for every scalar_cases_len | |
| 2434 | /// 2. scalar_cases: { // for every scalar_cases_len | |
| 2495 | 2435 | /// item: Ref, |
| 2496 | 2436 | /// body_len: u32, |
| 2497 | 2437 | /// body member Index for every body_len |
| 2498 | 2438 | /// } |
| 2499 | /// 2. multi_cases: { // for every multi_cases_len | |
| 2439 | /// 3. multi_cases: { // for every multi_cases_len | |
| 2500 | 2440 | /// items_len: u32, |
| 2501 | 2441 | /// ranges_len: u32, |
| 2502 | 2442 | /// body_len: u32, |
| ... | ... | @@ -2507,10 +2447,78 @@ pub const Inst = struct { |
| 2507 | 2447 | /// } |
| 2508 | 2448 | /// body member Index for every body_len |
| 2509 | 2449 | /// } |
| 2510 | pub const SwitchBlockMulti = struct { | |
| 2450 | pub const SwitchBlock = struct { | |
| 2511 | 2451 | 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 | } | |
| 2514 | 2522 | }; |
| 2515 | 2523 | |
| 2516 | 2524 | pub const Field = struct { |
| ... | ... | @@ -2934,7 +2942,7 @@ pub const Inst = struct { |
| 2934 | 2942 | |
| 2935 | 2943 | /// Trailing: for each `imports_len` there is an Item |
| 2936 | 2944 | pub const Imports = struct { |
| 2937 | imports_len: Zir.Inst.Index, | |
| 2945 | imports_len: Inst.Index, | |
| 2938 | 2946 | |
| 2939 | 2947 | pub const Item = struct { |
| 2940 | 2948 | /// null terminated string index |
| ... | ... | @@ -3077,7 +3085,7 @@ pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclItera |
| 3077 | 3085 | |
| 3078 | 3086 | /// The iterator would have to allocate memory anyway to iterate. So here we populate |
| 3079 | 3087 | /// an ArrayList as the result. |
| 3080 | pub fn findDecls(zir: Zir, list: *std.ArrayList(Zir.Inst.Index), decl_sub_index: u32) !void { | |
| 3088 | pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_sub_index: u32) !void { | |
| 3081 | 3089 | const block_inst = zir.extra[decl_sub_index + 6]; |
| 3082 | 3090 | list.clearRetainingCapacity(); |
| 3083 | 3091 | |
| ... | ... | @@ -3086,8 +3094,8 @@ pub fn findDecls(zir: Zir, list: *std.ArrayList(Zir.Inst.Index), decl_sub_index: |
| 3086 | 3094 | |
| 3087 | 3095 | fn findDeclsInner( |
| 3088 | 3096 | zir: Zir, |
| 3089 | list: *std.ArrayList(Zir.Inst.Index), | |
| 3090 | inst: Zir.Inst.Index, | |
| 3097 | list: *std.ArrayList(Inst.Index), | |
| 3098 | inst: Inst.Index, | |
| 3091 | 3099 | ) Allocator.Error!void { |
| 3092 | 3100 | const tags = zir.instructions.items(.tag); |
| 3093 | 3101 | const datas = zir.instructions.items(.data); |
| ... | ... | @@ -3148,19 +3156,7 @@ fn findDeclsInner( |
| 3148 | 3156 | try zir.findDeclsBody(list, then_body); |
| 3149 | 3157 | try zir.findDeclsBody(list, else_body); |
| 3150 | 3158 | }, |
| 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), | |
| 3164 | 3160 | |
| 3165 | 3161 | .suspend_block => @panic("TODO iterate suspend block"), |
| 3166 | 3162 | |
| ... | ... | @@ -3170,71 +3166,34 @@ fn findDeclsInner( |
| 3170 | 3166 | |
| 3171 | 3167 | fn findDeclsSwitch( |
| 3172 | 3168 | 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, | |
| 3176 | 3171 | ) Allocator.Error!void { |
| 3177 | 3172 | const inst_data = zir.instructions.items(.data)[inst].pl_node; |
| 3178 | 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 | }; | |
| 3193 | 3174 | |
| 3194 | try zir.findDeclsBody(list, special.body); | |
| 3175 | var extra_index: usize = extra.end; | |
| 3195 | 3176 | |
| 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]; | |
| 3199 | 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 | 3185 | const body_len = zir.extra[extra_index]; |
| 3201 | 3186 | extra_index += 1; |
| 3202 | 3187 | const body = zir.extra[extra_index..][0..body_len]; |
| 3203 | extra_index += body_len; | |
| 3188 | extra_index += body.len; | |
| 3204 | 3189 | |
| 3205 | 3190 | try zir.findDeclsBody(list, body); |
| 3206 | 3191 | } |
| 3207 | } | |
| 3208 | 3192 | |
| 3209 | fn 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 | 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 | 3197 | extra_index += 1; |
| 3239 | 3198 | const body_len = zir.extra[extra_index]; |
| 3240 | 3199 | extra_index += 1; |
| ... | ... | @@ -3246,7 +3205,7 @@ fn findDeclsSwitchMulti( |
| 3246 | 3205 | } |
| 3247 | 3206 | { |
| 3248 | 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 | 3209 | const items_len = zir.extra[extra_index]; |
| 3251 | 3210 | extra_index += 1; |
| 3252 | 3211 | const ranges_len = zir.extra[extra_index]; |
| ... | ... | @@ -3353,3 +3312,18 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo { |
| 3353 | 3312 | .total_params_len = total_params_len, |
| 3354 | 3313 | }; |
| 3355 | 3314 | } |
| 3315 | ||
| 3316 | const ref_start_index: u32 = Inst.Ref.typed_value_map.len; | |
| 3317 | ||
| 3318 | pub fn indexToRef(inst: Inst.Index) Inst.Ref { | |
| 3319 | return @intToEnum(Inst.Ref, ref_start_index + inst); | |
| 3320 | } | |
| 3321 | ||
| 3322 | pub 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 | 234 | .@"await", |
| 235 | 235 | .await_nosuspend, |
| 236 | 236 | .fence, |
| 237 | .switch_cond, | |
| 238 | .switch_cond_ref, | |
| 237 | 239 | => try self.writeUnNode(stream, inst), |
| 238 | 240 | |
| 239 | 241 | .ref, |
| ... | ... | @@ -379,19 +381,7 @@ const Writer = struct { |
| 379 | 381 | .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon), |
| 380 | 382 | .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func), |
| 381 | 383 | |
| 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), | |
| 395 | 385 | |
| 396 | 386 | .field_ptr, |
| 397 | 387 | .field_val, |
| ... | ... | @@ -1649,113 +1639,46 @@ const Writer = struct { |
| 1649 | 1639 | try self.writeSrc(stream, inst_data.src()); |
| 1650 | 1640 | } |
| 1651 | 1641 | |
| 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 { | |
| 1658 | 1643 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 1659 | 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; | |
| 1711 | 1645 | |
| 1712 | try stream.writeAll(") "); | |
| 1713 | try self.writeSrc(stream, inst_data.src()); | |
| 1714 | } | |
| 1646 | var extra_index: usize = extra.end; | |
| 1715 | 1647 | |
| 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; | |
| 1738 | 1653 | |
| 1739 | 1654 | try self.writeInstRef(stream, extra.data.operand); |
| 1655 | try self.writeFlag(stream, ", ref", extra.data.bits.is_ref); | |
| 1740 | 1656 | |
| 1741 | 1657 | self.indent += 2; |
| 1742 | 1658 | |
| 1743 | if (special.body.len != 0) { | |
| 1659 | else_prong: { | |
| 1660 | const special_prong = extra.data.bits.specialProng(); | |
| 1744 | 1661 | const prong_name = switch (special_prong) { |
| 1745 | 1662 | .@"else" => "else", |
| 1746 | 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 | 1672 | try stream.writeAll(",\n"); |
| 1750 | 1673 | try stream.writeByteNTimes(' ', self.indent); |
| 1751 | 1674 | try stream.print("{s} => ", .{prong_name}); |
| 1752 | try self.writeBracedBody(stream, special.body); | |
| 1675 | try self.writeBracedBody(stream, body); | |
| 1753 | 1676 | } |
| 1754 | 1677 | |
| 1755 | var extra_index: usize = special.end; | |
| 1756 | 1678 | { |
| 1679 | const scalar_cases_len = extra.data.bits.scalar_cases_len; | |
| 1757 | 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 | 1682 | const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]); |
| 1760 | 1683 | extra_index += 1; |
| 1761 | 1684 | const body_len = self.code.extra[extra_index]; |
| ... | ... | @@ -1772,7 +1695,7 @@ const Writer = struct { |
| 1772 | 1695 | } |
| 1773 | 1696 | { |
| 1774 | 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 | 1699 | const items_len = self.code.extra[extra_index]; |
| 1777 | 1700 | extra_index += 1; |
| 1778 | 1701 | const ranges_len = self.code.extra[extra_index]; |
src/value.zig+1-1| ... | ... | @@ -116,7 +116,7 @@ pub const Value = extern union { |
| 116 | 116 | decl_ref_mut, |
| 117 | 117 | /// Pointer to a specific element of an array. |
| 118 | 118 | elem_ptr, |
| 119 | /// Pointer to a specific field of a struct. | |
| 119 | /// Pointer to a specific field of a struct or union. | |
| 120 | 120 | field_ptr, |
| 121 | 121 | /// A slice of u8 whose memory is managed externally. |
| 122 | 122 | bytes, |
test/behavior/union.zig+31| ... | ... | @@ -71,3 +71,34 @@ test "0-sized extern union definition" { |
| 71 | 71 | |
| 72 | 72 | try expect(U.f == 1); |
| 73 | 73 | } |
| 74 | ||
| 75 | const Value = union(enum) { | |
| 76 | Int: u64, | |
| 77 | Array: [9]u8, | |
| 78 | }; | |
| 79 | ||
| 80 | const Agg = struct { | |
| 81 | val1: Value, | |
| 82 | val2: Value, | |
| 83 | }; | |
| 84 | ||
| 85 | const v1 = Value{ .Int = 1234 }; | |
| 86 | const v2 = Value{ .Array = [_]u8{3} ** 9 }; | |
| 87 | ||
| 88 | const err = @as(anyerror!Agg, Agg{ | |
| 89 | .val1 = v1, | |
| 90 | .val2 = v2, | |
| 91 | }); | |
| 92 | ||
| 93 | const array = [_]Value{ v1, v2, v1, v2 }; | |
| 94 | ||
| 95 | test "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 | 3 | const expectEqual = std.testing.expectEqual; |
| 4 | 4 | const Tag = std.meta.Tag; |
| 5 | 5 | |
| 6 | const Value = union(enum) { | |
| 7 | Int: u64, | |
| 8 | Array: [9]u8, | |
| 9 | }; | |
| 10 | ||
| 11 | const Agg = struct { | |
| 12 | val1: Value, | |
| 13 | val2: Value, | |
| 14 | }; | |
| 15 | ||
| 16 | const v1 = Value{ .Int = 1234 }; | |
| 17 | const v2 = Value{ .Array = [_]u8{3} ** 9 }; | |
| 18 | ||
| 19 | const err = @as(anyerror!Agg, Agg{ | |
| 20 | .val1 = v1, | |
| 21 | .val2 = v2, | |
| 22 | }); | |
| 23 | ||
| 24 | const array = [_]Value{ v1, v2, v1, v2 }; | |
| 25 | ||
| 26 | test "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 | ||
| 37 | 6 | const Letter = enum { A, B, C }; |
| 38 | 7 | const Payload = union(Letter) { |
| 39 | 8 | A: i32, |
test/stage2/cbe.zig+1-1| ... | ... | @@ -852,7 +852,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 852 | 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 | 856 | ":1:11: note: enum declared here", |
| 857 | 857 | }); |
| 858 | 858 |