authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-21 12:25:19+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-21 12:51:40+03:00
logd48af541c7aa235948621cdbc250d983af303977
tree8dbe65a62e708f182371b207896ebe33cf1586b4
parente8102d8738eafb969e03b0609c60be73326610eb

Sema: handle union and enum field order being different

Closes #12543

6 files changed, 53 insertions(+), 20 deletions(-)

src/Sema.zig+18-15
......@@ -3615,8 +3615,6 @@ fn validateUnionInit(
36153615 union_ptr: Air.Inst.Ref,
36163616 is_comptime: bool,
36173617) CompileError!void {
3618 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
3619
36203618 if (instrs.len != 1) {
36213619 const msg = msg: {
36223620 const msg = try sema.errMsg(
......@@ -3650,7 +3648,8 @@ fn validateUnionInit(
36503648 const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node };
36513649 const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
36523650 const field_name = sema.code.nullTerminatedString(field_ptr_extra.field_name_start);
3653 const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
3651 // Validate the field access but ignore the index since we want the tag enum field index.
3652 _ = try sema.unionFieldIndex(block, union_ty, field_name, field_src);
36543653 const air_tags = sema.air_instructions.items(.tag);
36553654 const air_datas = sema.air_instructions.items(.data);
36563655 const field_ptr_air_ref = sema.inst_map.get(field_ptr).?;
......@@ -3709,7 +3708,9 @@ fn validateUnionInit(
37093708 break;
37103709 }
37113710
3712 const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
3711 const tag_ty = union_ty.unionTagTypeHypothetical();
3712 const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?);
3713 const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index);
37133714
37143715 if (init_val) |val| {
37153716 // Our task is to delete all the `field_ptr` and `store` instructions, and insert
......@@ -3726,7 +3727,7 @@ fn validateUnionInit(
37263727 }
37273728
37283729 try sema.requireFunctionBlock(block, init_src);
3729 const new_tag = try sema.addConstant(union_obj.tag_ty, tag_val);
3730 const new_tag = try sema.addConstant(tag_ty, tag_val);
37303731 _ = try block.addBinOp(.set_union_tag, union_ptr, new_tag);
37313732}
37323733
......@@ -8838,13 +8839,11 @@ fn zirSwitchCapture(
88388839 switch (operand_ty.zigTypeTag()) {
88398840 .Union => {
88408841 const union_obj = operand_ty.cast(Type.Payload.Union).?.data;
8841 const enum_ty = union_obj.tag_ty;
8842
88438842 const first_item = try sema.resolveInst(items[0]);
88448843 // Previous switch validation ensured this will succeed
88458844 const first_item_val = sema.resolveConstValue(block, .unneeded, first_item, undefined) catch unreachable;
88468845
8847 const first_field_index = @intCast(u32, enum_ty.enumTagFieldIndex(first_item_val, sema.mod).?);
8846 const first_field_index = @intCast(u32, operand_ty.unionTagFieldIndex(first_item_val, sema.mod).?);
88488847 const first_field = union_obj.fields.values()[first_field_index];
88498848
88508849 for (items[1..]) |item, i| {
......@@ -8852,7 +8851,7 @@ fn zirSwitchCapture(
88528851 // Previous switch validation ensured this will succeed
88538852 const item_val = sema.resolveConstValue(block, .unneeded, item_ref, undefined) catch unreachable;
88548853
8855 const field_index = enum_ty.enumTagFieldIndex(item_val, sema.mod).?;
8854 const field_index = operand_ty.unionTagFieldIndex(item_val, sema.mod).?;
88568855 const field = union_obj.fields.values()[field_index];
88578856 if (!field.ty.eql(first_field.ty, sema.mod)) {
88588857 const msg = msg: {
......@@ -15585,7 +15584,9 @@ fn unionInit(
1558515584 const init = try sema.coerce(block, field.ty, uncasted_init, init_src);
1558615585
1558715586 if (try sema.resolveMaybeUndefVal(block, init_src, init)) |init_val| {
15588 const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
15587 const tag_ty = union_ty.unionTagTypeHypothetical();
15588 const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?);
15589 const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index);
1558915590 return sema.addConstant(union_ty, try Value.Tag.@"union".create(sema.arena, .{
1559015591 .tag = tag_val,
1559115592 .val = init_val,
......@@ -15683,7 +15684,9 @@ fn zirStructInit(
1568315684 const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
1568415685 const field_name = sema.code.nullTerminatedString(field_type_extra.name_start);
1568515686 const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src);
15686 const tag_val = try Value.Tag.enum_field_index.create(sema.arena, field_index);
15687 const tag_ty = resolved_ty.unionTagTypeHypothetical();
15688 const enum_field_index = @intCast(u32, tag_ty.enumFieldIndex(field_name).?);
15689 const tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index);
1568715690
1568815691 const init_inst = try sema.resolveInst(item.data.init);
1568915692 if (try sema.resolveMaybeUndefVal(block, field_src, init_inst)) |val| {
......@@ -16448,9 +16451,8 @@ fn zirReify(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData, in
1644816451 const type_info = try sema.coerce(block, type_info_ty, uncasted_operand, operand_src);
1644916452 const val = try sema.resolveConstValue(block, operand_src, type_info, "operand to @Type must be comptime known");
1645016453 const union_val = val.cast(Value.Payload.Union).?.data;
16451 const tag_ty = type_info_ty.unionTagType().?;
1645216454 const target = mod.getTarget();
16453 const tag_index = tag_ty.enumTagFieldIndex(union_val.tag, mod).?;
16455 const tag_index = type_info_ty.unionTagFieldIndex(union_val.tag, mod).?;
1645416456 if (union_val.val.anyUndef()) return sema.failWithUseOfUndef(block, src);
1645516457 switch (@intToEnum(std.builtin.TypeId, tag_index)) {
1645616458 .Type => return Air.Inst.Ref.type_type,
......@@ -25155,8 +25157,7 @@ fn coerceEnumToUnion(
2515525157
2515625158 const enum_tag = try sema.coerce(block, tag_ty, inst, inst_src);
2515725159 if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| {
25158 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
25159 const field_index = union_obj.tag_ty.enumTagFieldIndex(val, sema.mod) orelse {
25160 const field_index = union_ty.unionTagFieldIndex(val, sema.mod) orelse {
2516025161 const msg = msg: {
2516125162 const msg = try sema.errMsg(block, inst_src, "union '{}' has no tag with value '{}'", .{
2516225163 union_ty.fmt(sema.mod), val.fmtValue(tag_ty, sema.mod),
......@@ -25167,6 +25168,8 @@ fn coerceEnumToUnion(
2516725168 };
2516825169 return sema.failWithOwnedErrorMsg(msg);
2516925170 };
25171
25172 const union_obj = union_ty.cast(Type.Payload.Union).?.data;
2517025173 const field = union_obj.fields.values()[field_index];
2517125174 const field_ty = try sema.resolveTypeFields(block, inst_src, field.ty);
2517225175 if (field_ty.zigTypeTag() == .NoReturn) {
src/codegen.zig+1-1
......@@ -607,7 +607,7 @@ pub fn generateSymbol(
607607
608608 const union_ty = typed_value.ty.cast(Type.Payload.Union).?.data;
609609 const mod = bin_file.options.module.?;
610 const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, mod).?;
610 const field_index = typed_value.ty.unionTagFieldIndex(union_obj.tag, mod).?;
611611 assert(union_ty.haveFieldTypes());
612612 const field_ty = union_ty.fields.values()[field_index].ty;
613613 if (!field_ty.hasRuntimeBits()) {
src/codegen/c.zig+1-2
......@@ -835,7 +835,6 @@ pub const DeclGen = struct {
835835 },
836836 .Union => {
837837 const union_obj = val.castTag(.@"union").?.data;
838 const union_ty = ty.cast(Type.Payload.Union).?.data;
839838 const layout = ty.unionGetLayout(target);
840839
841840 try writer.writeAll("(");
......@@ -851,7 +850,7 @@ pub const DeclGen = struct {
851850 try writer.writeAll(".payload = {");
852851 }
853852
854 const index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag, dg.module).?;
853 const index = ty.unionTagFieldIndex(union_obj.tag, dg.module).?;
855854 const field_ty = ty.unionFields().values()[index].ty;
856855 const field_name = ty.unionFields().keys()[index];
857856 if (field_ty.hasRuntimeBits()) {
src/codegen/llvm.zig+1-1
......@@ -3502,7 +3502,7 @@ pub const DeclGen = struct {
35023502 });
35033503 }
35043504 const union_obj = tv.ty.cast(Type.Payload.Union).?.data;
3505 const field_index = union_obj.tag_ty.enumTagFieldIndex(tag_and_val.tag, dg.module).?;
3505 const field_index = tv.ty.unionTagFieldIndex(tag_and_val.tag, dg.module).?;
35063506 assert(union_obj.haveFieldTypes());
35073507
35083508 // Sometimes we must make an unnamed struct because LLVM does
src/type.zig+8-1
......@@ -4285,11 +4285,18 @@ pub const Type = extern union {
42854285
42864286 pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type {
42874287 const union_obj = ty.cast(Payload.Union).?.data;
4288 const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, mod).?;
4288 const index = ty.unionTagFieldIndex(enum_tag, mod).?;
42894289 assert(union_obj.haveFieldTypes());
42904290 return union_obj.fields.values()[index].ty;
42914291 }
42924292
4293 pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?usize {
4294 const union_obj = ty.cast(Payload.Union).?.data;
4295 const index = union_obj.tag_ty.enumTagFieldIndex(enum_tag, mod) orelse return null;
4296 const name = union_obj.tag_ty.enumFieldName(index);
4297 return union_obj.fields.getIndex(name);
4298 }
4299
42934300 pub fn unionHasAllZeroBitFieldTypes(ty: Type) bool {
42944301 return ty.cast(Payload.Union).?.data.hasAllZeroBitFieldTypes();
42954302 }
test/behavior/union.zig+24
......@@ -1301,3 +1301,27 @@ test "noreturn field in union" {
13011301 }
13021302 try expect(count == 5);
13031303}
1304
1305test "union and enum field order doesn't match" {
1306 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
1307 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1308 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1309
1310 const MyTag = enum(u32) {
1311 b = 1337,
1312 a = 1666,
1313 };
1314 const MyUnion = union(MyTag) {
1315 a: f32,
1316 b: void,
1317 };
1318 var x: MyUnion = .{ .a = 666 };
1319 switch (x) {
1320 .a => |my_f32| {
1321 try expect(@TypeOf(my_f32) == f32);
1322 },
1323 .b => unreachable,
1324 }
1325 x = .b;
1326 try expect(x == .b);
1327}