| author | |
| committer | |
| log | 2fddd767ba20374e7677003c101e60f470c3804c |
| tree | 91ffbed5086771488201cebd82be6e1554ac14b5 |
| parent | ce919ccf45951856a762ffdb8ef850301cd8c588 |
10 files changed, 160 insertions(+), 14 deletions(-)
src/Module.zig+28| ... | ... | @@ -6607,6 +6607,7 @@ pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.UnionType, field_in |
| 6607 | 6607 | |
| 6608 | 6608 | pub fn unionTagFieldIndex(mod: *Module, u: InternPool.UnionType, enum_tag: Value) ?u32 { |
| 6609 | 6609 | const ip = &mod.intern_pool; |
| 6610 | if (enum_tag.toIntern() == .undef) return null; | |
| 6610 | 6611 | assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty); |
| 6611 | 6612 | const enum_type = ip.indexToKey(u.enum_tag_ty).enum_type; |
| 6612 | 6613 | return enum_type.tagValueIndex(ip, enum_tag.toIntern()); |
| ... | ... | @@ -6672,3 +6673,30 @@ pub fn structPackedFieldBitOffset( |
| 6672 | 6673 | } |
| 6673 | 6674 | unreachable; // index out of bounds |
| 6674 | 6675 | } |
| 6676 | ||
| 6677 | pub 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+13-3| ... | ... | @@ -29740,10 +29740,15 @@ fn storePtrVal( |
| 29740 | 29740 | error.OutOfMemory => return error.OutOfMemory, |
| 29741 | 29741 | error.ReinterpretDeclRef => unreachable, |
| 29742 | 29742 | error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already |
| 29743 | error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), | |
| 29743 | error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{operand_ty.fmt(mod)}), | |
| 29744 | 29744 | }; |
| 29745 | 29745 | |
| 29746 | reinterpret.val_ptr.* = (try (try Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena)).intern(mut_kit.ty, mod)).toValue(); | |
| 29746 | const val = Value.readFromMemory(mut_kit.ty, mod, buffer, sema.arena) catch |err| switch (err) { | |
| 29747 | error.OutOfMemory => return error.OutOfMemory, | |
| 29748 | error.IllDefinedMemoryLayout => unreachable, | |
| 29749 | error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{mut_kit.ty.fmt(mod)}), | |
| 29750 | }; | |
| 29751 | reinterpret.val_ptr.* = (try val.intern(mut_kit.ty, mod)).toValue(); | |
| 29747 | 29752 | }, |
| 29748 | 29753 | .bad_decl_ty, .bad_ptr_ty => { |
| 29749 | 29754 | // TODO show the decl declaration site in a note and explain whether the decl |
| ... | ... | @@ -30655,7 +30660,12 @@ fn bitCastVal( |
| 30655 | 30660 | error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already |
| 30656 | 30661 | error.Unimplemented => return sema.fail(block, src, "TODO: implement writeToMemory for type '{}'", .{old_ty.fmt(mod)}), |
| 30657 | 30662 | }; |
| 30658 | return try Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena); | |
| 30663 | ||
| 30664 | return Value.readFromMemory(new_ty, mod, buffer[buffer_offset..], sema.arena) catch |err| switch (err) { | |
| 30665 | error.OutOfMemory => return error.OutOfMemory, | |
| 30666 | error.IllDefinedMemoryLayout => unreachable, | |
| 30667 | error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{new_ty.fmt(mod)}), | |
| 30668 | }; | |
| 30659 | 30669 | } |
| 30660 | 30670 | |
| 30661 | 30671 | fn coerceArrayPtrToSlice( |
src/arch/wasm/CodeGen.zig+4-1| ... | ... | @@ -3259,7 +3259,10 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { |
| 3259 | 3259 | .un => |un| { |
| 3260 | 3260 | // in this case we have a packed union which will not be passed by reference. |
| 3261 | 3261 | const union_obj = mod.typeToUnion(ty).?; |
| 3262 | const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; | |
| 3262 | const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: { | |
| 3263 | assert(union_obj.getLayout(ip) == .Extern); | |
| 3264 | break :f mod.unionLargestField(union_obj).index; | |
| 3265 | }; | |
| 3263 | 3266 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); |
| 3264 | 3267 | return func.lowerConstant(un.val.toValue(), field_ty); |
| 3265 | 3268 | }, |
src/codegen.zig+5-1| ... | ... | @@ -583,7 +583,11 @@ pub fn generateSymbol( |
| 583 | 583 | } |
| 584 | 584 | |
| 585 | 585 | const union_obj = mod.typeToUnion(typed_value.ty).?; |
| 586 | const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod).?; | |
| 586 | const field_index = typed_value.ty.unionTagFieldIndex(un.tag.toValue(), mod) orelse f: { | |
| 587 | assert(union_obj.getLayout(ip) == .Extern); | |
| 588 | break :f mod.unionLargestField(union_obj).index; | |
| 589 | }; | |
| 590 | ||
| 587 | 591 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); |
| 588 | 592 | if (!field_ty.hasRuntimeBits(mod)) { |
| 589 | 593 | try code.appendNTimes(0xaa, math.cast(usize, layout.payload_size) orelse return error.Overflow); |
src/codegen/c.zig+4-1| ... | ... | @@ -1439,7 +1439,10 @@ pub const DeclGen = struct { |
| 1439 | 1439 | } |
| 1440 | 1440 | |
| 1441 | 1441 | const union_obj = mod.typeToUnion(ty).?; |
| 1442 | const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; | |
| 1442 | const field_i = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: { | |
| 1443 | assert(union_obj.getLayout(ip) == .Extern); | |
| 1444 | break :f mod.unionLargestField(union_obj).index; | |
| 1445 | }; | |
| 1443 | 1446 | const field_ty = union_obj.field_types.get(ip)[field_i].toType(); |
| 1444 | 1447 | const field_name = union_obj.field_names.get(ip)[field_i]; |
| 1445 | 1448 | if (union_obj.getLayout(ip) == .Packed) { |
src/codegen/llvm.zig+4-1| ... | ... | @@ -4108,7 +4108,10 @@ pub const Object = struct { |
| 4108 | 4108 | if (layout.payload_size == 0) return o.lowerValue(un.tag); |
| 4109 | 4109 | |
| 4110 | 4110 | const union_obj = mod.typeToUnion(ty).?; |
| 4111 | const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()).?; | |
| 4111 | const field_index = mod.unionTagFieldIndex(union_obj, un.tag.toValue()) orelse f: { | |
| 4112 | assert(union_obj.getLayout(ip) == .Extern); | |
| 4113 | break :f mod.unionLargestField(union_obj).index; | |
| 4114 | }; | |
| 4112 | 4115 | |
| 4113 | 4116 | const field_ty = union_obj.field_types.get(ip)[field_index].toType(); |
| 4114 | 4117 | if (union_obj.getLayout(ip) == .Packed) { |
src/codegen/spirv.zig+4-1| ... | ... | @@ -838,7 +838,10 @@ pub const DeclGen = struct { |
| 838 | 838 | return dg.todo("packed union constants", .{}); |
| 839 | 839 | } |
| 840 | 840 | |
| 841 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module).?; | |
| 841 | const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module) orelse f: { | |
| 842 | assert(union_obj.getLayout(ip) == .Extern); | |
| 843 | break :f mod.unionLargestField(union_obj).index; | |
| 844 | }; | |
| 842 | 845 | const active_field_ty = union_obj.field_types.get(ip)[active_field].toType(); |
| 843 | 846 | |
| 844 | 847 | const has_tag = layout.tag_size != 0; |
src/type.zig+6-2| ... | ... | @@ -1929,8 +1929,12 @@ pub const Type = struct { |
| 1929 | 1929 | pub fn unionFieldType(ty: Type, enum_tag: Value, mod: *Module) Type { |
| 1930 | 1930 | const ip = &mod.intern_pool; |
| 1931 | 1931 | const union_obj = mod.typeToUnion(ty).?; |
| 1932 | const index = mod.unionTagFieldIndex(union_obj, enum_tag).?; | |
| 1933 | return union_obj.field_types.get(ip)[index].toType(); | |
| 1932 | const union_fields = union_obj.field_types.get(ip); | |
| 1933 | if (mod.unionTagFieldIndex(union_obj, enum_tag)) |index| { | |
| 1934 | return union_fields[index].toType(); | |
| 1935 | } else { | |
| 1936 | return mod.unionLargestField(union_obj).ty; | |
| 1937 | } | |
| 1934 | 1938 | } |
| 1935 | 1939 | |
| 1936 | 1940 | pub fn unionTagFieldIndex(ty: Type, enum_tag: Value, mod: *Module) ?u32 { |
src/value.zig+69-4| ... | ... | @@ -704,7 +704,22 @@ pub const Value = struct { |
| 704 | 704 | }, |
| 705 | 705 | .Union => switch (ty.containerLayout(mod)) { |
| 706 | 706 | .Auto => return error.IllDefinedMemoryLayout, |
| 707 | .Extern => return error.Unimplemented, | |
| 707 | .Extern => { | |
| 708 | const union_obj = mod.typeToUnion(ty).?; | |
| 709 | const union_tag = val.unionTag(mod); | |
| 710 | ||
| 711 | const field_type, const field_index = if (mod.unionTagFieldIndex(union_obj, union_tag)) |field_index| .{ | |
| 712 | union_obj.field_types.get(&mod.intern_pool)[field_index].toType(), | |
| 713 | field_index, | |
| 714 | } else f: { | |
| 715 | const largest_field = mod.unionLargestField(union_obj); | |
| 716 | break :f .{ largest_field.ty, largest_field.index }; | |
| 717 | }; | |
| 718 | ||
| 719 | const field_val = try val.fieldValue(mod, field_index); | |
| 720 | const byte_count = @as(usize, @intCast(field_type.abiSize(mod))); | |
| 721 | return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]); | |
| 722 | }, | |
| 708 | 723 | .Packed => { |
| 709 | 724 | const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8; |
| 710 | 725 | return writeToPackedMemory(val, ty, mod, buffer[0..byte_count], 0); |
| ... | ... | @@ -856,7 +871,11 @@ pub const Value = struct { |
| 856 | 871 | mod: *Module, |
| 857 | 872 | buffer: []const u8, |
| 858 | 873 | arena: Allocator, |
| 859 | ) Allocator.Error!Value { | |
| 874 | ) error{ | |
| 875 | IllDefinedMemoryLayout, | |
| 876 | Unimplemented, | |
| 877 | OutOfMemory, | |
| 878 | }!Value { | |
| 860 | 879 | const ip = &mod.intern_pool; |
| 861 | 880 | const target = mod.getTarget(); |
| 862 | 881 | const endian = target.cpu.arch.endian(); |
| ... | ... | @@ -966,6 +985,26 @@ pub const Value = struct { |
| 966 | 985 | .name = name, |
| 967 | 986 | } })).toValue(); |
| 968 | 987 | }, |
| 988 | .Union => switch (ty.containerLayout(mod)) { | |
| 989 | .Auto => return error.IllDefinedMemoryLayout, | |
| 990 | .Extern => { | |
| 991 | const union_obj = mod.typeToUnion(ty).?; | |
| 992 | const largest_field = mod.unionLargestField(union_obj); | |
| 993 | const field_size: usize = @intCast(largest_field.size); | |
| 994 | const val = try (try readFromMemory(largest_field.ty, mod, buffer[0..field_size], arena)).intern(largest_field.ty, mod); | |
| 995 | return (try mod.intern(.{ | |
| 996 | .un = .{ | |
| 997 | .ty = ty.toIntern(), | |
| 998 | .tag = .undef, | |
| 999 | .val = val, | |
| 1000 | }, | |
| 1001 | })).toValue(); | |
| 1002 | }, | |
| 1003 | .Packed => { | |
| 1004 | const byte_count = (@as(usize, @intCast(ty.bitSize(mod))) + 7) / 8; | |
| 1005 | return readFromPackedMemory(ty, mod, buffer[0..byte_count], 0, arena); | |
| 1006 | }, | |
| 1007 | }, | |
| 969 | 1008 | .Pointer => { |
| 970 | 1009 | assert(!ty.isSlice(mod)); // No well defined layout. |
| 971 | 1010 | const int_val = try readFromMemory(Type.usize, mod, buffer, arena); |
| ... | ... | @@ -987,7 +1026,7 @@ pub const Value = struct { |
| 987 | 1026 | }, |
| 988 | 1027 | } })).toValue(); |
| 989 | 1028 | }, |
| 990 | else => @panic("TODO implement readFromMemory for more types"), | |
| 1029 | else => return error.Unimplemented, | |
| 991 | 1030 | } |
| 992 | 1031 | } |
| 993 | 1032 | |
| ... | ... | @@ -1001,7 +1040,10 @@ pub const Value = struct { |
| 1001 | 1040 | buffer: []const u8, |
| 1002 | 1041 | bit_offset: usize, |
| 1003 | 1042 | arena: Allocator, |
| 1004 | ) Allocator.Error!Value { | |
| 1043 | ) error{ | |
| 1044 | IllDefinedMemoryLayout, | |
| 1045 | OutOfMemory, | |
| 1046 | }!Value { | |
| 1005 | 1047 | const ip = &mod.intern_pool; |
| 1006 | 1048 | const target = mod.getTarget(); |
| 1007 | 1049 | const endian = target.cpu.arch.endian(); |
| ... | ... | @@ -1098,6 +1140,21 @@ pub const Value = struct { |
| 1098 | 1140 | .storage = .{ .elems = field_vals }, |
| 1099 | 1141 | } })).toValue(); |
| 1100 | 1142 | }, |
| 1143 | .Union => switch (ty.containerLayout(mod)) { | |
| 1144 | .Auto => return error.IllDefinedMemoryLayout, | |
| 1145 | .Extern => unreachable, // Handled by non-packed readFromMemory | |
| 1146 | .Packed => { | |
| 1147 | const union_obj = mod.typeToUnion(ty).?; | |
| 1148 | const largest_field = mod.unionLargestField(union_obj); | |
| 1149 | const un_tag_val = try mod.enumValueFieldIndex(union_obj.enum_tag_ty.toType(), largest_field.index); | |
| 1150 | const un_val = try (try readFromPackedMemory(largest_field.ty, mod, buffer, bit_offset, arena)).intern(largest_field.ty, mod); | |
| 1151 | return (try mod.intern(.{ .un = .{ | |
| 1152 | .ty = ty.toIntern(), | |
| 1153 | .tag = un_tag_val.ip_index, | |
| 1154 | .val = un_val, | |
| 1155 | } })).toValue(); | |
| 1156 | }, | |
| 1157 | }, | |
| 1101 | 1158 | .Pointer => { |
| 1102 | 1159 | assert(!ty.isSlice(mod)); // No well defined layout. |
| 1103 | 1160 | return readFromPackedMemory(Type.usize, mod, buffer, bit_offset, arena); |
| ... | ... | @@ -1713,6 +1770,14 @@ pub const Value = struct { |
| 1713 | 1770 | }; |
| 1714 | 1771 | } |
| 1715 | 1772 | |
| 1773 | pub fn unionValue(val: Value, mod: *Module) Value { | |
| 1774 | if (val.ip_index == .none) return val.castTag(.@"union").?.data.val; | |
| 1775 | return switch (mod.intern_pool.indexToKey(val.toIntern())) { | |
| 1776 | .un => |un| un.val.toValue(), | |
| 1777 | else => unreachable, | |
| 1778 | }; | |
| 1779 | } | |
| 1780 | ||
| 1716 | 1781 | /// Returns a pointer to the element value at the index. |
| 1717 | 1782 | pub fn elemPtr( |
| 1718 | 1783 | val: Value, |
test/behavior/comptime_memory.zig+23| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | const std = @import("std"); | |
| 1 | 2 | const builtin = @import("builtin"); |
| 2 | 3 | const endian = builtin.cpu.arch.endian(); |
| 3 | 4 | const testing = @import("std").testing; |
| ... | ... | @@ -452,3 +453,25 @@ test "type pun null pointer-like optional" { |
| 452 | 453 | // note that expectEqual hides the bug |
| 453 | 454 | try testing.expect(@as(*const ?*i8, @ptrCast(&p)).* == null); |
| 454 | 455 | } |
| 456 | ||
| 457 | test "reinterpret extern union" { | |
| 458 | const U = extern union { | |
| 459 | a: u32, | |
| 460 | b: u64, | |
| 461 | }; | |
| 462 | ||
| 463 | comptime var u: U = undefined; | |
| 464 | comptime @memset(std.mem.asBytes(&u), 42); | |
| 465 | try testing.expectEqual(@as(u64, 0x2a2a2a2a_2a2a2a2a), u.b); | |
| 466 | } | |
| 467 | ||
| 468 | test "reinterpret packed union" { | |
| 469 | const U = packed union { | |
| 470 | a: u32, | |
| 471 | b: u64, | |
| 472 | }; | |
| 473 | ||
| 474 | comptime var u: U = undefined; | |
| 475 | comptime @memset(std.mem.asBytes(&u), 42); | |
| 476 | try testing.expectEqual(@as(u64, 0x2a2a2a2a_2a2a2a2a), u.b); | |
| 477 | } |