mirror of
https://github.com/ziglang/zig.git
synced 2026-01-24 00:05:28 +00:00
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.
This commit is contained in:
parent
4a76523b92
commit
dfb3231959
102
src/AstGen.zig
102
src/AstGen.zig
@ -11,6 +11,8 @@ const StringIndexAdapter = std.hash_map.StringIndexAdapter;
|
||||
const StringIndexContext = std.hash_map.StringIndexContext;
|
||||
|
||||
const Zir = @import("Zir.zig");
|
||||
const refToIndex = Zir.refToIndex;
|
||||
const indexToRef = Zir.indexToRef;
|
||||
const trace = @import("tracy.zig").trace;
|
||||
const BuiltinFn = @import("BuiltinFn.zig");
|
||||
|
||||
@ -57,6 +59,7 @@ fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
|
||||
Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
|
||||
i32 => @bitCast(u32, @field(extra, field.name)),
|
||||
Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),
|
||||
Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)),
|
||||
else => @compileError("bad field type"),
|
||||
});
|
||||
}
|
||||
@ -2133,17 +2136,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
|
||||
.slice_sentinel,
|
||||
.import,
|
||||
.switch_block,
|
||||
.switch_block_multi,
|
||||
.switch_block_else,
|
||||
.switch_block_else_multi,
|
||||
.switch_block_under,
|
||||
.switch_block_under_multi,
|
||||
.switch_block_ref,
|
||||
.switch_block_ref_multi,
|
||||
.switch_block_ref_else,
|
||||
.switch_block_ref_else_multi,
|
||||
.switch_block_ref_under,
|
||||
.switch_block_ref_under_multi,
|
||||
.switch_cond,
|
||||
.switch_cond_ref,
|
||||
.switch_capture,
|
||||
.switch_capture_ref,
|
||||
.switch_capture_multi,
|
||||
@ -5127,11 +5121,12 @@ fn fieldAccess(
|
||||
rl: ResultLoc,
|
||||
node: Ast.Node.Index,
|
||||
) InnerError!Zir.Inst.Ref {
|
||||
if (rl == .ref) {
|
||||
return addFieldAccess(.field_ptr, gz, scope, .ref, node);
|
||||
} else {
|
||||
const access = try addFieldAccess(.field_val, gz, scope, .none, node);
|
||||
return rvalue(gz, rl, access, node);
|
||||
switch (rl) {
|
||||
.ref => return addFieldAccess(.field_ptr, gz, scope, .ref, node),
|
||||
else => {
|
||||
const access = try addFieldAccess(.field_val, gz, scope, .none, node);
|
||||
return rvalue(gz, rl, access, node);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -6028,11 +6023,13 @@ fn switchExpr(
|
||||
}
|
||||
|
||||
const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
|
||||
const operand = try expr(parent_gz, scope, operand_rl, operand_node);
|
||||
const raw_operand = try expr(parent_gz, scope, operand_rl, operand_node);
|
||||
const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
|
||||
const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node);
|
||||
// We need the type of the operand to use as the result location for all the prong items.
|
||||
const typeof_tag: Zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof;
|
||||
const operand_ty_inst = try parent_gz.addUnNode(typeof_tag, operand, operand_node);
|
||||
const item_rl: ResultLoc = .{ .ty = operand_ty_inst };
|
||||
const cond_ty_inst = try parent_gz.addUnNode(typeof_tag, cond, operand_node);
|
||||
const item_rl: ResultLoc = .{ .ty = cond_ty_inst };
|
||||
|
||||
// These contain the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti.
|
||||
// This is the optional else prong body.
|
||||
@ -6050,7 +6047,7 @@ fn switchExpr(
|
||||
defer block_scope.instructions.deinit(gpa);
|
||||
|
||||
// This gets added to the parent block later, after the item expressions.
|
||||
const switch_block = try parent_gz.addBlock(undefined, switch_node);
|
||||
const switch_block = try parent_gz.addBlock(.switch_block, switch_node);
|
||||
|
||||
// We re-use this same scope for all cases, including the special prong, if any.
|
||||
var case_scope = parent_gz.makeSubBlock(&block_scope.base);
|
||||
@ -6203,44 +6200,32 @@ fn switchExpr(
|
||||
// Now that the item expressions are generated we can add this.
|
||||
try parent_gz.instructions.append(gpa, switch_block);
|
||||
|
||||
const ref_bit: u4 = @boolToInt(any_payload_is_ref);
|
||||
const multi_bit: u4 = @boolToInt(multi_cases_len != 0);
|
||||
const special_prong_bits: u4 = @enumToInt(special_prong);
|
||||
comptime {
|
||||
assert(@enumToInt(Zir.SpecialProng.none) == 0b00);
|
||||
assert(@enumToInt(Zir.SpecialProng.@"else") == 0b01);
|
||||
assert(@enumToInt(Zir.SpecialProng.under) == 0b10);
|
||||
}
|
||||
const zir_tags = astgen.instructions.items(.tag);
|
||||
zir_tags[switch_block] = switch ((ref_bit << 3) | (special_prong_bits << 1) | multi_bit) {
|
||||
0b0_00_0 => .switch_block,
|
||||
0b0_00_1 => .switch_block_multi,
|
||||
0b0_01_0 => .switch_block_else,
|
||||
0b0_01_1 => .switch_block_else_multi,
|
||||
0b0_10_0 => .switch_block_under,
|
||||
0b0_10_1 => .switch_block_under_multi,
|
||||
0b1_00_0 => .switch_block_ref,
|
||||
0b1_00_1 => .switch_block_ref_multi,
|
||||
0b1_01_0 => .switch_block_ref_else,
|
||||
0b1_01_1 => .switch_block_ref_else_multi,
|
||||
0b1_10_0 => .switch_block_ref_under,
|
||||
0b1_10_1 => .switch_block_ref_under_multi,
|
||||
else => unreachable,
|
||||
};
|
||||
const payload_index = astgen.extra.items.len;
|
||||
const zir_datas = astgen.instructions.items(.data);
|
||||
zir_datas[switch_block].pl_node.payload_index = @intCast(u32, payload_index);
|
||||
// Documentation for this: `Zir.Inst.SwitchBlock` and `Zir.Inst.SwitchBlockMulti`.
|
||||
try astgen.extra.ensureUnusedCapacity(gpa, @as(usize, 2) + // operand, scalar_cases_len
|
||||
try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
|
||||
@boolToInt(multi_cases_len != 0) +
|
||||
special_case_payload.items.len +
|
||||
scalar_cases_payload.items.len +
|
||||
multi_cases_payload.items.len);
|
||||
astgen.extra.appendAssumeCapacity(@enumToInt(operand));
|
||||
astgen.extra.appendAssumeCapacity(scalar_cases_len);
|
||||
|
||||
const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
|
||||
.operand = cond,
|
||||
.bits = Zir.Inst.SwitchBlock.Bits{
|
||||
.is_ref = any_payload_is_ref,
|
||||
.has_multi_cases = multi_cases_len != 0,
|
||||
.has_else = special_prong == .@"else",
|
||||
.has_under = special_prong == .under,
|
||||
.scalar_cases_len = @intCast(u28, scalar_cases_len),
|
||||
},
|
||||
});
|
||||
|
||||
const zir_datas = astgen.instructions.items(.data);
|
||||
const zir_tags = astgen.instructions.items(.tag);
|
||||
|
||||
zir_datas[switch_block].pl_node.payload_index = payload_index;
|
||||
|
||||
if (multi_cases_len != 0) {
|
||||
astgen.extra.appendAssumeCapacity(multi_cases_len);
|
||||
}
|
||||
|
||||
const strat = rl.strategy(&block_scope);
|
||||
switch (strat.tag) {
|
||||
.break_operand => {
|
||||
@ -10622,21 +10607,6 @@ fn advanceSourceCursor(astgen: *AstGen, source: []const u8, end: usize) void {
|
||||
astgen.source_column = column;
|
||||
}
|
||||
|
||||
const ref_start_index: u32 = Zir.Inst.Ref.typed_value_map.len;
|
||||
|
||||
fn indexToRef(inst: Zir.Inst.Index) Zir.Inst.Ref {
|
||||
return @intToEnum(Zir.Inst.Ref, ref_start_index + inst);
|
||||
}
|
||||
|
||||
fn refToIndex(inst: Zir.Inst.Ref) ?Zir.Inst.Index {
|
||||
const ref_int = @enumToInt(inst);
|
||||
if (ref_int >= ref_start_index) {
|
||||
return ref_int - ref_start_index;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !void {
|
||||
const gpa = astgen.gpa;
|
||||
const tree = astgen.tree;
|
||||
|
||||
339
src/Sema.zig
339
src/Sema.zig
@ -550,18 +550,9 @@ pub fn analyzeBody(
|
||||
.slice_sentinel => try sema.zirSliceSentinel(block, inst),
|
||||
.slice_start => try sema.zirSliceStart(block, inst),
|
||||
.str => try sema.zirStr(block, inst),
|
||||
.switch_block => try sema.zirSwitchBlock(block, inst, false, .none),
|
||||
.switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),
|
||||
.switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),
|
||||
.switch_block_else_multi => try sema.zirSwitchBlockMulti(block, inst, false, .@"else"),
|
||||
.switch_block_under => try sema.zirSwitchBlock(block, inst, false, .under),
|
||||
.switch_block_under_multi => try sema.zirSwitchBlockMulti(block, inst, false, .under),
|
||||
.switch_block_ref => try sema.zirSwitchBlock(block, inst, true, .none),
|
||||
.switch_block_ref_multi => try sema.zirSwitchBlockMulti(block, inst, true, .none),
|
||||
.switch_block_ref_else => try sema.zirSwitchBlock(block, inst, true, .@"else"),
|
||||
.switch_block_ref_else_multi => try sema.zirSwitchBlockMulti(block, inst, true, .@"else"),
|
||||
.switch_block_ref_under => try sema.zirSwitchBlock(block, inst, true, .under),
|
||||
.switch_block_ref_under_multi => try sema.zirSwitchBlockMulti(block, inst, true, .under),
|
||||
.switch_block => try sema.zirSwitchBlock(block, inst),
|
||||
.switch_cond => try sema.zirSwitchCond(block, inst, false),
|
||||
.switch_cond_ref => try sema.zirSwitchCond(block, inst, true),
|
||||
.switch_capture => try sema.zirSwitchCapture(block, inst, false, false),
|
||||
.switch_capture_ref => try sema.zirSwitchCapture(block, inst, false, true),
|
||||
.switch_capture_multi => try sema.zirSwitchCapture(block, inst, true, false),
|
||||
@ -5433,11 +5424,80 @@ fn zirSwitchCapture(
|
||||
const zir_datas = sema.code.instructions.items(.data);
|
||||
const capture_info = zir_datas[inst].switch_capture;
|
||||
const switch_info = zir_datas[capture_info.switch_inst].pl_node;
|
||||
const src = switch_info.src();
|
||||
const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index);
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_info.src_node };
|
||||
const switch_src = switch_info.src();
|
||||
const operand_is_ref = switch_extra.data.bits.is_ref;
|
||||
const cond_inst = Zir.refToIndex(switch_extra.data.operand).?;
|
||||
const cond_info = sema.code.instructions.items(.data)[cond_inst].un_node;
|
||||
const operand_ptr = sema.resolveInst(cond_info.operand);
|
||||
const operand_ptr_ty = sema.typeOf(operand_ptr);
|
||||
const operand_ty = if (operand_is_ref) operand_ptr_ty.childType() else operand_ptr_ty;
|
||||
|
||||
_ = is_ref;
|
||||
_ = is_multi;
|
||||
return sema.fail(block, src, "TODO implement Sema for zirSwitchCapture", .{});
|
||||
if (is_multi) {
|
||||
return sema.fail(block, switch_src, "TODO implement Sema for switch capture multi", .{});
|
||||
}
|
||||
const scalar_prong = switch_extra.data.getScalarProng(sema.code, switch_extra.end, capture_info.prong_index);
|
||||
const item = sema.resolveInst(scalar_prong.item);
|
||||
// Previous switch validation ensured this will succeed
|
||||
const item_val = sema.resolveConstValue(block, .unneeded, item) catch unreachable;
|
||||
|
||||
switch (operand_ty.zigTypeTag()) {
|
||||
.Union => {
|
||||
const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
|
||||
const enum_ty = union_obj.tag_ty;
|
||||
|
||||
const field_index_usize = enum_ty.enumTagFieldIndex(item_val).?;
|
||||
const field_index = @intCast(u32, field_index_usize);
|
||||
const field = union_obj.fields.values()[field_index];
|
||||
|
||||
// TODO handle multiple union tags which have compatible types
|
||||
|
||||
if (is_ref) {
|
||||
assert(operand_is_ref);
|
||||
|
||||
const field_ty_ptr = try Type.ptr(sema.arena, .{
|
||||
.pointee_type = field.ty,
|
||||
.@"addrspace" = .generic,
|
||||
.mutable = operand_ptr_ty.ptrIsMutable(),
|
||||
});
|
||||
|
||||
if (try sema.resolveDefinedValue(block, operand_src, operand_ptr)) |op_ptr_val| {
|
||||
return sema.addConstant(
|
||||
field_ty_ptr,
|
||||
try Value.Tag.field_ptr.create(sema.arena, .{
|
||||
.container_ptr = op_ptr_val,
|
||||
.field_index = field_index,
|
||||
}),
|
||||
);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, operand_src);
|
||||
return block.addStructFieldPtr(operand_ptr, field_index, field.ty);
|
||||
}
|
||||
|
||||
const operand = if (operand_is_ref)
|
||||
try sema.analyzeLoad(block, operand_src, operand_ptr, operand_src)
|
||||
else
|
||||
operand_ptr;
|
||||
|
||||
if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| {
|
||||
return sema.addConstant(
|
||||
field.ty,
|
||||
operand_val.castTag(.@"union").?.data.val,
|
||||
);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, operand_src);
|
||||
return block.addStructFieldVal(operand, field_index, field.ty);
|
||||
},
|
||||
.ErrorSet => {
|
||||
return sema.fail(block, operand_src, "TODO implement Sema for zirSwitchCapture for error sets", .{});
|
||||
},
|
||||
else => {
|
||||
return sema.fail(block, operand_src, "switch on type '{}' provides no capture value", .{
|
||||
operand_ty,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn zirSwitchCaptureElse(
|
||||
@ -5452,96 +5512,108 @@ fn zirSwitchCaptureElse(
|
||||
const zir_datas = sema.code.instructions.items(.data);
|
||||
const capture_info = zir_datas[inst].switch_capture;
|
||||
const switch_info = zir_datas[capture_info.switch_inst].pl_node;
|
||||
const switch_extra = sema.code.extraData(Zir.Inst.SwitchBlock, switch_info.payload_index).data;
|
||||
const src = switch_info.src();
|
||||
const operand_is_ref = switch_extra.bits.is_ref;
|
||||
assert(!is_ref or operand_is_ref);
|
||||
|
||||
_ = is_ref;
|
||||
return sema.fail(block, src, "TODO implement Sema for zirSwitchCaptureElse", .{});
|
||||
}
|
||||
|
||||
fn zirSwitchBlock(
|
||||
fn zirSwitchCond(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
inst: Zir.Inst.Index,
|
||||
is_ref: bool,
|
||||
special_prong: Zir.SpecialProng,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src = inst_data.src();
|
||||
const operand_ptr = sema.resolveInst(inst_data.operand);
|
||||
const operand = if (is_ref) try sema.analyzeLoad(block, src, operand_ptr, src) else operand_ptr;
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
|
||||
switch (operand_ty.zigTypeTag()) {
|
||||
.Type,
|
||||
.Void,
|
||||
.Bool,
|
||||
.Int,
|
||||
.Float,
|
||||
.ComptimeFloat,
|
||||
.ComptimeInt,
|
||||
.EnumLiteral,
|
||||
.Pointer,
|
||||
.Fn,
|
||||
.ErrorSet,
|
||||
.Enum,
|
||||
=> {
|
||||
if ((try sema.typeHasOnePossibleValue(block, src, operand_ty))) |opv| {
|
||||
return sema.addConstant(operand_ty, opv);
|
||||
}
|
||||
return operand;
|
||||
},
|
||||
|
||||
.Union => {
|
||||
const enum_ty = operand_ty.unionTagType() orelse {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "switch on untagged union", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.addDeclaredHereNote(msg, operand_ty);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
};
|
||||
return sema.unionToTag(block, enum_ty, operand, src);
|
||||
},
|
||||
|
||||
.ErrorUnion,
|
||||
.NoReturn,
|
||||
.Array,
|
||||
.Struct,
|
||||
.Undefined,
|
||||
.Null,
|
||||
.Optional,
|
||||
.BoundFn,
|
||||
.Opaque,
|
||||
.Vector,
|
||||
.Frame,
|
||||
.AnyFrame,
|
||||
=> return sema.fail(block, src, "switch on type '{}'", .{operand_ty}),
|
||||
}
|
||||
}
|
||||
|
||||
fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const gpa = sema.gpa;
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
|
||||
const src_node_offset = inst_data.src_node;
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
|
||||
const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
|
||||
const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
|
||||
|
||||
const operand_ptr = sema.resolveInst(extra.data.operand);
|
||||
const operand = if (is_ref)
|
||||
const operand = if (extra.data.bits.is_ref)
|
||||
try sema.analyzeLoad(block, src, operand_ptr, operand_src)
|
||||
else
|
||||
operand_ptr;
|
||||
|
||||
return sema.analyzeSwitch(
|
||||
block,
|
||||
operand,
|
||||
extra.end,
|
||||
special_prong,
|
||||
extra.data.cases_len,
|
||||
0,
|
||||
inst,
|
||||
inst_data.src_node,
|
||||
);
|
||||
}
|
||||
var header_extra_index: usize = extra.end;
|
||||
|
||||
fn zirSwitchBlockMulti(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
inst: Zir.Inst.Index,
|
||||
is_ref: bool,
|
||||
special_prong: Zir.SpecialProng,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.SwitchBlockMulti, inst_data.payload_index);
|
||||
|
||||
const operand_ptr = sema.resolveInst(extra.data.operand);
|
||||
const operand = if (is_ref)
|
||||
try sema.analyzeLoad(block, src, operand_ptr, operand_src)
|
||||
else
|
||||
operand_ptr;
|
||||
|
||||
return sema.analyzeSwitch(
|
||||
block,
|
||||
operand,
|
||||
extra.end,
|
||||
special_prong,
|
||||
extra.data.scalar_cases_len,
|
||||
extra.data.multi_cases_len,
|
||||
inst,
|
||||
inst_data.src_node,
|
||||
);
|
||||
}
|
||||
|
||||
fn analyzeSwitch(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
operand: Air.Inst.Ref,
|
||||
extra_end: usize,
|
||||
special_prong: Zir.SpecialProng,
|
||||
scalar_cases_len: usize,
|
||||
multi_cases_len: usize,
|
||||
switch_inst: Zir.Inst.Index,
|
||||
src_node_offset: i32,
|
||||
) CompileError!Air.Inst.Ref {
|
||||
const gpa = sema.gpa;
|
||||
const scalar_cases_len = extra.data.bits.scalar_cases_len;
|
||||
const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
|
||||
const multi_cases_len = sema.code.extra[header_extra_index];
|
||||
header_extra_index += 1;
|
||||
break :blk multi_cases_len;
|
||||
} else 0;
|
||||
|
||||
const special_prong = extra.data.bits.specialProng();
|
||||
const special: struct { body: []const Zir.Inst.Index, end: usize } = switch (special_prong) {
|
||||
.none => .{ .body = &.{}, .end = extra_end },
|
||||
.none => .{ .body = &.{}, .end = header_extra_index },
|
||||
.under, .@"else" => blk: {
|
||||
const body_len = sema.code.extra[extra_end];
|
||||
const extra_body_start = extra_end + 1;
|
||||
const body_len = sema.code.extra[header_extra_index];
|
||||
const extra_body_start = header_extra_index + 1;
|
||||
break :blk .{
|
||||
.body = sema.code.extra[extra_body_start..][0..body_len],
|
||||
.end = extra_body_start + body_len,
|
||||
@ -5549,9 +5621,6 @@ fn analyzeSwitch(
|
||||
},
|
||||
};
|
||||
|
||||
const src: LazySrcLoc = .{ .node_offset = src_node_offset };
|
||||
const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset };
|
||||
const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
|
||||
const operand_ty = sema.typeOf(operand);
|
||||
|
||||
// Validate usage of '_' prongs.
|
||||
@ -5945,7 +6014,7 @@ fn analyzeSwitch(
|
||||
.data = undefined,
|
||||
});
|
||||
var label: Block.Label = .{
|
||||
.zir_block = switch_inst,
|
||||
.zir_block = inst,
|
||||
.merges = .{
|
||||
.results = .{},
|
||||
.br_list = .{},
|
||||
@ -8934,8 +9003,9 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
|
||||
const field_src: LazySrcLoc = .{ .node_offset_back2tok = field_type_data.src_node };
|
||||
const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
|
||||
const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
|
||||
const field_index = union_obj.fields.getIndex(field_name) orelse
|
||||
const field_index_usize = union_obj.fields.getIndex(field_name) orelse
|
||||
return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name);
|
||||
const field_index = @intCast(u32, field_index_usize);
|
||||
|
||||
if (is_ref) {
|
||||
return sema.fail(block, src, "TODO: Sema.zirStructInit is_ref=true union", .{});
|
||||
@ -8943,12 +9013,10 @@ fn zirStructInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool)
|
||||
|
||||
const init_inst = sema.resolveInst(item.data.init);
|
||||
if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| {
|
||||
const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
|
||||
return sema.addConstant(
|
||||
resolved_ty,
|
||||
try Value.Tag.@"union".create(sema.arena, .{
|
||||
.tag = try Value.Tag.int_u64.create(sema.arena, field_index),
|
||||
.val = val,
|
||||
}),
|
||||
try Value.Tag.@"union".create(sema.arena, .{ .tag = tag_val, .val = val }),
|
||||
);
|
||||
}
|
||||
return sema.fail(block, src, "TODO: Sema.zirStructInit for runtime-known union values", .{});
|
||||
@ -9152,8 +9220,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
|
||||
const val = try sema.resolveConstValue(block, operand_src, type_info);
|
||||
const union_val = val.cast(Value.Payload.Union).?.data;
|
||||
const TypeInfoTag = std.meta.Tag(std.builtin.TypeInfo);
|
||||
const tag_index = @intCast(std.meta.Tag(TypeInfoTag), union_val.tag.toUnsignedInt());
|
||||
const tag_ty = type_info_ty.unionTagType().?;
|
||||
const tag_index = tag_ty.enumTagFieldIndex(union_val.tag).?;
|
||||
switch (@intToEnum(std.builtin.TypeId, tag_index)) {
|
||||
.Type => return Air.Inst.Ref.type_type,
|
||||
.Void => return Air.Inst.Ref.void_type,
|
||||
@ -10819,10 +10887,39 @@ fn fieldVal(
|
||||
try Value.Tag.@"error".create(arena, .{ .name = name }),
|
||||
);
|
||||
},
|
||||
.Struct, .Opaque, .Union => {
|
||||
.Union => {
|
||||
if (child_type.getNamespace()) |namespace| {
|
||||
if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
|
||||
return sema.analyzeLoad(block, src, inst, src);
|
||||
if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
if (child_type.unionTagType()) |enum_ty| {
|
||||
if (enum_ty.enumFieldIndex(field_name)) |field_index_usize| {
|
||||
const field_index = @intCast(u32, field_index_usize);
|
||||
return sema.addConstant(
|
||||
enum_ty,
|
||||
try Value.Tag.enum_field_index.create(sema.arena, field_index),
|
||||
);
|
||||
}
|
||||
}
|
||||
return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
|
||||
},
|
||||
.Enum => {
|
||||
if (child_type.getNamespace()) |namespace| {
|
||||
if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
const field_index_usize = child_type.enumFieldIndex(field_name) orelse
|
||||
return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
|
||||
const field_index = @intCast(u32, field_index_usize);
|
||||
const enum_val = try Value.Tag.enum_field_index.create(arena, field_index);
|
||||
return sema.addConstant(try child_type.copy(arena), enum_val);
|
||||
},
|
||||
.Struct, .Opaque => {
|
||||
if (child_type.getNamespace()) |namespace| {
|
||||
if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
// TODO add note: declared here
|
||||
@ -10836,35 +10933,6 @@ fn fieldVal(
|
||||
kw_name, child_type, field_name,
|
||||
});
|
||||
},
|
||||
.Enum => {
|
||||
if (child_type.getNamespace()) |namespace| {
|
||||
if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
|
||||
return sema.analyzeLoad(block, src, inst, src);
|
||||
}
|
||||
}
|
||||
const field_index = child_type.enumFieldIndex(field_name) orelse {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(
|
||||
block,
|
||||
src,
|
||||
"enum '{}' has no member named '{s}'",
|
||||
.{ child_type, field_name },
|
||||
);
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.mod.errNoteNonLazy(
|
||||
child_type.declSrcLoc(),
|
||||
msg,
|
||||
"enum declared here",
|
||||
.{},
|
||||
);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(msg);
|
||||
};
|
||||
const field_index_u32 = @intCast(u32, field_index);
|
||||
const enum_val = try Value.Tag.enum_field_index.create(arena, field_index_u32);
|
||||
return sema.addConstant(try child_type.copy(arena), enum_val);
|
||||
},
|
||||
else => return sema.fail(block, src, "type '{}' has no members", .{child_type}),
|
||||
}
|
||||
},
|
||||
@ -11244,6 +11312,17 @@ fn namespaceLookupRef(
|
||||
return try sema.analyzeDeclRef(decl);
|
||||
}
|
||||
|
||||
fn namespaceLookupVal(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
namespace: *Namespace,
|
||||
decl_name: []const u8,
|
||||
) CompileError!?Air.Inst.Ref {
|
||||
const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
|
||||
return try sema.analyzeDeclVal(block, src, decl);
|
||||
}
|
||||
|
||||
fn structFieldPtr(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@ -11370,10 +11449,9 @@ fn unionFieldVal(
|
||||
const union_ty = try sema.resolveTypeFields(block, src, unresolved_union_ty);
|
||||
const union_obj = union_ty.cast(Type.Payload.Union).?.data;
|
||||
|
||||
const field_index_big = union_obj.fields.getIndex(field_name) orelse
|
||||
const field_index_usize = union_obj.fields.getIndex(field_name) orelse
|
||||
return sema.failWithBadUnionFieldAccess(block, union_obj, field_name_src, field_name);
|
||||
const field_index = @intCast(u32, field_index_big);
|
||||
|
||||
const field_index = @intCast(u32, field_index_usize);
|
||||
const field = union_obj.fields.values()[field_index];
|
||||
|
||||
if (try sema.resolveMaybeUndefVal(block, src, union_byval)) |union_val| {
|
||||
@ -12960,15 +13038,18 @@ fn wrapErrorUnion(
|
||||
fn unionToTag(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
dest_ty: Type,
|
||||
enum_ty: Type,
|
||||
un: Air.Inst.Ref,
|
||||
un_src: LazySrcLoc,
|
||||
) !Air.Inst.Ref {
|
||||
if ((try sema.typeHasOnePossibleValue(block, un_src, enum_ty))) |opv| {
|
||||
return sema.addConstant(enum_ty, opv);
|
||||
}
|
||||
if (try sema.resolveMaybeUndefVal(block, un_src, un)) |un_val| {
|
||||
return sema.addConstant(dest_ty, un_val.unionTag());
|
||||
return sema.addConstant(enum_ty, un_val.unionTag());
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, un_src);
|
||||
return block.addTyOp(.get_union_tag, dest_ty, un);
|
||||
return block.addTyOp(.get_union_tag, enum_ty, un);
|
||||
}
|
||||
|
||||
fn resolvePeerTypes(
|
||||
|
||||
272
src/Zir.zig
272
src/Zir.zig
@ -72,6 +72,7 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) struct { data: T, en
|
||||
Inst.Ref => @intToEnum(Inst.Ref, code.extra[i]),
|
||||
i32 => @bitCast(i32, code.extra[i]),
|
||||
Inst.Call.Flags => @bitCast(Inst.Call.Flags, code.extra[i]),
|
||||
Inst.SwitchBlock.Bits => @bitCast(Inst.SwitchBlock.Bits, code.extra[i]),
|
||||
else => @compileError("bad field type"),
|
||||
};
|
||||
i += 1;
|
||||
@ -618,39 +619,16 @@ pub const Inst = struct {
|
||||
enum_literal,
|
||||
/// A switch expression. Uses the `pl_node` union field.
|
||||
/// AST node is the switch, payload is `SwitchBlock`.
|
||||
/// All prongs of target handled.
|
||||
switch_block,
|
||||
/// Same as switch_block, except one or more prongs have multiple items.
|
||||
/// Payload is `SwitchBlockMulti`
|
||||
switch_block_multi,
|
||||
/// Same as switch_block, except has an else prong.
|
||||
switch_block_else,
|
||||
/// Same as switch_block_else, except one or more prongs have multiple items.
|
||||
/// Payload is `SwitchBlockMulti`
|
||||
switch_block_else_multi,
|
||||
/// Same as switch_block, except has an underscore prong.
|
||||
switch_block_under,
|
||||
/// Same as switch_block, except one or more prongs have multiple items.
|
||||
/// Payload is `SwitchBlockMulti`
|
||||
switch_block_under_multi,
|
||||
/// Same as `switch_block` but the target is a pointer to the value being switched on.
|
||||
switch_block_ref,
|
||||
/// Same as `switch_block_multi` but the target is a pointer to the value being switched on.
|
||||
/// Payload is `SwitchBlockMulti`
|
||||
switch_block_ref_multi,
|
||||
/// Same as `switch_block_else` but the target is a pointer to the value being switched on.
|
||||
switch_block_ref_else,
|
||||
/// Same as `switch_block_else_multi` but the target is a pointer to the
|
||||
/// value being switched on.
|
||||
/// Payload is `SwitchBlockMulti`
|
||||
switch_block_ref_else_multi,
|
||||
/// Same as `switch_block_under` but the target is a pointer to the value
|
||||
/// being switched on.
|
||||
switch_block_ref_under,
|
||||
/// Same as `switch_block_under_multi` but the target is a pointer to
|
||||
/// the value being switched on.
|
||||
/// Payload is `SwitchBlockMulti`
|
||||
switch_block_ref_under_multi,
|
||||
/// Produces the value that will be switched on. For example, for
|
||||
/// integers, it returns the integer with no modifications. For tagged unions, it
|
||||
/// returns the active enum tag.
|
||||
/// Uses the `un_node` union field.
|
||||
switch_cond,
|
||||
/// Same as `switch_cond`, except the input operand is a pointer to
|
||||
/// what will be switched on.
|
||||
/// Uses the `un_node` union field.
|
||||
switch_cond_ref,
|
||||
/// Produces the capture value for a switch prong.
|
||||
/// Uses the `switch_capture` field.
|
||||
switch_capture,
|
||||
@ -1109,17 +1087,8 @@ pub const Inst = struct {
|
||||
.switch_capture_else,
|
||||
.switch_capture_else_ref,
|
||||
.switch_block,
|
||||
.switch_block_multi,
|
||||
.switch_block_else,
|
||||
.switch_block_else_multi,
|
||||
.switch_block_under,
|
||||
.switch_block_under_multi,
|
||||
.switch_block_ref,
|
||||
.switch_block_ref_multi,
|
||||
.switch_block_ref_else,
|
||||
.switch_block_ref_else_multi,
|
||||
.switch_block_ref_under,
|
||||
.switch_block_ref_under_multi,
|
||||
.switch_cond,
|
||||
.switch_cond_ref,
|
||||
.validate_struct_init,
|
||||
.validate_array_init,
|
||||
.struct_init_empty,
|
||||
@ -1367,17 +1336,8 @@ pub const Inst = struct {
|
||||
.ensure_err_payload_void = .un_tok,
|
||||
.enum_literal = .str_tok,
|
||||
.switch_block = .pl_node,
|
||||
.switch_block_multi = .pl_node,
|
||||
.switch_block_else = .pl_node,
|
||||
.switch_block_else_multi = .pl_node,
|
||||
.switch_block_under = .pl_node,
|
||||
.switch_block_under_multi = .pl_node,
|
||||
.switch_block_ref = .pl_node,
|
||||
.switch_block_ref_multi = .pl_node,
|
||||
.switch_block_ref_else = .pl_node,
|
||||
.switch_block_ref_else_multi = .pl_node,
|
||||
.switch_block_ref_under = .pl_node,
|
||||
.switch_block_ref_under_multi = .pl_node,
|
||||
.switch_cond = .un_node,
|
||||
.switch_cond_ref = .un_node,
|
||||
.switch_capture = .switch_capture,
|
||||
.switch_capture_ref = .switch_capture,
|
||||
.switch_capture_multi = .switch_capture,
|
||||
@ -2466,37 +2426,17 @@ pub const Inst = struct {
|
||||
index: u32,
|
||||
};
|
||||
|
||||
/// This form is supported when there are no ranges, and exactly 1 item per block.
|
||||
/// Depending on zir tag and len fields, extra fields trail
|
||||
/// this one in the extra array.
|
||||
/// 0. else_body { // If the tag has "_else" or "_under" in it.
|
||||
/// 0. multi_cases_len: u32 // If has_multi_cases is set.
|
||||
/// 1. else_body { // If has_else or has_under is set.
|
||||
/// body_len: u32,
|
||||
/// body member Index for every body_len
|
||||
/// }
|
||||
/// 1. cases: {
|
||||
/// item: Ref,
|
||||
/// body_len: u32,
|
||||
/// body member Index for every body_len
|
||||
/// } for every cases_len
|
||||
pub const SwitchBlock = struct {
|
||||
operand: Ref,
|
||||
cases_len: u32,
|
||||
};
|
||||
|
||||
/// This form is required when there exists a block which has more than one item,
|
||||
/// or a range.
|
||||
/// Depending on zir tag and len fields, extra fields trail
|
||||
/// this one in the extra array.
|
||||
/// 0. else_body { // If the tag has "_else" or "_under" in it.
|
||||
/// body_len: u32,
|
||||
/// body member Index for every body_len
|
||||
/// }
|
||||
/// 1. scalar_cases: { // for every scalar_cases_len
|
||||
/// 2. scalar_cases: { // for every scalar_cases_len
|
||||
/// item: Ref,
|
||||
/// body_len: u32,
|
||||
/// body member Index for every body_len
|
||||
/// }
|
||||
/// 2. multi_cases: { // for every multi_cases_len
|
||||
/// 3. multi_cases: { // for every multi_cases_len
|
||||
/// items_len: u32,
|
||||
/// ranges_len: u32,
|
||||
/// body_len: u32,
|
||||
@ -2507,10 +2447,78 @@ pub const Inst = struct {
|
||||
/// }
|
||||
/// body member Index for every body_len
|
||||
/// }
|
||||
pub const SwitchBlockMulti = struct {
|
||||
pub const SwitchBlock = struct {
|
||||
operand: Ref,
|
||||
scalar_cases_len: u32,
|
||||
multi_cases_len: u32,
|
||||
bits: Bits,
|
||||
|
||||
pub const Bits = packed struct {
|
||||
/// If true, one or more prongs have multiple items.
|
||||
has_multi_cases: bool,
|
||||
/// If true, there is an else prong. This is mutually exclusive with `has_under`.
|
||||
has_else: bool,
|
||||
/// If true, there is an underscore prong. This is mutually exclusive with `has_else`.
|
||||
has_under: bool,
|
||||
/// If true, the `operand` is a pointer to the value being switched on.
|
||||
is_ref: bool,
|
||||
scalar_cases_len: u28,
|
||||
|
||||
pub fn specialProng(bits: Bits) SpecialProng {
|
||||
const has_else: u2 = @boolToInt(bits.has_else);
|
||||
const has_under: u2 = @boolToInt(bits.has_under);
|
||||
return switch ((has_else << 1) | has_under) {
|
||||
0b00 => .none,
|
||||
0b01 => .under,
|
||||
0b10 => .@"else",
|
||||
0b11 => unreachable,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const ScalarProng = struct {
|
||||
item: Ref,
|
||||
body: []const Index,
|
||||
};
|
||||
|
||||
/// TODO performance optimization: instead of having this helper method
|
||||
/// change the definition of switch_capture instruction to store extra_index
|
||||
/// instead of prong_index. This way, Sema won't be doing O(N^2) iterations
|
||||
/// over the switch prongs.
|
||||
pub fn getScalarProng(
|
||||
self: SwitchBlock,
|
||||
zir: Zir,
|
||||
extra_end: usize,
|
||||
prong_index: usize,
|
||||
) ScalarProng {
|
||||
var extra_index: usize = extra_end;
|
||||
|
||||
if (self.bits.has_multi_cases) {
|
||||
extra_index += 1;
|
||||
}
|
||||
|
||||
if (self.bits.specialProng() != .none) {
|
||||
const body_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const body = zir.extra[extra_index..][0..body_len];
|
||||
extra_index += body.len;
|
||||
}
|
||||
|
||||
var scalar_i: usize = 0;
|
||||
while (true) : (scalar_i += 1) {
|
||||
const item = @intToEnum(Ref, zir.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const body = zir.extra[extra_index..][0..body_len];
|
||||
extra_index += body.len;
|
||||
|
||||
if (scalar_i < prong_index) continue;
|
||||
|
||||
return .{
|
||||
.item = item,
|
||||
.body = body,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Field = struct {
|
||||
@ -2934,7 +2942,7 @@ pub const Inst = struct {
|
||||
|
||||
/// Trailing: for each `imports_len` there is an Item
|
||||
pub const Imports = struct {
|
||||
imports_len: Zir.Inst.Index,
|
||||
imports_len: Inst.Index,
|
||||
|
||||
pub const Item = struct {
|
||||
/// null terminated string index
|
||||
@ -3077,7 +3085,7 @@ pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclItera
|
||||
|
||||
/// The iterator would have to allocate memory anyway to iterate. So here we populate
|
||||
/// an ArrayList as the result.
|
||||
pub fn findDecls(zir: Zir, list: *std.ArrayList(Zir.Inst.Index), decl_sub_index: u32) !void {
|
||||
pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_sub_index: u32) !void {
|
||||
const block_inst = zir.extra[decl_sub_index + 6];
|
||||
list.clearRetainingCapacity();
|
||||
|
||||
@ -3086,8 +3094,8 @@ pub fn findDecls(zir: Zir, list: *std.ArrayList(Zir.Inst.Index), decl_sub_index:
|
||||
|
||||
fn findDeclsInner(
|
||||
zir: Zir,
|
||||
list: *std.ArrayList(Zir.Inst.Index),
|
||||
inst: Zir.Inst.Index,
|
||||
list: *std.ArrayList(Inst.Index),
|
||||
inst: Inst.Index,
|
||||
) Allocator.Error!void {
|
||||
const tags = zir.instructions.items(.tag);
|
||||
const datas = zir.instructions.items(.data);
|
||||
@ -3148,19 +3156,7 @@ fn findDeclsInner(
|
||||
try zir.findDeclsBody(list, then_body);
|
||||
try zir.findDeclsBody(list, else_body);
|
||||
},
|
||||
.switch_block => return findDeclsSwitch(zir, list, inst, .none),
|
||||
.switch_block_else => return findDeclsSwitch(zir, list, inst, .@"else"),
|
||||
.switch_block_under => return findDeclsSwitch(zir, list, inst, .under),
|
||||
.switch_block_ref => return findDeclsSwitch(zir, list, inst, .none),
|
||||
.switch_block_ref_else => return findDeclsSwitch(zir, list, inst, .@"else"),
|
||||
.switch_block_ref_under => return findDeclsSwitch(zir, list, inst, .under),
|
||||
|
||||
.switch_block_multi => return findDeclsSwitchMulti(zir, list, inst, .none),
|
||||
.switch_block_else_multi => return findDeclsSwitchMulti(zir, list, inst, .@"else"),
|
||||
.switch_block_under_multi => return findDeclsSwitchMulti(zir, list, inst, .under),
|
||||
.switch_block_ref_multi => return findDeclsSwitchMulti(zir, list, inst, .none),
|
||||
.switch_block_ref_else_multi => return findDeclsSwitchMulti(zir, list, inst, .@"else"),
|
||||
.switch_block_ref_under_multi => return findDeclsSwitchMulti(zir, list, inst, .under),
|
||||
.switch_block => return findDeclsSwitch(zir, list, inst),
|
||||
|
||||
.suspend_block => @panic("TODO iterate suspend block"),
|
||||
|
||||
@ -3170,71 +3166,34 @@ fn findDeclsInner(
|
||||
|
||||
fn findDeclsSwitch(
|
||||
zir: Zir,
|
||||
list: *std.ArrayList(Zir.Inst.Index),
|
||||
inst: Zir.Inst.Index,
|
||||
special_prong: SpecialProng,
|
||||
list: *std.ArrayList(Inst.Index),
|
||||
inst: Inst.Index,
|
||||
) Allocator.Error!void {
|
||||
const inst_data = zir.instructions.items(.data)[inst].pl_node;
|
||||
const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index);
|
||||
const special: struct {
|
||||
body: []const Inst.Index,
|
||||
end: usize,
|
||||
} = switch (special_prong) {
|
||||
.none => .{ .body = &.{}, .end = extra.end },
|
||||
.under, .@"else" => blk: {
|
||||
const body_len = zir.extra[extra.end];
|
||||
const extra_body_start = extra.end + 1;
|
||||
break :blk .{
|
||||
.body = zir.extra[extra_body_start..][0..body_len],
|
||||
.end = extra_body_start + body_len,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
try zir.findDeclsBody(list, special.body);
|
||||
var extra_index: usize = extra.end;
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
|
||||
const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
|
||||
const multi_cases_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
break :blk multi_cases_len;
|
||||
} else 0;
|
||||
|
||||
const special_prong = extra.data.bits.specialProng();
|
||||
if (special_prong != .none) {
|
||||
const body_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const body = zir.extra[extra_index..][0..body_len];
|
||||
extra_index += body_len;
|
||||
extra_index += body.len;
|
||||
|
||||
try zir.findDeclsBody(list, body);
|
||||
}
|
||||
}
|
||||
|
||||
fn findDeclsSwitchMulti(
|
||||
zir: Zir,
|
||||
list: *std.ArrayList(Zir.Inst.Index),
|
||||
inst: Zir.Inst.Index,
|
||||
special_prong: SpecialProng,
|
||||
) Allocator.Error!void {
|
||||
const inst_data = zir.instructions.items(.data)[inst].pl_node;
|
||||
const extra = zir.extraData(Inst.SwitchBlockMulti, inst_data.payload_index);
|
||||
const special: struct {
|
||||
body: []const Inst.Index,
|
||||
end: usize,
|
||||
} = switch (special_prong) {
|
||||
.none => .{ .body = &.{}, .end = extra.end },
|
||||
.under, .@"else" => blk: {
|
||||
const body_len = zir.extra[extra.end];
|
||||
const extra_body_start = extra.end + 1;
|
||||
break :blk .{
|
||||
.body = zir.extra[extra_body_start..][0..body_len],
|
||||
.end = extra_body_start + body_len,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
try zir.findDeclsBody(list, special.body);
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
{
|
||||
const scalar_cases_len = extra.data.bits.scalar_cases_len;
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
extra_index += 1;
|
||||
const body_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
@ -3246,7 +3205,7 @@ fn findDeclsSwitchMulti(
|
||||
}
|
||||
{
|
||||
var multi_i: usize = 0;
|
||||
while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) {
|
||||
while (multi_i < multi_cases_len) : (multi_i += 1) {
|
||||
const items_len = zir.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const ranges_len = zir.extra[extra_index];
|
||||
@ -3353,3 +3312,18 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
|
||||
.total_params_len = total_params_len,
|
||||
};
|
||||
}
|
||||
|
||||
const ref_start_index: u32 = Inst.Ref.typed_value_map.len;
|
||||
|
||||
pub fn indexToRef(inst: Inst.Index) Inst.Ref {
|
||||
return @intToEnum(Inst.Ref, ref_start_index + inst);
|
||||
}
|
||||
|
||||
pub fn refToIndex(inst: Inst.Ref) ?Inst.Index {
|
||||
const ref_int = @enumToInt(inst);
|
||||
if (ref_int >= ref_start_index) {
|
||||
return ref_int - ref_start_index;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -234,6 +234,8 @@ const Writer = struct {
|
||||
.@"await",
|
||||
.await_nosuspend,
|
||||
.fence,
|
||||
.switch_cond,
|
||||
.switch_cond_ref,
|
||||
=> try self.writeUnNode(stream, inst),
|
||||
|
||||
.ref,
|
||||
@ -379,19 +381,7 @@ const Writer = struct {
|
||||
.error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
|
||||
.error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
|
||||
|
||||
.switch_block => try self.writePlNodeSwitchBr(stream, inst, .none),
|
||||
.switch_block_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
|
||||
.switch_block_under => try self.writePlNodeSwitchBr(stream, inst, .under),
|
||||
.switch_block_ref => try self.writePlNodeSwitchBr(stream, inst, .none),
|
||||
.switch_block_ref_else => try self.writePlNodeSwitchBr(stream, inst, .@"else"),
|
||||
.switch_block_ref_under => try self.writePlNodeSwitchBr(stream, inst, .under),
|
||||
|
||||
.switch_block_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
|
||||
.switch_block_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
|
||||
.switch_block_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
|
||||
.switch_block_ref_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .none),
|
||||
.switch_block_ref_else_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .@"else"),
|
||||
.switch_block_ref_under_multi => try self.writePlNodeSwitchBlockMulti(stream, inst, .under),
|
||||
.switch_block => try self.writePlNodeSwitchBlock(stream, inst),
|
||||
|
||||
.field_ptr,
|
||||
.field_val,
|
||||
@ -1649,113 +1639,46 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writePlNodeSwitchBr(
|
||||
self: *Writer,
|
||||
stream: anytype,
|
||||
inst: Zir.Inst.Index,
|
||||
special_prong: Zir.SpecialProng,
|
||||
) !void {
|
||||
fn writePlNodeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
|
||||
const special: struct {
|
||||
body: []const Zir.Inst.Index,
|
||||
end: usize,
|
||||
} = switch (special_prong) {
|
||||
.none => .{ .body = &.{}, .end = extra.end },
|
||||
.under, .@"else" => blk: {
|
||||
const body_len = self.code.extra[extra.end];
|
||||
const extra_body_start = extra.end + 1;
|
||||
break :blk .{
|
||||
.body = self.code.extra[extra_body_start..][0..body_len],
|
||||
.end = extra_body_start + body_len,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
var extra_index: usize = extra.end;
|
||||
|
||||
const multi_cases_len = if (extra.data.bits.has_multi_cases) blk: {
|
||||
const multi_cases_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
break :blk multi_cases_len;
|
||||
} else 0;
|
||||
|
||||
try self.writeInstRef(stream, extra.data.operand);
|
||||
try self.writeFlag(stream, ", ref", extra.data.bits.is_ref);
|
||||
|
||||
self.indent += 2;
|
||||
|
||||
if (special.body.len != 0) {
|
||||
else_prong: {
|
||||
const special_prong = extra.data.bits.specialProng();
|
||||
const prong_name = switch (special_prong) {
|
||||
.@"else" => "else",
|
||||
.under => "_",
|
||||
else => unreachable,
|
||||
else => break :else_prong,
|
||||
};
|
||||
|
||||
const body_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const body = self.code.extra[extra_index..][0..body_len];
|
||||
extra_index += body.len;
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
try stream.print("{s} => ", .{prong_name});
|
||||
try self.writeBracedBody(stream, special.body);
|
||||
try self.writeBracedBody(stream, body);
|
||||
}
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
{
|
||||
const scalar_cases_len = extra.data.bits.scalar_cases_len;
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < extra.data.cases_len) : (scalar_i += 1) {
|
||||
const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const body = self.code.extra[extra_index..][0..body_len];
|
||||
extra_index += body_len;
|
||||
|
||||
try stream.writeAll(",\n");
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
try self.writeInstRef(stream, item_ref);
|
||||
try stream.writeAll(" => ");
|
||||
try self.writeBracedBody(stream, body);
|
||||
}
|
||||
}
|
||||
|
||||
self.indent -= 2;
|
||||
|
||||
try stream.writeAll(") ");
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writePlNodeSwitchBlockMulti(
|
||||
self: *Writer,
|
||||
stream: anytype,
|
||||
inst: Zir.Inst.Index,
|
||||
special_prong: Zir.SpecialProng,
|
||||
) !void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.SwitchBlockMulti, inst_data.payload_index);
|
||||
const special: struct {
|
||||
body: []const Zir.Inst.Index,
|
||||
end: usize,
|
||||
} = switch (special_prong) {
|
||||
.none => .{ .body = &.{}, .end = extra.end },
|
||||
.under, .@"else" => blk: {
|
||||
const body_len = self.code.extra[extra.end];
|
||||
const extra_body_start = extra.end + 1;
|
||||
break :blk .{
|
||||
.body = self.code.extra[extra_body_start..][0..body_len],
|
||||
.end = extra_body_start + body_len,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
try self.writeInstRef(stream, extra.data.operand);
|
||||
|
||||
self.indent += 2;
|
||||
|
||||
if (special.body.len != 0) {
|
||||
const prong_name = switch (special_prong) {
|
||||
.@"else" => "else",
|
||||
.under => "_",
|
||||
else => unreachable,
|
||||
};
|
||||
try stream.writeAll(",\n");
|
||||
try stream.writeByteNTimes(' ', self.indent);
|
||||
try stream.print("{s} => ", .{prong_name});
|
||||
try self.writeBracedBody(stream, special.body);
|
||||
}
|
||||
|
||||
var extra_index: usize = special.end;
|
||||
{
|
||||
var scalar_i: usize = 0;
|
||||
while (scalar_i < extra.data.scalar_cases_len) : (scalar_i += 1) {
|
||||
while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
|
||||
const item_ref = @intToEnum(Zir.Inst.Ref, self.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const body_len = self.code.extra[extra_index];
|
||||
@ -1772,7 +1695,7 @@ const Writer = struct {
|
||||
}
|
||||
{
|
||||
var multi_i: usize = 0;
|
||||
while (multi_i < extra.data.multi_cases_len) : (multi_i += 1) {
|
||||
while (multi_i < multi_cases_len) : (multi_i += 1) {
|
||||
const items_len = self.code.extra[extra_index];
|
||||
extra_index += 1;
|
||||
const ranges_len = self.code.extra[extra_index];
|
||||
|
||||
@ -116,7 +116,7 @@ pub const Value = extern union {
|
||||
decl_ref_mut,
|
||||
/// Pointer to a specific element of an array.
|
||||
elem_ptr,
|
||||
/// Pointer to a specific field of a struct.
|
||||
/// Pointer to a specific field of a struct or union.
|
||||
field_ptr,
|
||||
/// A slice of u8 whose memory is managed externally.
|
||||
bytes,
|
||||
|
||||
@ -71,3 +71,34 @@ test "0-sized extern union definition" {
|
||||
|
||||
try expect(U.f == 1);
|
||||
}
|
||||
|
||||
const Value = union(enum) {
|
||||
Int: u64,
|
||||
Array: [9]u8,
|
||||
};
|
||||
|
||||
const Agg = struct {
|
||||
val1: Value,
|
||||
val2: Value,
|
||||
};
|
||||
|
||||
const v1 = Value{ .Int = 1234 };
|
||||
const v2 = Value{ .Array = [_]u8{3} ** 9 };
|
||||
|
||||
const err = @as(anyerror!Agg, Agg{
|
||||
.val1 = v1,
|
||||
.val2 = v2,
|
||||
});
|
||||
|
||||
const array = [_]Value{ v1, v2, v1, v2 };
|
||||
|
||||
test "unions embedded in aggregate types" {
|
||||
switch (array[1]) {
|
||||
Value.Array => |arr| try expect(arr[4] == 3),
|
||||
else => unreachable,
|
||||
}
|
||||
switch ((err catch unreachable).val1) {
|
||||
Value.Int => |x| try expect(x == 1234),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,37 +3,6 @@ const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const Tag = std.meta.Tag;
|
||||
|
||||
const Value = union(enum) {
|
||||
Int: u64,
|
||||
Array: [9]u8,
|
||||
};
|
||||
|
||||
const Agg = struct {
|
||||
val1: Value,
|
||||
val2: Value,
|
||||
};
|
||||
|
||||
const v1 = Value{ .Int = 1234 };
|
||||
const v2 = Value{ .Array = [_]u8{3} ** 9 };
|
||||
|
||||
const err = @as(anyerror!Agg, Agg{
|
||||
.val1 = v1,
|
||||
.val2 = v2,
|
||||
});
|
||||
|
||||
const array = [_]Value{ v1, v2, v1, v2 };
|
||||
|
||||
test "unions embedded in aggregate types" {
|
||||
switch (array[1]) {
|
||||
Value.Array => |arr| try expect(arr[4] == 3),
|
||||
else => unreachable,
|
||||
}
|
||||
switch ((err catch unreachable).val1) {
|
||||
Value.Int => |x| try expect(x == 1234),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
const Letter = enum { A, B, C };
|
||||
const Payload = union(Letter) {
|
||||
A: i32,
|
||||
|
||||
@ -852,7 +852,7 @@ pub fn addCases(ctx: *TestContext) !void {
|
||||
\\ _ = E.d;
|
||||
\\}
|
||||
, &.{
|
||||
":3:10: error: enum 'tmp.E' has no member named 'd'",
|
||||
":3:11: error: enum 'tmp.E' has no member named 'd'",
|
||||
":1:11: note: enum declared here",
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user