authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-09-26 11:16:03+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-09-26 11:16:03+03:00
logf4c884617f499b52eaecc0ef674609c774052f8f
treec24e4628c314d5e815f4b9796d0ff70335fa58a3
parent2adb932ad6ee4ff3d3c640cb8fb7bf7db0ff5d74
parent9f4649b197b720dbc168ced25eee0805d3b678b1
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17215 from kcbanner/read_from_memory_union

sema: add support for unions in readFromMemory and writeToMemory

9 files changed, 256 insertions(+), 81 deletions(-)

src/InternPool.zig+4-2
...@@ -1103,7 +1103,10 @@ pub const Key = union(enum) {...@@ -1103,7 +1103,10 @@ pub const Key = union(enum) {
1103 pub const Union = extern struct {1103 pub const Union = extern struct {
1104 /// This is the union type; not the field type.1104 /// This is the union type; not the field type.
1105 ty: Index,1105 ty: Index,
1106 /// Indicates the active field.1106 /// 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.
1107 /// In those cases, the type of `val` is:
1108 /// extern: a u8 array of the same byte length as the union
1109 /// packed: an unsigned integer with the same bit size as the union
1107 tag: Index,1110 tag: Index,
1108 /// The value of the active field.1111 /// The value of the active field.
1109 val: Index,1112 val: Index,
...@@ -5128,7 +5131,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {...@@ -5128,7 +5131,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
51285131
5129 .un => |un| {5132 .un => |un| {
5130 assert(un.ty != .none);5133 assert(un.ty != .none);
5131 assert(un.tag != .none);
5132 assert(un.val != .none);5134 assert(un.val != .none);
5133 ip.items.appendAssumeCapacity(.{5135 ip.items.appendAssumeCapacity(.{
5134 .tag = .union_value,5136 .tag = .union_value,
src/Module.zig+2-1
...@@ -5825,7 +5825,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void {...@@ -5825,7 +5825,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void {
5825 .aggregate => |aggregate| for (aggregate.storage.values()) |elem|5825 .aggregate => |aggregate| for (aggregate.storage.values()) |elem|
5826 try mod.markReferencedDeclsAlive(elem.toValue()),5826 try mod.markReferencedDeclsAlive(elem.toValue()),
5827 .un => |un| {5827 .un => |un| {
5828 try mod.markReferencedDeclsAlive(un.tag.toValue());5828 if (un.tag != .none) try mod.markReferencedDeclsAlive(un.tag.toValue());
5829 try mod.markReferencedDeclsAlive(un.val.toValue());5829 try mod.markReferencedDeclsAlive(un.val.toValue());
5830 },5830 },
5831 else => {},5831 else => {},
...@@ -6609,6 +6609,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in...@@ -6609,6 +6609,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in
66096609
6610pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 {6610pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 {
6611 const ip = &mod.intern_pool;6611 const ip = &mod.intern_pool;
6612 if (enum_tag.toIntern() == .none) return null;
6612 assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty);6613 assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty);
6613 const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type;6614 const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type;
6614 return enum_type.tagValueIndex(ip, enum_tag.toIntern());6615 return enum_type.tagValueIndex(ip, enum_tag.toIntern());
src/Sema.zig+25-11
...@@ -3861,7 +3861,7 @@ fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Re...@@ -3861,7 +3861,7 @@ fn resolveComptimeKnownAllocValue(sema: *Sema, block: *Block, alloc: Air.Inst.Re
3861 const air_ptr_inst = Air.refToIndex(bin_op.lhs).?;3861 const air_ptr_inst = Air.refToIndex(bin_op.lhs).?;
3862 const tag_val = (try sema.resolveMaybeUndefVal(bin_op.rhs)).?;3862 const tag_val = (try sema.resolveMaybeUndefVal(bin_op.rhs)).?;
3863 const union_ty = sema.typeOf(bin_op.lhs).childType(mod);3863 const union_ty = sema.typeOf(bin_op.lhs).childType(mod);
3864 const payload_ty = union_ty.unionFieldType(tag_val, mod);3864 const payload_ty = union_ty.unionFieldType(tag_val, mod).?;
3865 if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_val| {3865 if (try sema.typeHasOnePossibleValue(payload_ty)) |payload_val| {
3866 const new_ptr = ptr_mapping.get(air_ptr_inst).?;3866 const new_ptr = ptr_mapping.get(air_ptr_inst).?;
3867 const store_val = try mod.unionValue(union_ty, tag_val, payload_val);3867 const store_val = try mod.unionValue(union_ty, tag_val, payload_val);
...@@ -11998,7 +11998,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -11998,7 +11998,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
1199811998
11999 const analyze_body = if (union_originally) blk: {11999 const analyze_body = if (union_originally) blk: {
12000 const item_val = sema.resolveConstLazyValue(block, .unneeded, item, undefined) catch unreachable;12000 const item_val = sema.resolveConstLazyValue(block, .unneeded, item, undefined) catch unreachable;
12001 const field_ty = maybe_union_ty.unionFieldType(item_val, mod);12001 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
12002 break :blk field_ty.zigTypeTag(mod) != .NoReturn;12002 break :blk field_ty.zigTypeTag(mod) != .NoReturn;
12003 } else true;12003 } else true;
1200412004
...@@ -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.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;12126 const item_val = sema.resolveConstValue(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
...@@ -12178,7 +12178,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12178,7 +12178,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
12178 const analyze_body = if (union_originally)12178 const analyze_body = if (union_originally)
12179 for (items) |item| {12179 for (items) |item| {
12180 const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;12180 const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable;
12181 const field_ty = maybe_union_ty.unionFieldType(item_val, mod);12181 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
12182 if (field_ty.zigTypeTag(mod) != .NoReturn) break true;12182 if (field_ty.zigTypeTag(mod) != .NoReturn) break true;
12183 } else false12183 } else false
12184 else12184 else
...@@ -12330,7 +12330,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r...@@ -12330,7 +12330,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
12330 case_block.wip_capture_scope = child_block.wip_capture_scope;12330 case_block.wip_capture_scope = child_block.wip_capture_scope;
1233112331
12332 const analyze_body = if (union_originally) blk: {12332 const analyze_body = if (union_originally) blk: {
12333 const field_ty = maybe_union_ty.unionFieldType(item_val, mod);12333 const field_ty = maybe_union_ty.unionFieldType(item_val, mod).?;
12334 break :blk field_ty.zigTypeTag(mod) != .NoReturn;12334 break :blk field_ty.zigTypeTag(mod) != .NoReturn;
12335 } else true;12335 } else true;
1233612336
...@@ -16370,7 +16370,7 @@ fn analyzeCmpUnionTag(...@@ -16370,7 +16370,7 @@ fn analyzeCmpUnionTag(
1637016370
16371 if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| {16371 if (try sema.resolveMaybeUndefVal(coerced_tag)) |enum_val| {
16372 if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool);16372 if (enum_val.isUndef(mod)) return mod.undefRef(Type.bool);
16373 const field_ty = union_ty.unionFieldType(enum_val, mod);16373 const field_ty = union_ty.unionFieldType(enum_val, mod).?;
16374 if (field_ty.zigTypeTag(mod) == .NoReturn) {16374 if (field_ty.zigTypeTag(mod) == .NoReturn) {
16375 return .bool_false;16375 return .bool_false;
16376 }16376 }
...@@ -27207,7 +27207,11 @@ fn unionFieldVal(...@@ -27207,7 +27207,11 @@ fn unionFieldVal(
27207 if (tag_matches) {27207 if (tag_matches) {
27208 return Air.internedToRef(un.val);27208 return Air.internedToRef(un.val);
27209 } else {27209 } else {
27210 const old_ty = union_ty.unionFieldType(un.tag.toValue(), mod);27210 const old_ty = if (un.tag == .none)
27211 ip.typeOf(un.val).toType()
27212 else
27213 union_ty.unionFieldType(un.tag.toValue(), mod).?;
27214
27211 if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| {27215 if (try sema.bitCastVal(block, src, un.val.toValue(), old_ty, field_ty, 0)) |new_val| {
27212 return Air.internedToRef(new_val.toIntern());27216 return Air.internedToRef(new_val.toIntern());
27213 }27217 }
...@@ -29733,10 +29737,15 @@ fn storePtrVal(...@@ -29733,10 +29737,15 @@ fn storePtrVal(
29733 error.OutOfMemory => return error.OutOfMemory,29737 error.OutOfMemory => return error.OutOfMemory,
29734 error.ReinterpretDeclRef => unreachable,29738 error.ReinterpretDeclRef => unreachable,
29735 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already29739 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
29736 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),29740 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{operand_ty.fmt(mod)}),
29737 };29741 };
2973829742
29739 reinterpret.val_ptr.* = (try (try Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena)).intern(mut_kit.ty, mod)).toValue();29743 const val = Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena) catch |err| switch (err) {
29744 error.OutOfMemory => return error.OutOfMemory,
29745 error.IllDefinedMemoryLayout => unreachable,
29746 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
29747 };
29748 reinterpret.val_ptr.* = (try val.intern(mut_kit.ty, mod)).toValue();
29740 },29749 },
29741 .bad_decl_ty, .bad_ptr_ty => {29750 .bad_decl_ty, .bad_ptr_ty => {
29742 // TODO show the decl declaration site in a note and explain whether the decl29751 // TODO show the decl declaration site in a note and explain whether the decl
...@@ -30648,7 +30657,12 @@ fn bitCastVal(...@@ -30648,7 +30657,12 @@ fn bitCastVal(
30648 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already30657 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
30649 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}),30658 error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}),
30650 };30659 };
30651 return try Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena);30660
30661 return Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena) catch |err| switch (err) {
30662 error.OutOfMemory => return error.OutOfMemory,
30663 error.IllDefinedMemoryLayout => unreachable,
30664 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{new_ty.fmt(mod)}),
30665 };
30652}30666}
3065330667
30654fn coerceArrayPtrToSlice(30668fn coerceArrayPtrToSlice(
...@@ -32858,7 +32872,7 @@ fn unionToTag(...@@ -32858,7 +32872,7 @@ fn unionToTag(
32858 return Air.internedToRef(opv.toIntern());32872 return Air.internedToRef(opv.toIntern());
32859 }32873 }
32860 if (try sema.resolveMaybeUndefVal(un)) |un_val| {32874 if (try sema.resolveMaybeUndefVal(un)) |un_val| {
32861 return Air.internedToRef(un_val.unionTag(mod).toIntern());32875 return Air.internedToRef(un_val.unionTag(mod).?.toIntern());
32862 }32876 }
32863 try sema.requireRuntimeBlock(block, un_src, null);32877 try sema.requireRuntimeBlock(block, un_src, null);
32864 return block.addTyOp(.get_union_tag, enum_ty, un);32878 return block.addTyOp(.get_union_tag, enum_ty, un);
src/TypedValue.zig+28-18
...@@ -87,15 +87,20 @@ pub fn print(...@@ -87,15 +87,20 @@ pub fn print(
87 const union_val = val.castTag(.@"union").?.data;87 const union_val = val.castTag(.@"union").?.data;
88 try writer.writeAll(".{ ");88 try writer.writeAll(".{ ");
8989
90 try print(.{90 if (union_val.tag.toIntern() != .none) {
91 .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(),91 try print(.{
92 .val = union_val.tag,92 .ty = ip.indexToKey(ty.toIntern()).union_type.enum_tag_ty.toType(),
93 }, writer, level - 1, mod);93 .val = union_val.tag,
94 try writer.writeAll(" = ");94 }, writer, level - 1, mod);
95 try print(.{95 try writer.writeAll(" = ");
96 .ty = ty.unionFieldType(union_val.tag, mod),96 const field_ty = ty.unionFieldType(union_val.tag, mod).?;
97 .val = union_val.val,97 try print(.{
98 }, writer, level - 1, mod);98 .ty = field_ty,
99 .val = union_val.val,
100 }, writer, level - 1, mod);
101 } else {
102 return writer.writeAll("(unknown tag)");
103 }
99104
100 return writer.writeAll(" }");105 return writer.writeAll(" }");
101 },106 },
...@@ -404,15 +409,20 @@ pub fn print(...@@ -404,15 +409,20 @@ pub fn print(
404 .un => |un| {409 .un => |un| {
405 try writer.writeAll(".{ ");410 try writer.writeAll(".{ ");
406 if (level > 0) {411 if (level > 0) {
407 try print(.{412 if (un.tag != .none) {
408 .ty = ty.unionTagTypeHypothetical(mod),413 try print(.{
409 .val = un.tag.toValue(),414 .ty = ty.unionTagTypeHypothetical(mod),
410 }, writer, level - 1, mod);415 .val = un.tag.toValue(),
411 try writer.writeAll(" = ");416 }, writer, level - 1, mod);
412 try print(.{417 try writer.writeAll(" = ");
413 .ty = ty.unionFieldType(un.tag.toValue(), mod),418 const field_ty = ty.unionFieldType(un.tag.toValue(), mod).?;
414 .val = un.val.toValue(),419 try print(.{
415 }, writer, level - 1, mod);420 .ty = field_ty,
421 .val = un.val.toValue(),
422 }, writer, level - 1, mod);
423 } else {
424 try writer.writeAll("(unknown tag)");
425 }
416 } else try writer.writeAll("...");426 } else try writer.writeAll("...");
417 return writer.writeAll(" }");427 return writer.writeAll(" }");
418 },428 },
src/codegen.zig+20-10
...@@ -583,23 +583,33 @@ pub fn generateSymbol(...@@ -583,23 +583,33 @@ 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).?;586 if (un.tag != .none) {
587 const field_ty = union_obj.field_types.get(ip)[field_index].toType();587 const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?;
588 if (!field_ty.hasRuntimeBits(mod)) {588 const field_ty = union_obj.field_types.get(ip)[field_index].toType();
589 try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);589 if (!field_ty.hasRuntimeBits(mod)) {
590 try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow);
591 } else {
592 switch (try generateSymbol(bin_file, src_loc, .{
593 .ty = field_ty,
594 .val = un.val.toValue(),
595 }, code, debug_output, reloc_info)) {
596 .ok => {},
597 .fail => |em| return Result{ .fail = em },
598 }
599
600 const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(mod)) orelse return error.Overflow;
601 if (padding > 0) {
602 try code.appendNTimes(0, padding);
603 }
604 }
590 } else {605 } else {
591 switch (try generateSymbol(bin_file, src_loc, .{606 switch (try generateSymbol(bin_file, src_loc, .{
592 .ty = field_ty,607 .ty = ip.typeOf(un.val).toType(),
593 .val = un.val.toValue(),608 .val = un.val.toValue(),
594 }, code, debug_output, reloc_info)) {609 }, code, debug_output, reloc_info)) {
595 .ok => {},610 .ok => {},
596 .fail => |em| return Result{ .fail = em },611 .fail => |em| return Result{ .fail = em },
597 }612 }
598
599 const padding = math.cast(usize, layout.payload_size - field_ty.abiSize(mod)) orelse return error.Overflow;
600 if (padding > 0) {
601 try code.appendNTimes(0, padding);
602 }
603 }613 }
604614
605 if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {615 if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) {
src/codegen/llvm.zig+36-19
...@@ -4108,25 +4108,28 @@ pub const Object = struct {...@@ -4108,25 +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()).?;4111 const container_layout = union_obj.getLayout(ip);
41124112
4113 const field_ty = union_obj.field_types.get(ip)[field_index].toType();4113 var need_unnamed = false;
4114 if (union_obj.getLayout(ip) == .Packed) {4114 const payload = if (un.tag != .none) p: {
4115 if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0);4115 const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?;
4116 const small_int_val = try o.builder.castConst(4116 const field_ty = union_obj.field_types.get(ip)[field_index].toType();
4117 if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast,4117 if (container_layout == .Packed) {
4118 try o.lowerValue(un.val),4118 if (!field_ty.hasRuntimeBits(mod)) return o.builder.intConst(union_ty, 0);
4119 try o.builder.intType(@intCast(field_ty.bitSize(mod))),4119 const small_int_val = try o.builder.castConst(
4120 );4120 if (field_ty.isPtrAtRuntime(mod)) .ptrtoint else .bitcast,
4121 return o.builder.convConst(.unsigned, small_int_val, union_ty);4121 try o.lowerValue(un.val),
4122 }4122 try o.builder.intType(@intCast(field_ty.bitSize(mod))),
4123 );
4124 return o.builder.convConst(.unsigned, small_int_val, union_ty);
4125 }
4126
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 need_unnamed = layout.most_aligned_field != field_index;
41234132
4124 // Sometimes we must make an unnamed struct because LLVM does
4125 // not support bitcasting our payload struct to the true union payload type.
4126 // Instead we use an unnamed struct and every reference to the global
4127 // must pointer cast to the expected type before accessing the union.
4128 var need_unnamed = layout.most_aligned_field != field_index;
4129 const payload = p: {
4130 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {4133 if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) {
4131 const padding_len = layout.payload_size;4134 const padding_len = layout.payload_size;
4132 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));
...@@ -4144,9 +4147,23 @@ pub const Object = struct {...@@ -4144,9 +4147,23 @@ pub const Object = struct {
4144 try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }),4147 try o.builder.structType(.@"packed", &.{ payload_ty, padding_ty }),
4145 &.{ payload, try o.builder.undefConst(padding_ty) },4148 &.{ payload, try o.builder.undefConst(padding_ty) },
4146 );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;
4147 };4164 };
4148 const payload_ty = payload.typeOf(&o.builder);
41494165
4166 const payload_ty = payload.typeOf(&o.builder);
4150 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)
4151 try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty})4168 try o.builder.structType(union_ty.structKind(&o.builder), &.{payload_ty})
4152 else4169 else
src/type.zig+11-5
...@@ -1647,8 +1647,12 @@ pub const Type = struct {...@@ -1647,8 +1647,12 @@ pub const Type = struct {
1647 },1647 },
16481648
1649 .union_type => |union_type| {1649 .union_type => |union_type| {
1650 if (opt_sema) |sema| try sema.resolveTypeFields(ty);1650 const is_packed = ty.containerLayout(mod) == .Packed;
1651 if (ty.containerLayout(mod) != .Packed) {1651 if (opt_sema) |sema| {
1652 try sema.resolveTypeFields(ty);
1653 if (is_packed) try sema.resolveTypeLayout(ty);
1654 }
1655 if (!is_packed) {
1652 return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8;1656 return (try ty.abiSizeAdvanced(mod, strat)).scalar * 8;
1653 }1657 }
1654 const union_obj = ip.loadUnionType(union_type);1658 const union_obj = ip.loadUnionType(union_type);
...@@ -1659,6 +1663,7 @@ pub const Type = struct {...@@ -1659,6 +1663,7 @@ pub const Type = struct {
1659 const field_ty = union_obj.field_types.get(ip)[field_index];1663 const field_ty = union_obj.field_types.get(ip)[field_index];
1660 size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema));1664 size = @max(size, try bitSizeAdvanced(field_ty.toType(), mod, opt_sema));
1661 }1665 }
1666
1662 return size;1667 return size;
1663 },1668 },
1664 .opaque_type => unreachable,1669 .opaque_type => unreachable,
...@@ -1927,11 +1932,12 @@ pub const Type = struct {...@@ -1927,11 +1932,12 @@ pub const Type = struct {
1927 return union_obj.enum_tag_ty.toType();1932 return union_obj.enum_tag_ty.toType();
1928 }1933 }
19291934
1930 pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type {1935 pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) ?Type {
1931 const ip = &mod.intern_pool;1936 const ip = &mod.intern_pool;
1932 const union_obj = mod.typeToUnion(ty).?;1937 const union_obj = mod.typeToUnion(ty).?;
1933 const index = mod.unionTagFieldIndex(union_obj, enum_tag).?;1938 const union_fields = union_obj.field_types.get(ip);
1934 return union_obj.field_types.get(ip)[index].toType();1939 const index = mod.unionTagFieldIndex(union_obj, enum_tag) orelse return null;
1940 return union_fields[index].toType();
1935 }1941 }
19361942
1937 pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 {1943 pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 {
src/value.zig+78-15
...@@ -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,8 +703,21 @@ pub const Value = struct {...@@ -703,8 +703,21 @@ 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 => return error.Unimplemented,707 .Extern => {
708 const union_obj = mod.typeToUnion(ty).?;
709 if (val.unionTag(mod)) |union_tag| {
710 const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
711 const field_type = union_obj.field_types.get(&mod.intern_pool)[field_index].toType();
712 const field_val = try val.fieldValue(mod, field_index);
713 const byte_count = @as(usize, @intCast(field_type.abiSize(mod)));
714 return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]);
715 } else {
716 const union_size = ty.abiSize(mod);
717 const array_type = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
718 return writeToMemory(val.unionValue(mod), array_type, mod, buffer[0..@as(usize, @intCast(union_size))]);
719 }
720 },
708 .Packed => {721 .Packed => {
709 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;722 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
710 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);723 return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0);
...@@ -817,14 +830,18 @@ pub const Value = struct {...@@ -817,14 +830,18 @@ pub const Value = struct {
817 .Union => {830 .Union => {
818 const union_obj = mod.typeToUnion(ty).?;831 const union_obj = mod.typeToUnion(ty).?;
819 switch (union_obj.getLayout(ip)) {832 switch (union_obj.getLayout(ip)) {
820 .Auto => unreachable, // Sema is supposed to have emitted a compile error already833 .Auto, .Extern => unreachable, // Handled in non-packed writeToMemory
821 .Extern => unreachable, // Handled in non-packed writeToMemory
822 .Packed => {834 .Packed => {
823 const field_index = mod.unionTagFieldIndex(union_obj, val.unionTag(mod)).?;835 if (val.unionTag(mod)) |union_tag| {
824 const field_type = union_obj.field_types.get(ip)[field_index].toType();836 const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?;
825 const field_val = try val.fieldValue(mod, field_index);837 const field_type = union_obj.field_types.get(ip)[field_index].toType();
826838 const field_val = try val.fieldValue(mod, field_index);
827 return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);839 return field_val.writeToPackedMemory(field_type, mod, buffer, bit_offset);
840 } else {
841 const union_bits: u16 = @intCast(ty.bitSize(mod));
842 const int_ty = try mod.intType(.unsigned, union_bits);
843 return val.unionValue(mod).writeToPackedMemory(int_ty, mod, buffer, bit_offset);
844 }
828 },845 },
829 }846 }
830 },847 },
...@@ -856,7 +873,11 @@ pub const Value = struct {...@@ -856,7 +873,11 @@ pub const Value = struct {
856 mod: *Module,873 mod: *Module,
857 buffer: []const u8,874 buffer: []const u8,
858 arena: Allocator,875 arena: Allocator,
859 ) Allocator.Error!Value {876 ) error{
877 IllDefinedMemoryLayout,
878 Unimplemented,
879 OutOfMemory,
880 }!Value {
860 const ip = &mod.intern_pool;881 const ip = &mod.intern_pool;
861 const target = mod.getTarget();882 const target = mod.getTarget();
862 const endian = target.cpu.arch.endian();883 const endian = target.cpu.arch.endian();
...@@ -966,6 +987,23 @@ pub const Value = struct {...@@ -966,6 +987,23 @@ pub const Value = struct {
966 .name = name,987 .name = name,
967 } })).toValue();988 } })).toValue();
968 },989 },
990 .Union => switch (ty.containerLayout(mod)) {
991 .Auto => return error.IllDefinedMemoryLayout,
992 .Extern => {
993 const union_size = ty.abiSize(mod);
994 const array_ty = try mod.arrayType(.{ .len = union_size, .child = .u8_type });
995 const val = try (try readFromMemory(array_ty, mod, buffer, arena)).intern(array_ty, mod);
996 return (try mod.intern(.{ .un = .{
997 .ty = ty.toIntern(),
998 .tag = .none,
999 .val = val,
1000 } })).toValue();
1001 },
1002 .Packed => {
1003 const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8;
1004 return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena);
1005 },
1006 },
969 .Pointer => {1007 .Pointer => {
970 assert(!ty.isSlice(mod)); // No well defined layout.1008 assert(!ty.isSlice(mod)); // No well defined layout.
971 const int_val = try readFromMemory(Type.usize, mod, buffer, arena);1009 const int_val = try readFromMemory(Type.usize, mod, buffer, arena);
...@@ -987,7 +1025,7 @@ pub const Value = struct {...@@ -987,7 +1025,7 @@ pub const Value = struct {
987 },1025 },
988 } })).toValue();1026 } })).toValue();
989 },1027 },
990 else => @panic("TODO implement readFromMemory for more types"),1028 else => return error.Unimplemented,
991 }1029 }
992 }1030 }
9931031
...@@ -1001,7 +1039,10 @@ pub const Value = struct {...@@ -1001,7 +1039,10 @@ pub const Value = struct {
1001 buffer: []const u8,1039 buffer: []const u8,
1002 bit_offset: usize,1040 bit_offset: usize,
1003 arena: Allocator,1041 arena: Allocator,
1004 ) Allocator.Error!Value {1042 ) error{
1043 IllDefinedMemoryLayout,
1044 OutOfMemory,
1045 }!Value {
1005 const ip = &mod.intern_pool;1046 const ip = &mod.intern_pool;
1006 const target = mod.getTarget();1047 const target = mod.getTarget();
1007 const endian = target.cpu.arch.endian();1048 const endian = target.cpu.arch.endian();
...@@ -1098,6 +1139,20 @@ pub const Value = struct {...@@ -1098,6 +1139,20 @@ pub const Value = struct {
1098 .storage = .{ .elems = field_vals },1139 .storage = .{ .elems = field_vals },
1099 } })).toValue();1140 } })).toValue();
1100 },1141 },
1142 .Union => switch (ty.containerLayout(mod)) {
1143 .Auto, .Extern => unreachable, // Handled by non-packed readFromMemory
1144 .Packed => {
1145 const union_bits: u16 = @intCast(ty.bitSize(mod));
1146 assert(union_bits != 0);
1147 const int_ty = try mod.intType(.unsigned, union_bits);
1148 const val = (try readFromPackedMemory(int_ty, mod, buffer, bit_offset, arena)).toIntern();
1149 return (try mod.intern(.{ .un = .{
1150 .ty = ty.toIntern(),
1151 .tag = .none,
1152 .val = val,
1153 } })).toValue();
1154 },
1155 },
1101 .Pointer => {1156 .Pointer => {
1102 assert(!ty.isSlice(mod)); // No well defined layout.1157 assert(!ty.isSlice(mod)); // No well defined layout.
1103 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);1158 return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena);
...@@ -1704,11 +1759,19 @@ pub const Value = struct {...@@ -1704,11 +1759,19 @@ pub const Value = struct {
1704 };1759 };
1705 }1760 }
17061761
1707 pub fn unionTag(val: Value, mod: *Module) Value {1762 pub fn unionTag(val: Value, mod: *Module) ?Value {
1708 if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag;1763 if (val.ip_index == .none) return val.castTag(.@"union").?.data.tag;
1709 return switch (mod.intern_pool.indexToKey(val.toIntern())) {1764 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1710 .undef, .enum_tag => val,1765 .undef, .enum_tag => val,
1711 .un => |un| un.tag.toValue(),1766 .un => |un| if (un.tag != .none) un.tag.toValue() else return null,
1767 else => unreachable,
1768 };
1769 }
1770
1771 pub fn unionValue(val: Value, mod: *Module) Value {
1772 if (val.ip_index == .none) return val.castTag(.@"union").?.data.val;
1773 return switch (mod.intern_pool.indexToKey(val.toIntern())) {
1774 .un => |un| un.val.toValue(),
1712 else => unreachable,1775 else => unreachable,
1713 };1776 };
1714 }1777 }
test/behavior/comptime_memory.zig+52
...@@ -1,3 +1,4 @@...@@ -1,3 +1,4 @@
1const std = @import("std");
1const builtin = @import("builtin");2const builtin = @import("builtin");
2const endian = builtin.cpu.arch.endian();3const endian = builtin.cpu.arch.endian();
3const testing = @import("std").testing;4const testing = @import("std").testing;
...@@ -454,3 +455,54 @@ test "type pun null pointer-like optional" {...@@ -454,3 +455,54 @@ test "type pun null pointer-like optional" {
454 // note that expectEqual hides the bug455 // note that expectEqual hides the bug
455 try testing.expect(@as(*const ?*i8, @ptrCast(&p)).* == null);456 try testing.expect(@as(*const ?*i8, @ptrCast(&p)).* == null);
456}457}
458
459test "reinterpret extern union" {
460 {
461 const U = extern union {
462 a: u32,
463 b: u8 align(8),
464 };
465
466 comptime var u: U = undefined;
467 comptime @memset(std.mem.asBytes(&u), 42);
468 try comptime testing.expect(0x2a2a2a2a == u.a);
469 try comptime testing.expect(42 == u.b);
470 try testing.expectEqual(@as(u32, 0x2a2a2a2a), u.a);
471 try testing.expectEqual(42, u.b);
472 }
473}
474
475test "reinterpret packed union" {
476 {
477 const U = packed union {
478 a: u32,
479 b: u8 align(8),
480 };
481
482 comptime var u: U = undefined;
483 comptime @memset(std.mem.asBytes(&u), 42);
484 try comptime testing.expect(0x2a2a2a2a == u.a);
485 try comptime testing.expect(0x2a == u.b);
486 try testing.expectEqual(@as(u32, 0x2a2a2a2a), u.a);
487 try testing.expectEqual(0x2a, u.b);
488 }
489
490 {
491 const U = packed union {
492 a: u7,
493 b: u1,
494 };
495
496 const S = packed struct {
497 lsb: U,
498 msb: U,
499 };
500
501 comptime var s: S = undefined;
502 comptime @memset(std.mem.asBytes(&s), 0xaa);
503 try comptime testing.expectEqual(@as(u7, 0x2a), s.lsb.a);
504 try comptime testing.expectEqual(@as(u1, 0), s.lsb.b);
505 try comptime testing.expectEqual(@as(u7, 0x55), s.msb.a);
506 try comptime testing.expectEqual(@as(u1, 1), s.msb.b);
507 }
508}