authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-09-23 13:03:03-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-09-23 13:05:04-04:00
logf2a24b48e1221a8954ddf16e9070e1470ee13e8d
tree65443a1d67bb03be28927643462f92568fedbbc0
parent2fddd767ba20374e7677003c101e60f470c3804c

sema: rework the comptime representation of comptime unions

When the tag is not known, it's set to `.none`. In this case, the value is either an array of bytes (for extern unions) or an integer (for packed unions).

12 files changed, 147 insertions(+), 138 deletions(-)

src/InternPool.zig+4-2
...@@ -1105,7 +1105,10 @@ pub const Key = union(enum) {...@@ -1105,7 +1105,10 @@ pub const Key = union(enum) {
1105 pub const Union = extern struct {1105 pub const Union = extern struct {
1106 /// This is the union type; not the field type.1106 /// This is the union type; not the field type.
1107 ty: Index,1107 ty: Index,
1108 /// Indicates the active field.1108 /// Indicates the active field. This could be `none`, which indicates the tag is not known. `none` is only a valid value for extern and packed unions.
1109 /// In those cases, the type of `val` is:
1110 /// extern: a u8 array of the same byte length as the union
1111 /// packed: an unsigned integer with the same bit size as the union
1109 tag: Index,1112 tag: Index,
1110 /// The value of the active field.1113 /// The value of the active field.
1111 val: Index,1114 val: Index,
...@@ -5130,7 +5133,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {...@@ -5130,7 +5133,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
51305133
5131 .un => |un| {5134 .un => |un| {
5132 assert(un.ty != .none);5135 assert(un.ty != .none);
5133 assert(un.tag != .none);
5134 assert(un.val != .none);5136 assert(un.val != .none);
5135 ip.items.appendAssumeCapacity(.{5137 ip.items.appendAssumeCapacity(.{
5136 .tag = .union_value,5138 .tag = .union_value,
src/Module.zig+2-29
...@@ -5823,7 +5823,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void {...@@ -5823,7 +5823,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void {
5823 .aggregate => |aggregate| for (aggregate.storage.values()) |elem|5823 .aggregate => |aggregate| for (aggregate.storage.values()) |elem|
5824 try mod.markReferencedDeclsAlive(elem.toValue()),5824 try mod.markReferencedDeclsAlive(elem.toValue()),
5825 .un => |un| {5825 .un => |un| {
5826 try mod.markReferencedDeclsAlive(un.tag.toValue());5826 if (un.tag != .none) try mod.markReferencedDeclsAlive(un.tag.toValue());
5827 try mod.markReferencedDeclsAlive(un.val.toValue());5827 try mod.markReferencedDeclsAlive(un.val.toValue());
5828 },5828 },
5829 else => {},5829 else => {},
...@@ -6607,7 +6607,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in...@@ -6607,7 +6607,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in
66076607
6608pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 {6608pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 {
6609 const ip = &mod.intern_pool;6609 const ip = &mod.intern_pool;
6610 if (enum_tag.toIntern() == .undef) return null;6610 if (enum_tag.toIntern() == .none) return null;
6611 assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty);6611 assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty);
6612 const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type;6612 const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type;
6613 return enum_type.tagValueIndex(ip, enum_tag.toIntern());6613 return enum_type.tagValueIndex(ip, enum_tag.toIntern());
...@@ -6673,30 +6673,3 @@ pub fn structPackedFieldBitOffset(...@@ -6673,30 +6673,3 @@ pub fn structPackedFieldBitOffset(
6673 }6673 }
6674 unreachable; // index out of bounds6674 unreachable; // index out of bounds
6675}6675}
6676
6677pub fn unionLargestField(mod: *Module, u: InternPool.UnionType) struct {
6678 ty: Type,
6679 index: u32,
6680 size: u64,
6681} {
6682 const fields = u.field_types.get(&mod.intern_pool);
6683 assert(fields.len != 0);
6684 var largest_field_ty: Type = undefined;
6685 var largest_field_size: u64 = 0;
6686 var largest_field_index: u32 = 0;
6687 for (fields, 0..) |union_field, i| {
6688 const field_ty = union_field.toType();
6689 const size: u32 = @intCast(field_ty.abiSize(mod));
6690 if (size > largest_field_size) {
6691 largest_field_ty = field_ty;
6692 largest_field_size = size;
6693 largest_field_index = @intCast(i);
6694 }
6695 }
6696
6697 return .{
6698 .ty = largest_field_ty,
6699 .index = largest_field_index,
6700 .size = largest_field_size,
6701 };
6702}
src/Sema.zig+10-6
...@@ -12124,7 +12124,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12124,7 +12124,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1212412124
12125 const analyze_body = if (union_originally) blk: {12125 const analyze_body = if (union_originally) blk: {
12126 const item_val = sema.resolveConstLazyValue(block, .unneeded, item, undefined) catch unreachable;12126 const item_val = sema.resolveConstLazyValue(block, .unneeded, item, undefined) catch unreachable;
12127 const field_ty = maybe_union_ty.unionFieldType(item_val, mod);12127 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
12128 break :blk field_ty.zigTypeTag(mod) != .NoReturn;12128 break :blk field_ty.zigTypeTag(mod) != .NoReturn;
12129 } else true;12129 } else true;
1213012130
...@@ -12250,7 +12250,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12250,7 +12250,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1225012250
12251 const analyze_body = if (union_originally) blk: {12251 const analyze_body = if (union_originally) blk: {
12252 const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;12252 const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
12253 const field_ty = maybe_union_ty.unionFieldType(item_val, mod);12253 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
12254 break :blk field_ty.zigTypeTag(mod) != .NoReturn;12254 break :blk field_ty.zigTypeTag(mod) != .NoReturn;
12255 } else true;12255 } else true;
1225612256
...@@ -12304,7 +12304,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12304,7 +12304,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
12304 const analyze_body = if (union_originally)12304 const analyze_body = if (union_originally)
12305 for (items) |item| {12305 for (items) |item| {
12306 const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;12306 const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
12307 const field_ty = maybe_union_ty.unionFieldType(item_val, mod);12307 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
12308 if (field_ty.zigTypeTag(mod) != .NoReturn) break true;12308 if (field_ty.zigTypeTag(mod) != .NoReturn) break true;
12309 } else false12309 } else false
12310 else12310 else
...@@ -12456,7 +12456,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12456,7 +12456,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
12456 case_block.wip_capture_scope = child_block.wip_capture_scope;12456 case_block.wip_capture_scope = child_block.wip_capture_scope;
1245712457
12458 const analyze_body = if (union_originally) blk: {12458 const analyze_body = if (union_originally) blk: {
12459 const field_ty = maybe_union_ty.unionFieldType(item_val, mod);12459 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
12460 break :blk field_ty.zigTypeTag(mod) != .NoReturn;12460 break :blk field_ty.zigTypeTag(mod) != .NoReturn;
12461 } else true;12461 } else true;
1246212462
...@@ -16496,7 +16496,7 @@ fn analyzeCmpUnionTag(...@@ -16496,7 +16496,7 @@ fn analyzeCmpUnionTag(
1649616496
16497 if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| {16497 if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| {
16498 if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool);16498 if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool);
16499 const field_ty = union_ty.unionFieldType(enum_val, mod);16499 const field_ty = union_ty.unionFieldType(enum_val, mod).?;
16500 if (field_ty.zigTypeTag(mod) == .NoReturn) {16500 if (field_ty.zigTypeTag(mod) == .NoReturn) {
16501 return .bool_false;16501 return .bool_false;
16502 }16502 }
...@@ -27208,7 +27208,11 @@ fn unionFieldVal(...@@ -27208,7 +27208,11 @@ fn unionFieldVal(
27208 if (tag_matches) {27208 if (tag_matches) {
27209 return Air.internedToRef(un.val);27209 return Air.internedToRef(un.val);
27210 } else {27210 } else {
27211 const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod);27211 const old_ty = if (un.tag == .none)
27212 ip.typeOf(un.val).toType()
27213 else
27214 union_ty.unionFieldType(un.tag.toValue(), mod).?;
27215
27212 if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| {27216 if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| {
27213 return Air.internedToRef(new_val.toIntern());27217 return Air.internedToRef(new_val.toIntern());
27214 }27218 }
src/TypedValue.zig+16-8
...@@ -92,10 +92,14 @@ pub fn print(...@@ -92,10 +92,14 @@ pub fn print(
92 .val = union_val.tag,92 .val = union_val.tag,
93 }, writer, level - 1, mod);93 }, writer, level - 1, mod);
94 try writer.writeAll(" = ");94 try writer.writeAll(" = ");
95 try print(.{95 if (ty.unionFieldType(union_val.tag, mod)) |field_ty| {
96 .ty = ty.unionFieldType(union_val.tag, mod),96 try print(.{
97 .val = union_val.val,97 .ty = field_ty,
98 }, writer, level - 1, mod);98 .val = union_val.val,
99 }, writer, level - 1, mod);
100 } else {
101 return writer.writeAll("(no tag)");
102 }
99103
100 return writer.writeAll(" }");104 return writer.writeAll(" }");
101 },105 },
...@@ -409,10 +413,14 @@ pub fn print(...@@ -409,10 +413,14 @@ pub fn print(
409 .val = un.tag.toValue(),413 .val = un.tag.toValue(),
410 }, writer, level - 1, mod);414 }, writer, level - 1, mod);
411 try writer.writeAll(" = ");415 try writer.writeAll(" = ");
412 try print(.{416 if (ty.unionFieldType(un.tag.toValue(), mod)) |field_ty| {
413 .ty = ty.unionFieldType(un.tag.toValue(), mod),417 try print(.{
414 .val = un.val.toValue(),418 .ty = field_ty,
415 }, writer, level - 1, mod);419 .val = un.val.toValue(),
420 }, writer, level - 1, mod);
421 } else {
422 try writer.writeAll("(no tag)");
423 }
416 } else try writer.writeAll("...");424 } else try writer.writeAll("...");
417 return writer.writeAll(" }");425 return writer.writeAll(" }");
418 },426 },
src/arch/wasm/CodeGen.zig+1-4
...@@ -3259,10 +3259,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {...@@ -3259,10 +3259,7 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue {
3259 .un => |un| {3259 .un => |un| {
3260 // in this case we have a packed union which will not be passed by reference.3260 // in this case we have a packed union which will not be passed by reference.
3261 const union_obj = mod.typeToUnion(ty).?;3261 const union_obj = mod.typeToUnion(ty).?;
3262 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: {3262 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
3263 assert(union_obj.getLayout(ip) == .Extern);
3264 break :f mod.unionLargestField(union_obj).index;
3265 };
3266 const field_ty = union_obj.field_types.get(ip)[field_index].toType();3263 const field_ty = union_obj.field_types.get(ip)[field_index].toType();
3267 return func.lowerConstant(un.val.toValue(), field_ty);3264 return func.lowerConstant(un.val.toValue(), field_ty);
3268 },3265 },
src/codegen.zig+1-4
...@@ -583,10 +583,7 @@ pub fn generateSymbol(...@@ -583,10 +583,7 @@ pub fn generateSymbol(
583 }583 }
584584
585 const union_obj = mod.typeToUnion(typed_value.ty).?;585 const union_obj = mod.typeToUnion(typed_value.ty).?;
586 const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod) orelse f: {586 const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
587 assert(union_obj.getLayout(ip) == .Extern);
588 break :f mod.unionLargestField(union_obj).index;
589 };
590587
591 const field_ty = union_obj.field_types.get(ip)[field_index].toType();588 const field_ty = union_obj.field_types.get(ip)[field_index].toType();
592 if (!field_ty.hasRuntimeBits(mod)) {589 if (!field_ty.hasRuntimeBits(mod)) {
src/codegen/c.zig+1-4
...@@ -1439,10 +1439,7 @@ pub const DeclGen = struct {...@@ -1439,10 +1439,7 @@ pub const DeclGen = struct {
1439 }1439 }
14401440
1441 const union_obj = mod.typeToUnion(ty).?;1441 const union_obj = mod.typeToUnion(ty).?;
1442 const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: {1442 const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
1443 assert(union_obj.getLayout(ip) == .Extern);
1444 break :f mod.unionLargestField(union_obj).index;
1445 };
1446 const field_ty = union_obj.field_types.get(ip)[field_i].toType();1443 const field_ty = union_obj.field_types.get(ip)[field_i].toType();
1447 const field_name = union_obj.field_names.get(ip)[field_i];1444 const field_name = union_obj.field_names.get(ip)[field_i];
1448 if (union_obj.getLayout(ip) == .Packed) {1445 if (union_obj.getLayout(ip) == .Packed) {
src/codegen/llvm.zig+35-21
...@@ -4108,28 +4108,28 @@ pub const Object = struct {...@@ -4108,28 +4108,28 @@ pub const Object = struct {
4108 if (layout.payload_size == 0) return o.lowerValue(un.tag);4108 if (layout.payload_size == 0) return o.lowerValue(un.tag);
41094109
4110 const union_obj = mod.typeToUnion(ty).?;4110 const union_obj = mod.typeToUnion(ty).?;
4111 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: {4111 const container_layout = union_obj.getLayout(ip);
4112 assert(union_obj.getLayout(ip) == .Extern);4112
4113 break :f mod.unionLargestField(union_obj).index;4113 var need_unnamed = false;
4114 };4114 const payload = if (un.tag != .none) p: {
4115 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
4116 const field_ty = union_obj.field_types.get(ip)[field_index].toType();
4117 if (container_layout == .Packed) {
4118 if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0);
4119 const small_int_val = try o.builder.castConst(
4120 if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast,
4121 try o.lowerValue(un.val),
4122 try o.builder.intType(@intCast(field_ty.bitSize(mod))),
4123 );
4124 return o.builder.convConst(.unsigned, small_int_val, union_ty);
4125 }
41154126
4116 const field_ty = union_obj.field_types.get(ip)[field_index].toType();4127 // Sometimes we must make an unnamed struct because LLVM does
4117 if (union_obj.getLayout(ip) == .Packed) {4128 // not support bitcasting our payload struct to the true union payload type.
4118 if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0);4129 // Instead we use an unnamed struct and every reference to the global
4119 const small_int_val = try o.builder.castConst(4130 // must pointer cast to the expected type before accessing the union.
4120 if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast,4131 need_unnamed = layout.most_aligned_field != field_index;
4121 try o.lowerValue(un.val),
4122 try o.builder.intType(@intCast(field_ty.bitSize(mod))),
4123 );
4124 return o.builder.convConst(.unsigned, small_int_val, union_ty);
4125 }
41264132
4127 // Sometimes we must make an unnamed struct because LLVM does
4128 // not support bitcasting our payload struct to the true union payload type.
4129 // Instead we use an unnamed struct and every reference to the global
4130 // must pointer cast to the expected type before accessing the union.
4131 var need_unnamed = layout.most_aligned_field != field_index;
4132 const payload = p: {
4133 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {4133 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {
4134 const padding_len = layout.payload_size;4134 const padding_len = layout.payload_size;
4135 break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8));4135 break :p try o.builder.undefConst(try o.builder.arrayType(padding_len, .i8));
...@@ -4147,9 +4147,23 @@ pub const Object = struct {...@@ -4147,9 +4147,23 @@ pub const Object = struct {
4147 try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }),4147 try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }),
4148 &.{ payload, try o.builder.undefConst(padding_ty) },4148 &.{ payload, try o.builder.undefConst(padding_ty) },
4149 );4149 );
4150 } else p: {
4151 assert(layout.tag_size == 0);
4152 const union_val = try o.lowerValue(un.val);
4153 if (container_layout == .Packed) {
4154 const bitcast_val = try o.builder.castConst(
4155 .bitcast,
4156 union_val,
4157 try o.builder.intType(@intCast(ty.bitSize(mod))),
4158 );
4159 return o.builder.convConst(.unsigned, bitcast_val, union_ty);
4160 }
4161
4162 need_unnamed = true;
4163 break :p union_val;
4150 };4164 };
4151 const payload_ty = payload.typeOf(&o.builder);
41524165
4166 const payload_ty = payload.typeOf(&o.builder);
4153 if (layout.tag_size == 0) return o.builder.structConst(if (need_unnamed)4167 if (layout.tag_size == 0) return o.builder.structConst(if (need_unnamed)
4154 try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty})4168 try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty})
4155 else4169 else
src/codegen/spirv.zig+1-4
...@@ -838,10 +838,7 @@ pub const DeclGen = struct {...@@ -838,10 +838,7 @@ pub const DeclGen = struct {
838 return dg.todo("packed union constants", .{});838 return dg.todo("packed union constants", .{});
839 }839 }
840840
841 const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module) orelse f: {841 const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module).?;
842 assert(union_obj.getLayout(ip) == .Extern);
843 break :f mod.unionLargestField(union_obj).index;
844 };
845 const active_field_ty = union_obj.field_types.get(ip)[active_field].toType();842 const active_field_ty = union_obj.field_types.get(ip)[active_field].toType();
846843
847 const has_tag = layout.tag_size != 0;844 const has_tag = layout.tag_size != 0;
src/type.zig+4-6
...@@ -1658,6 +1658,7 @@ pub const Type = struct {...@@ -1658,6 +1658,7 @@ pub const Type = struct {
1658 const field_ty = union_obj.field_types.get(ip)[field_index];1658 const field_ty = union_obj.field_types.get(ip)[field_index];
1659 size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema));1659 size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema));
1660 }1660 }
1661
1661 return size;1662 return size;
1662 },1663 },
1663 .opaque_type => unreachable,1664 .opaque_type => unreachable,
...@@ -1926,15 +1927,12 @@ pub const Type = struct {...@@ -1926,15 +1927,12 @@ pub const Type = struct {
1926 return union_obj.enum_tag_ty.toType();1927 return union_obj.enum_tag_ty.toType();
1927 }1928 }
19281929
1929 pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type {1930 pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) ?Type {
1930 const ip = &mod.intern_pool;1931 const ip = &mod.intern_pool;
1931 const union_obj = mod.typeToUnion(ty).?;1932 const union_obj = mod.typeToUnion(ty).?;
1932 const union_fields = union_obj.field_types.get(ip);1933 const union_fields = union_obj.field_types.get(ip);
1933 if (mod.unionTagFieldIndex(union_obj, enum_tag)) |index| {1934 const index = mod.unionTagFieldIndex(union_obj, enum_tag) orelse return null;
1934 return union_fields[index].toType();1935 return union_fields[index].toType();
1935 } else {
1936 return mod.unionLargestField(union_obj).ty;
1937 }
1938 }1936 }
19391937
1940 pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 {1938 pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 {
src/value.zig+29-36
...@@ -330,7 +330,7 @@ pub const Value = struct {...@@ -330,7 +330,7 @@ pub const Value = struct {
330 return mod.intern(.{ .un = .{330 return mod.intern(.{ .un = .{
331 .ty = ty.toIntern(),331 .ty = ty.toIntern(),
332 .tag = try pl.tag.intern(ty.unionTagTypeHypothetical(mod), mod),332 .tag = try pl.tag.intern(ty.unionTagTypeHypothetical(mod), mod),
333 .val = try pl.val.intern(ty.unionFieldType(pl.tag, mod), mod),333 .val = try pl.val.intern(ty.unionFieldType(pl.tag, mod).?, mod),
334 } });334 } });
335 },335 },
336 }336 }
...@@ -703,22 +703,20 @@ pub const Value = struct {...@@ -703,22 +703,20 @@ pub const Value = struct {
703 std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], @as(Int, @intCast(int)), endian);703 std.mem.writeInt(Int, buffer[0..@sizeOf(Int)], @as(Int, @intCast(int)), endian);
704 },704 },
705 .Union => switch (ty.containerLayout(mod)) {705 .Union => switch (ty.containerLayout(mod)) {
706 .Auto => return error.IllDefinedMemoryLayout,706 .Auto => return error.IllDefinedMemoryLayout, // Sema is supposed to have emitted a compile error already
707 .Extern => {707 .Extern => {
708 const union_obj = mod.typeToUnion(ty).?;708 const union_obj = mod.typeToUnion(ty).?;
709 const union_tag = val.unionTag(mod);709 const union_tag = val.unionTag(mod);
710710 if (mod.unionTagFieldIndex(union_obj, union_tag)) |field_index| {
711 const field_type, const field_index = if (mod.unionTagFieldIndex(union_obj, union_tag)) |field_index| .{711 const field_type = union_obj.field_types.get(&mod.intern_pool)[field_index].toType();
712 union_obj.field_types.get(&mod.intern_pool)[field_index].toType(),712 const field_val = try val.fieldValue(mod, field_index);
713 field_index,713 const byte_count = @as(usize, @intCast(field_type.abiSize(mod)));
714 } else f: {714 return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]);
715 const largest_field = mod.unionLargestField(union_obj);715 } else {
716 break :f .{ largest_field.ty, largest_field.index };716 const union_size = ty.abiSize(mod);
717 };717 const array_type = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
718718 return writeToMemory(val.unionValue(mod), array_type, mod, buffer[0..union_size]);
719 const field_val = try val.fieldValue(mod, field_index);719 }
720 const byte_count = @as(usize, @intCast(field_type.abiSize(mod)));
721 return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]);
722 },720 },
723 .Packed => {721 .Packed => {
724 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;722 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
...@@ -832,13 +830,11 @@ pub const Value = struct {...@@ -832,13 +830,11 @@ pub const Value = struct {
832 .Union => {830 .Union => {
833 const union_obj = mod.typeToUnion(ty).?;831 const union_obj = mod.typeToUnion(ty).?;
834 switch (union_obj.getLayout(ip)) {832 switch (union_obj.getLayout(ip)) {
835 .Auto => unreachable, // Sema is supposed to have emitted a compile error already833 .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory
836 .Extern => unreachable, // Handled in non-packed writeToMemory
837 .Packed => {834 .Packed => {
838 const field_index = mod.unionTagFieldIndex(union_obj, val.unionTag(mod)).?;835 const field_index = mod.unionTagFieldIndex(union_obj, val.unionTag(mod)).?;
839 const field_type = union_obj.field_types.get(ip)[field_index].toType();836 const field_type = union_obj.field_types.get(ip)[field_index].toType();
840 const field_val = try val.fieldValue(mod, field_index);837 const field_val = try val.fieldValue(mod, field_index);
841
842 return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);838 return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
843 },839 },
844 }840 }
...@@ -988,17 +984,14 @@ pub const Value = struct {...@@ -988,17 +984,14 @@ pub const Value = struct {
988 .Union => switch (ty.containerLayout(mod)) {984 .Union => switch (ty.containerLayout(mod)) {
989 .Auto => return error.IllDefinedMemoryLayout,985 .Auto => return error.IllDefinedMemoryLayout,
990 .Extern => {986 .Extern => {
991 const union_obj = mod.typeToUnion(ty).?;987 const union_size = ty.abiSize(mod);
992 const largest_field = mod.unionLargestField(union_obj);988 const array_ty = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
993 const field_size: usize = @intCast(largest_field.size);989 const val = try (try readFromMemory(array_ty, mod, buffer, arena)).intern(array_ty, mod);
994 const val = try (try readFromMemory(largest_field.ty, mod, buffer[0..field_size], arena)).intern(largest_field.ty, mod);990 return (try mod.intern(.{ .un = .{
995 return (try mod.intern(.{991 .ty = ty.toIntern(),
996 .un = .{992 .tag = .none,
997 .ty = ty.toIntern(),993 .val = val,
998 .tag = .undef,994 } })).toValue();
999 .val = val,
1000 },
1001 })).toValue();
1002 },995 },
1003 .Packed => {996 .Packed => {
1004 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;997 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
...@@ -1141,17 +1134,17 @@ pub const Value = struct {...@@ -1141,17 +1134,17 @@ pub const Value = struct {
1141 } })).toValue();1134 } })).toValue();
1142 },1135 },
1143 .Union => switch (ty.containerLayout(mod)) {1136 .Union => switch (ty.containerLayout(mod)) {
1144 .Auto => return error.IllDefinedMemoryLayout,1137 .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory
1145 .Extern => unreachable, // Handled by non-packed readFromMemory
1146 .Packed => {1138 .Packed => {
1147 const union_obj = mod.typeToUnion(ty).?;1139 const union_bits: u16 = @intCast(ty.bitSize(mod));
1148 const largest_field = mod.unionLargestField(union_obj);1140 // TODO: Remove after tests pass
1149 const un_tag_val = try mod.enumValueFieldIndex(union_obj.enum_tag_ty.toType(), largest_field.index);1141 assert(union_bits != 0);
1150 const un_val = try (try readFromPackedMemory(largest_field.ty, mod, buffer, bit_offset, arena)).intern(largest_field.ty, mod);1142 const int_ty = try mod.intType(.unsigned, union_bits);
1143 const val = (try readFromPackedMemory(int_ty, mod, buffer, bit_offset, arena)).toIntern();
1151 return (try mod.intern(.{ .un = .{1144 return (try mod.intern(.{ .un = .{
1152 .ty = ty.toIntern(),1145 .ty = ty.toIntern(),
1153 .tag = un_tag_val.ip_index,1146 .tag = .none,
1154 .val = un_val,1147 .val = val,
1155 } })).toValue();1148 } })).toValue();
1156 },1149 },
1157 },1150 },
test/behavior/comptime_memory.zig+43-14
...@@ -455,23 +455,52 @@ test "type pun null pointer-like optional" {...@@ -455,23 +455,52 @@ test "type pun null pointer-like optional" {
455}455}
456456
457test "reinterpret extern union" {457test "reinterpret extern union" {
458 const U = extern union {458 {
459 a: u32,459 const U = extern union {
460 b: u64,460 a: u32,
461 };461 b: u8 align(8),
462 };
462463
463 comptime var u: U = undefined;464 comptime var u: U = undefined;
464 comptime @memset(std.mem.asBytes(&u), 42);465 comptime @memset(std.mem.asBytes(&u), 42);
465 try testing.expectEqual(@as(u64, 0x2a2a2a2a_2a2a2a2a), u.b);466 try comptime testing.expect(0x2a2a2a2a == u.a);
467 try comptime testing.expect(42 == u.b);
468 try testing.expectEqual(@as(u32, 0x2a2a2a2a), u.a);
469 try testing.expectEqual(42, u.b);
470 }
466}471}
467472
468test "reinterpret packed union" {473test "reinterpret packed union" {
469 const U = packed union {474 {
470 a: u32,475 const U = packed union {
471 b: u64,476 a: u32,
472 };477 b: u8 align(8),
478 };
473479
474 comptime var u: U = undefined;480 comptime var u: U = undefined;
475 comptime @memset(std.mem.asBytes(&u), 42);481 comptime @memset(std.mem.asBytes(&u), 42);
476 try testing.expectEqual(@as(u64, 0x2a2a2a2a_2a2a2a2a), u.b);482 try comptime testing.expect(0x2a2a2a2a == u.a);
483 try comptime testing.expect(0x2a == u.b);
484 try testing.expectEqual(@as(u32, 0x2a2a2a2a), u.a);
485 try testing.expectEqual(0x2a, u.b);
486 }
487
488 {
489 const U = packed union {
490 a: u7,
491 b: u1,
492 };
493
494 const S = packed struct {
495 lsb: U,
496 msb: U,
497 };
498
499 comptime var s: S = undefined;
500 comptime @memset(std.mem.asBytes(&s), 0xaa);
501 try comptime testing.expectEqual(@as(u7, 0x2a), s.lsb.a);
502 try comptime testing.expectEqual(@as(u1, 0), s.lsb.b);
503 try comptime testing.expectEqual(@as(u7, 0x55), s.msb.a);
504 try comptime testing.expectEqual(@as(u1, 1), s.msb.b);
505 }
477}506}