authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-03-25 23:08:59+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2024-03-26 13:48:06+00:00
log5ec6e3036b2772e6efc08726b22c560dedd556bc
tree9c4625688ff540c0cd9bece36bef96276da08842
parentc6f3e9d79cf849623e6c4f25e02e17cdfab07b7c
signaturelock-open Commit is signed but in an unrecognized format.

Sema: introduce separate `MutableValue` representation for comptime-mutable memory

Perhaps someday, we will make Sema operate on mutable values more generally. For now, it makes sense to split out this representation, since it is only used in comptime pointer accesses. There are some currently unused methods on `MutableValue` which will be used once I rewrite the comptime pointer access logic to be less terrible. The commit following this one will - at long last - delete the legacy Value representation

4 files changed, 734 insertions(+), 441 deletions(-)

src/Module.zig+1-1
......@@ -3598,7 +3598,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
35983598
35993599 const old_has_tv = decl.has_tv;
36003600 // The following values are ignored if `!old_has_tv`
3601 const old_ty = decl.typeOf(mod);
3601 const old_ty = if (old_has_tv) decl.typeOf(mod) else undefined;
36023602 const old_val = decl.val;
36033603 const old_align = decl.alignment;
36043604 const old_linksection = decl.@"linksection";
src/Sema.zig+219-438
......@@ -139,8 +139,7 @@ const MaybeComptimeAlloc = struct {
139139};
140140
141141const ComptimeAlloc = struct {
142 ty: Type,
143 val: Value,
142 val: MutableValue,
144143 is_const: bool,
145144 /// `.none` indicates that the alignment is the natural alignment of `val`.
146145 alignment: Alignment,
......@@ -153,8 +152,7 @@ const ComptimeAlloc = struct {
153152fn newComptimeAlloc(sema: *Sema, block: *Block, ty: Type, alignment: Alignment) !ComptimeAllocIndex {
154153 const idx = sema.comptime_allocs.items.len;
155154 try sema.comptime_allocs.append(sema.gpa, .{
156 .ty = ty,
157 .val = Value.fromInterned(try sema.mod.intern(.{ .undef = ty.toIntern() })),
155 .val = .{ .interned = try sema.mod.intern(.{ .undef = ty.toIntern() }) },
158156 .is_const = false,
159157 .alignment = alignment,
160158 .runtime_index = block.runtime_index,
......@@ -175,6 +173,7 @@ const log = std.log.scoped(.sema);
175173
176174const Sema = @This();
177175const Value = @import("Value.zig");
176const MutableValue = @import("mutable_value.zig").MutableValue;
178177const Type = @import("type.zig").Type;
179178const TypedValue = @import("TypedValue.zig");
180179const Air = @import("Air.zig");
......@@ -3762,10 +3761,10 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
37623761 if (!sema.isComptimeMutablePtr(ptr_val)) break :already_ct;
37633762 const alloc_index = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr.comptime_alloc;
37643763 const ct_alloc = sema.getComptimeAlloc(alloc_index);
3765 const interned = try ct_alloc.val.intern(ct_alloc.ty, mod);
3764 const interned = try ct_alloc.val.intern(mod, sema.arena);
37663765 if (Value.fromInterned(interned).canMutateComptimeVarState(mod)) {
37673766 // Preserve the comptime alloc, just make the pointer const.
3768 ct_alloc.val = Value.fromInterned(interned);
3767 ct_alloc.val = .{ .interned = interned };
37693768 ct_alloc.is_const = true;
37703769 return sema.makePtrConst(block, alloc);
37713770 } else {
......@@ -4030,7 +4029,7 @@ fn finishResolveComptimeKnownAllocPtr(
40304029 const alloc_index = existing_comptime_alloc orelse a: {
40314030 const idx = try sema.newComptimeAlloc(block, alloc_ty.childType(zcu), alloc_ty.ptrAlignment(zcu));
40324031 const alloc = sema.getComptimeAlloc(idx);
4033 alloc.val = Value.fromInterned(result_val);
4032 alloc.val = .{ .interned = result_val };
40344033 break :a idx;
40354034 };
40364035 sema.getComptimeAlloc(alloc_index).is_const = true;
......@@ -4193,7 +4192,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
41934192 .anon_decl => |a| a.val,
41944193 .comptime_alloc => |i| val: {
41954194 const alloc = sema.getComptimeAlloc(i);
4196 break :val try alloc.val.intern(alloc.ty, mod);
4195 break :val try alloc.val.intern(mod, sema.arena);
41974196 },
41984197 else => unreachable,
41994198 };
......@@ -5597,7 +5596,7 @@ fn storeToInferredAllocComptime(
55975596 } });
55985597 } else {
55995598 const alloc_index = try sema.newComptimeAlloc(block, operand_ty, iac.alignment);
5600 sema.getComptimeAlloc(alloc_index).val = operand_val;
5599 sema.getComptimeAlloc(alloc_index).val = .{ .interned = operand_val.toIntern() };
56015600 iac.ptr = try zcu.intern(.{ .ptr = .{
56025601 .ty = alloc_ty.toIntern(),
56035602 .addr = .{ .comptime_alloc = alloc_index },
......@@ -30655,21 +30654,22 @@ fn storePtrVal(
3065530654 .opv => {},
3065630655 .direct => |val_ptr| {
3065730656 if (mut_kit.root == .comptime_field) {
30658 val_ptr.* = Value.fromInterned((try val_ptr.intern(operand_ty, mod)));
30659 if (!operand_val.eql(val_ptr.*, operand_ty, mod)) {
30657 val_ptr.* = .{ .interned = try val_ptr.intern(mod, sema.arena) };
30658 if (operand_val.toIntern() != val_ptr.interned) {
3066030659 // TODO use failWithInvalidComptimeFieldStore
3066130660 return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
3066230661 }
3066330662 return;
3066430663 }
30665 val_ptr.* = Value.fromInterned((try operand_val.intern(operand_ty, mod)));
30664 val_ptr.* = .{ .interned = operand_val.toIntern() };
3066630665 },
3066730666 .reinterpret => |reinterpret| {
3066830667 try sema.resolveTypeLayout(mut_kit.ty);
3066930668 const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod));
3067030669 const buffer = try sema.gpa.alloc(u8, abi_size);
3067130670 defer sema.gpa.free(buffer);
30672 reinterpret.val_ptr.*.writeToMemory(mut_kit.ty, mod, buffer) catch |err| switch (err) {
30671 const interned_old = Value.fromInterned(try reinterpret.val_ptr.intern(mod, sema.arena));
30672 interned_old.writeToMemory(mut_kit.ty, mod, buffer) catch |err| switch (err) {
3067330673 error.OutOfMemory => return error.OutOfMemory,
3067430674 error.ReinterpretDeclRef => unreachable,
3067530675 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
......@@ -30693,7 +30693,7 @@ fn storePtrVal(
3069330693 error.IllDefinedMemoryLayout => unreachable,
3069430694 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
3069530695 };
30696 reinterpret.val_ptr.* = Value.fromInterned((try val.intern(mut_kit.ty, mod)));
30696 reinterpret.val_ptr.* = .{ .interned = val.toIntern() };
3069730697 },
3069830698 .bad_decl_ty, .bad_ptr_ty => {
3069930699 // TODO show the decl declaration site in a note and explain whether the decl
......@@ -30718,11 +30718,11 @@ const ComptimePtrMutationKit = struct {
3071830718 opv,
3071930719 /// The pointer type matches the actual comptime Value so a direct
3072030720 /// modification is possible.
30721 direct: *Value,
30721 direct: *MutableValue,
3072230722 /// The largest parent Value containing pointee and having a well-defined memory layout.
3072330723 /// This is used for bitcasting, if direct dereferencing failed.
3072430724 reinterpret: struct {
30725 val_ptr: *Value,
30725 val_ptr: *MutableValue,
3072630726 byte_offset: usize,
3072730727 /// If set, write the operand to packed memory
3072830728 write_packed: bool = false,
......@@ -30754,15 +30754,15 @@ fn beginComptimePtrMutation(
3075430754 .decl, .anon_decl, .int => unreachable, // isComptimeMutablePtr has been checked already
3075530755 .comptime_alloc => |alloc_index| {
3075630756 const alloc = sema.getComptimeAlloc(alloc_index);
30757 return sema.beginComptimePtrMutationInner(block, src, alloc.ty, &alloc.val, ptr_elem_ty, .{ .alloc = alloc_index });
30757 return sema.beginComptimePtrMutationInner(block, src, alloc.val.typeOf(mod), &alloc.val, ptr_elem_ty, .{ .alloc = alloc_index });
3075830758 },
3075930759 .comptime_field => |comptime_field| {
30760 const duped = try sema.arena.create(Value);
30761 duped.* = Value.fromInterned(comptime_field);
30760 const duped = try sema.arena.create(MutableValue);
30761 duped.* = .{ .interned = comptime_field };
3076230762 return sema.beginComptimePtrMutationInner(
3076330763 block,
3076430764 src,
30765 Type.fromInterned(mod.intern_pool.typeOf(comptime_field)),
30765 duped.typeOf(mod),
3076630766 duped,
3076730767 ptr_elem_ty,
3076830768 .comptime_field,
......@@ -30775,36 +30775,28 @@ fn beginComptimePtrMutation(
3077530775 .opv => unreachable,
3077630776 .direct => |val_ptr| {
3077730777 const payload_ty = parent.ty.errorUnionPayload(mod);
30778 if (val_ptr.ip_index == .none and val_ptr.tag() == .eu_payload) {
30779 return ComptimePtrMutationKit{
30780 .root = parent.root,
30781 .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data },
30782 .ty = payload_ty,
30783 };
30784 } else {
30778 try val_ptr.unintern(mod, sema.arena, false, false);
30779 if (val_ptr.* == .interned) {
3078530780 // An error union has been initialized to undefined at comptime and now we
3078630781 // are for the first time setting the payload. We must change the
30787 // representation of the error union from `undef` to `opt_payload`.
30788
30789 const payload = try sema.arena.create(Value.Payload.SubValue);
30790 payload.* = .{
30791 .base = .{ .tag = .eu_payload },
30792 .data = Value.fromInterned((try mod.intern(.{ .undef = payload_ty.toIntern() }))),
30793 };
30794
30795 val_ptr.* = Value.initPayload(&payload.base);
30796
30797 return ComptimePtrMutationKit{
30798 .root = parent.root,
30799 .pointee = .{ .direct = &payload.data },
30800 .ty = payload_ty,
30801 };
30782 // representation of the error union to `eu_payload`.
30783 const child = try sema.arena.create(MutableValue);
30784 child.* = .{ .interned = try mod.intern(.{ .undef = payload_ty.toIntern() }) };
30785 val_ptr.* = .{ .eu_payload = .{
30786 .ty = parent.ty.toIntern(),
30787 .child = child,
30788 } };
3080230789 }
30790 return .{
30791 .root = parent.root,
30792 .pointee = .{ .direct = val_ptr.eu_payload.child },
30793 .ty = payload_ty,
30794 };
3080330795 },
3080430796 .bad_decl_ty, .bad_ptr_ty => return parent,
3080530797 // Even though the parent value type has well-defined memory layout, our
3080630798 // pointer type does not.
30807 .reinterpret => return ComptimePtrMutationKit{
30799 .reinterpret => return .{
3080830800 .root = parent.root,
3080930801 .pointee = .bad_ptr_ty,
3081030802 .ty = eu_ty,
......@@ -30818,46 +30810,28 @@ fn beginComptimePtrMutation(
3081830810 .opv => unreachable,
3081930811 .direct => |val_ptr| {
3082030812 const payload_ty = parent.ty.optionalChild(mod);
30821 switch (val_ptr.ip_index) {
30822 .none => return ComptimePtrMutationKit{
30823 .root = parent.root,
30824 .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data },
30825 .ty = payload_ty,
30826 },
30827 else => {
30828 const payload_val = switch (mod.intern_pool.indexToKey(val_ptr.ip_index)) {
30829 .undef => try mod.intern(.{ .undef = payload_ty.toIntern() }),
30830 .opt => |opt| switch (opt.val) {
30831 .none => try mod.intern(.{ .undef = payload_ty.toIntern() }),
30832 else => |payload| payload,
30833 },
30834 else => unreachable,
30835 };
30836
30837 // An optional has been initialized to undefined at comptime and now we
30838 // are for the first time setting the payload. We must change the
30839 // representation of the optional from `undef` to `opt_payload`.
30840
30841 const payload = try sema.arena.create(Value.Payload.SubValue);
30842 payload.* = .{
30843 .base = .{ .tag = .opt_payload },
30844 .data = Value.fromInterned(payload_val),
30845 };
30846
30847 val_ptr.* = Value.initPayload(&payload.base);
30848
30849 return ComptimePtrMutationKit{
30850 .root = parent.root,
30851 .pointee = .{ .direct = &payload.data },
30852 .ty = payload_ty,
30853 };
30854 },
30813 try val_ptr.unintern(mod, sema.arena, false, false);
30814 if (val_ptr.* == .interned) {
30815 // An optional has been initialized to undefined at comptime and now we
30816 // are for the first time setting the payload. We must change the
30817 // representation of the optional to `opt_payload`.
30818 const child = try sema.arena.create(MutableValue);
30819 child.* = .{ .interned = try mod.intern(.{ .undef = payload_ty.toIntern() }) };
30820 val_ptr.* = .{ .opt_payload = .{
30821 .ty = parent.ty.toIntern(),
30822 .child = child,
30823 } };
3085530824 }
30825 return .{
30826 .root = parent.root,
30827 .pointee = .{ .direct = val_ptr.opt_payload.child },
30828 .ty = payload_ty,
30829 };
3085630830 },
3085730831 .bad_decl_ty, .bad_ptr_ty => return parent,
3085830832 // Even though the parent value type has well-defined memory layout, our
3085930833 // pointer type does not.
30860 .reinterpret => return ComptimePtrMutationKit{
30834 .reinterpret => return .{
3086130835 .root = parent.root,
3086230836 .pointee = .bad_ptr_ty,
3086330837 .ty = opt_ty,
......@@ -30916,106 +30890,28 @@ fn beginComptimePtrMutation(
3091630890 };
3091730891 }
3091830892
30919 switch (val_ptr.ip_index) {
30920 .none => switch (val_ptr.tag()) {
30921 .bytes => {
30922 // An array is memory-optimized to store a slice of bytes, but we are about
30923 // to modify an individual field and the representation has to change.
30924 // If we wanted to avoid this, there would need to be special detection
30925 // elsewhere to identify when writing a value to an array element that is stored
30926 // using the `bytes` tag, and handle it without making a call to this function.
30927 const arena = sema.arena;
30928
30929 const bytes = val_ptr.castTag(.bytes).?.data;
30930 const dest_len = parent.ty.arrayLenIncludingSentinel(mod);
30931 // bytes.len may be one greater than dest_len because of the case when
30932 // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
30933 assert(bytes.len >= dest_len);
30934 const elems = try arena.alloc(Value, @intCast(dest_len));
30935 for (elems, 0..) |*elem, i| {
30936 elem.* = try mod.intValue(elem_ty, bytes[i]);
30937 }
30938
30939 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
30940
30941 return beginComptimePtrMutationInner(
30942 sema,
30943 block,
30944 src,
30945 elem_ty,
30946 &elems[@intCast(elem_ptr.index)],
30947 ptr_elem_ty,
30948 parent.root,
30949 );
30950 },
30951 .repeated => {
30952 // An array is memory-optimized to store only a single element value, and
30953 // that value is understood to be the same for the entire length of the array.
30954 // However, now we want to modify an individual field and so the
30955 // representation has to change. If we wanted to avoid this, there would
30956 // need to be special detection elsewhere to identify when writing a value to an
30957 // array element that is stored using the `repeated` tag, and handle it
30958 // without making a call to this function.
30959 const arena = sema.arena;
30960
30961 const repeated_val = try val_ptr.castTag(.repeated).?.data.intern(parent.ty.childType(mod), mod);
30962 const array_len_including_sentinel =
30963 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
30964 const elems = try arena.alloc(Value, array_len_including_sentinel);
30965 @memset(elems, Value.fromInterned(repeated_val));
30966
30967 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
30968
30969 return beginComptimePtrMutationInner(
30970 sema,
30971 block,
30972 src,
30973 elem_ty,
30974 &elems[@intCast(elem_ptr.index)],
30975 ptr_elem_ty,
30976 parent.root,
30977 );
30978 },
30979
30980 .aggregate => return beginComptimePtrMutationInner(
30981 sema,
30982 block,
30983 src,
30984 elem_ty,
30985 &val_ptr.castTag(.aggregate).?.data[@intCast(elem_ptr.index)],
30986 ptr_elem_ty,
30987 parent.root,
30988 ),
30893 try val_ptr.unintern(mod, sema.arena, false, false);
30894
30895 const aggregate = switch (val_ptr.*) {
30896 .interned,
30897 .bytes,
30898 .repeated,
30899 .eu_payload,
30900 .opt_payload,
30901 .slice,
30902 .un,
30903 => unreachable,
30904 .aggregate => |*a| a,
30905 };
3098930906
30990 else => unreachable,
30991 },
30992 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
30993 .undef => {
30994 // An array has been initialized to undefined at comptime and now we
30995 // are for the first time setting an element. We must change the representation
30996 // of the array from `undef` to `array`.
30997 const arena = sema.arena;
30998
30999 const array_len_including_sentinel =
31000 try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel(mod));
31001 const elems = try arena.alloc(Value, array_len_including_sentinel);
31002 @memset(elems, Value.fromInterned((try mod.intern(.{ .undef = elem_ty.toIntern() }))));
31003
31004 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
31005
31006 return beginComptimePtrMutationInner(
31007 sema,
31008 block,
31009 src,
31010 elem_ty,
31011 &elems[@intCast(elem_ptr.index)],
31012 ptr_elem_ty,
31013 parent.root,
31014 );
31015 },
31016 else => unreachable,
31017 },
31018 }
30907 return sema.beginComptimePtrMutationInner(
30908 block,
30909 src,
30910 elem_ty,
30911 &aggregate.elems[@intCast(elem_ptr.index)],
30912 ptr_elem_ty,
30913 parent.root,
30914 );
3101930915 },
3102030916 else => {
3102130917 if (elem_ptr.index != 0) {
......@@ -31039,7 +30935,7 @@ fn beginComptimePtrMutation(
3103930935 if (!base_elem_ty.hasWellDefinedLayout(mod)) {
3104030936 // Even though the parent value type has well-defined memory layout, our
3104130937 // pointer type does not.
31042 return ComptimePtrMutationKit{
30938 return .{
3104330939 .root = parent.root,
3104430940 .pointee = .bad_ptr_ty,
3104530941 .ty = base_elem_ty,
......@@ -31049,7 +30945,7 @@ fn beginComptimePtrMutation(
3104930945 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);
3105030946 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
3105130947 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
31052 return ComptimePtrMutationKit{
30948 return .{
3105330949 .root = parent.root,
3105430950 .pointee = .{ .reinterpret = .{
3105530951 .val_ptr = reinterpret.val_ptr,
......@@ -31068,56 +30964,68 @@ fn beginComptimePtrMutation(
3106830964 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(field_ptr.base), base_child_ty);
3106930965 switch (parent.pointee) {
3107030966 .opv => unreachable,
31071 .direct => |val_ptr| switch (val_ptr.ip_index) {
31072 .empty_struct => {
31073 const duped = try sema.arena.create(Value);
31074 duped.* = val_ptr.*;
31075 return beginComptimePtrMutationInner(
31076 sema,
31077 block,
31078 src,
31079 parent.ty.structFieldType(field_index, mod),
31080 duped,
31081 ptr_elem_ty,
31082 parent.root,
31083 );
31084 },
31085 .none => switch (val_ptr.tag()) {
31086 .aggregate => return beginComptimePtrMutationInner(
31087 sema,
30967 .direct => |val_ptr| {
30968 try val_ptr.unintern(mod, sema.arena, false, false);
30969 switch (val_ptr.*) {
30970 .interned,
30971 .eu_payload,
30972 .opt_payload,
30973 .repeated,
30974 .bytes,
30975 => unreachable,
30976 .aggregate => |*a| return sema.beginComptimePtrMutationInner(
3108830977 block,
3108930978 src,
3109030979 parent.ty.structFieldType(field_index, mod),
31091 &val_ptr.castTag(.aggregate).?.data[field_index],
30980 &a.elems[field_index],
3109230981 ptr_elem_ty,
3109330982 parent.root,
3109430983 ),
31095 .repeated => {
31096 const arena = sema.arena;
31097
31098 const elems = try arena.alloc(Value, parent.ty.structFieldCount(mod));
31099 @memset(elems, val_ptr.castTag(.repeated).?.data);
31100 val_ptr.* = try Value.Tag.aggregate.create(arena, elems);
31101
31102 return beginComptimePtrMutationInner(
31103 sema,
30984 .slice => |*s| switch (field_index) {
30985 Value.slice_ptr_index => return sema.beginComptimePtrMutationInner(
3110430986 block,
3110530987 src,
31106 parent.ty.structFieldType(field_index, mod),
31107 &elems[field_index],
30988 parent.ty.slicePtrFieldType(mod),
30989 s.ptr,
3110830990 ptr_elem_ty,
3110930991 parent.root,
31110 );
30992 ),
30993 Value.slice_len_index => return sema.beginComptimePtrMutationInner(
30994 block,
30995 src,
30996 Type.usize,
30997 s.len,
30998 ptr_elem_ty,
30999 parent.root,
31000 ),
31001 else => unreachable,
3111131002 },
31112 .@"union" => {
31113 const payload = &val_ptr.castTag(.@"union").?.data;
31003 .un => |*un| {
3111431004 const layout = base_child_ty.containerLayout(mod);
3111531005
3111631006 const tag_type = base_child_ty.unionTagTypeHypothetical(mod);
3111731007 const hypothetical_tag = try mod.enumValueFieldIndex(tag_type, field_index);
31118 if (layout == .auto or (payload.tag != null and hypothetical_tag.eql(payload.tag.?, tag_type, mod))) {
31008 if (un.tag == .none and un.payload.* == .interned and un.payload.interned == .undef) {
31009 // A union has been initialized to undefined at comptime and now we
31010 // are for the first time setting the payload. We must change the
31011 // tag implicitly.
31012 const payload_ty = parent.ty.structFieldType(field_index, mod);
31013 un.tag = hypothetical_tag.toIntern();
31014 un.payload.* = .{ .interned = try mod.intern(.{ .undef = payload_ty.toIntern() }) };
31015 return beginComptimePtrMutationInner(
31016 sema,
31017 block,
31018 src,
31019 payload_ty,
31020 un.payload,
31021 ptr_elem_ty,
31022 parent.root,
31023 );
31024 }
31025
31026 if (layout == .auto or hypothetical_tag.toIntern() == un.tag) {
3111931027 // We need to set the active field of the union.
31120 payload.tag = hypothetical_tag;
31028 un.tag = hypothetical_tag.toIntern();
3112131029
3112231030 const field_ty = parent.ty.structFieldType(field_index, mod);
3112331031 return beginComptimePtrMutationInner(
......@@ -31125,7 +31033,7 @@ fn beginComptimePtrMutation(
3112531033 block,
3112631034 src,
3112731035 field_ty,
31128 &payload.val,
31036 un.payload,
3112931037 ptr_elem_ty,
3113031038 parent.root,
3113131039 );
......@@ -31133,11 +31041,10 @@ fn beginComptimePtrMutation(
3113331041 // Writing to a different field (a different or unknown tag is active) requires reinterpreting
3113431042 // memory of the entire union, which requires knowing its abiSize.
3113531043 try sema.resolveTypeLayout(parent.ty);
31136
3113731044 // This union value no longer has a well-defined tag type.
3113831045 // The reinterpretation will read it back out as .none.
31139 payload.val = try payload.val.unintern(sema.arena, mod);
31140 return ComptimePtrMutationKit{
31046 try un.payload.unintern(mod, sema.arena, false, false);
31047 return .{
3114131048 .root = parent.root,
3114231049 .pointee = .{ .reinterpret = .{
3114331050 .val_ptr = val_ptr,
......@@ -31148,119 +31055,12 @@ fn beginComptimePtrMutation(
3114831055 };
3114931056 }
3115031057 },
31151 .slice => switch (field_index) {
31152 Value.slice_ptr_index => return beginComptimePtrMutationInner(
31153 sema,
31154 block,
31155 src,
31156 parent.ty.slicePtrFieldType(mod),
31157 &val_ptr.castTag(.slice).?.data.ptr,
31158 ptr_elem_ty,
31159 parent.root,
31160 ),
31161
31162 Value.slice_len_index => return beginComptimePtrMutationInner(
31163 sema,
31164 block,
31165 src,
31166 Type.usize,
31167 &val_ptr.castTag(.slice).?.data.len,
31168 ptr_elem_ty,
31169 parent.root,
31170 ),
31171
31172 else => unreachable,
31173 },
31174 else => unreachable,
31175 },
31176 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {
31177 .undef => {
31178 // A struct or union has been initialized to undefined at comptime and now we
31179 // are for the first time setting a field. We must change the representation
31180 // of the struct/union from `undef` to `struct`/`union`.
31181 const arena = sema.arena;
31182
31183 switch (parent.ty.zigTypeTag(mod)) {
31184 .Struct => {
31185 const fields = try arena.alloc(Value, parent.ty.structFieldCount(mod));
31186 for (fields, 0..) |*field, i| field.* = Value.fromInterned((try mod.intern(.{
31187 .undef = parent.ty.structFieldType(i, mod).toIntern(),
31188 })));
31189
31190 val_ptr.* = try Value.Tag.aggregate.create(arena, fields);
31191
31192 return beginComptimePtrMutationInner(
31193 sema,
31194 block,
31195 src,
31196 parent.ty.structFieldType(field_index, mod),
31197 &fields[field_index],
31198 ptr_elem_ty,
31199 parent.root,
31200 );
31201 },
31202 .Union => {
31203 const payload = try arena.create(Value.Payload.Union);
31204 const tag_ty = parent.ty.unionTagTypeHypothetical(mod);
31205 const payload_ty = parent.ty.structFieldType(field_index, mod);
31206 payload.* = .{ .data = .{
31207 .tag = try mod.enumValueFieldIndex(tag_ty, field_index),
31208 .val = Value.fromInterned((try mod.intern(.{ .undef = payload_ty.toIntern() }))),
31209 } };
31210
31211 val_ptr.* = Value.initPayload(&payload.base);
31212
31213 return beginComptimePtrMutationInner(
31214 sema,
31215 block,
31216 src,
31217 payload_ty,
31218 &payload.data.val,
31219 ptr_elem_ty,
31220 parent.root,
31221 );
31222 },
31223 .Pointer => {
31224 assert(parent.ty.isSlice(mod));
31225 const ptr_ty = parent.ty.slicePtrFieldType(mod);
31226 val_ptr.* = try Value.Tag.slice.create(arena, .{
31227 .ptr = Value.fromInterned((try mod.intern(.{ .undef = ptr_ty.toIntern() }))),
31228 .len = Value.fromInterned((try mod.intern(.{ .undef = .usize_type }))),
31229 });
31230
31231 switch (field_index) {
31232 Value.slice_ptr_index => return beginComptimePtrMutationInner(
31233 sema,
31234 block,
31235 src,
31236 ptr_ty,
31237 &val_ptr.castTag(.slice).?.data.ptr,
31238 ptr_elem_ty,
31239 parent.root,
31240 ),
31241 Value.slice_len_index => return beginComptimePtrMutationInner(
31242 sema,
31243 block,
31244 src,
31245 Type.usize,
31246 &val_ptr.castTag(.slice).?.data.len,
31247 ptr_elem_ty,
31248 parent.root,
31249 ),
31250
31251 else => unreachable,
31252 }
31253 },
31254 else => unreachable,
31255 }
31256 },
31257 else => unreachable,
31258 },
31058 }
3125931059 },
3126031060 .reinterpret => |reinterpret| {
3126131061 const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod);
3126231062 const field_offset = try sema.usizeCast(block, src, field_offset_u64);
31263 return ComptimePtrMutationKit{
31063 return .{
3126431064 .root = parent.root,
3126531065 .pointee = .{ .reinterpret = .{
3126631066 .val_ptr = reinterpret.val_ptr,
......@@ -31280,7 +31080,7 @@ fn beginComptimePtrMutationInner(
3128031080 block: *Block,
3128131081 src: LazySrcLoc,
3128231082 decl_ty: Type,
31283 decl_val: *Value,
31083 decl_val: *MutableValue,
3128431084 ptr_elem_ty: Type,
3128531085 root: ComptimePtrMutationKit.Root,
3128631086) CompileError!ComptimePtrMutationKit {
......@@ -31288,7 +31088,13 @@ fn beginComptimePtrMutationInner(
3128831088 const target = mod.getTarget();
3128931089 const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok;
3129031090
31291 decl_val.* = try decl_val.unintern(sema.arena, mod);
31091 const old_decl_val = decl_val.*;
31092 try decl_val.unintern(mod, sema.arena, false, false);
31093 if (decl_val.* == .un and decl_val.un.tag == .none and decl_val.un.payload.* == .interned and decl_val.un.payload.interned == .undef) {
31094 // HACKHACK: undefined union - re-intern it for now
31095 // `unintern` probably should just leave these as is, but I'm leaving it until I rewrite comptime pointer access.
31096 decl_val.* = old_decl_val;
31097 }
3129231098
3129331099 if (coerce_ok) {
3129431100 return ComptimePtrMutationKit{
......@@ -31334,21 +31140,16 @@ fn beginComptimePtrMutationInner(
3133431140 };
3133531141}
3133631142
31337const TypedValueAndOffset = struct {
31338 tv: TypedValue,
31339 byte_offset: usize,
31340};
31341
3134231143const ComptimePtrLoadKit = struct {
3134331144 /// The Value and Type corresponding to the pointee of the provided pointer.
3134431145 /// If a direct dereference is not possible, this is null.
31345 pointee: ?TypedValue,
31146 pointee: ?MutableValue,
3134631147 /// The largest parent Value containing `pointee` and having a well-defined memory layout.
3134731148 /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null).
31348 parent: ?TypedValueAndOffset,
31349 /// Whether the `pointee` could be mutated by further
31350 /// semantic analysis and a copy must be performed.
31351 is_mutable: bool,
31149 parent: ?struct {
31150 val: MutableValue,
31151 byte_offset: usize,
31152 },
3135231153 /// If the root decl could not be used as `parent`, this is the type that
3135331154 /// caused that by not having a well-defined layout
3135431155 ty_without_well_defined_layout: ?Type,
......@@ -31375,53 +31176,41 @@ fn beginComptimePtrLoad(
3137531176 .ptr => |ptr| switch (ptr.addr) {
3137631177 .decl => |decl_index| blk: {
3137731178 const decl = mod.declPtr(decl_index);
31378 const decl_tv = try decl.typedValue(mod);
3137931179 try sema.declareDependency(.{ .decl_val = decl_index });
3138031180 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad;
31381
31181 const decl_val: MutableValue = .{ .interned = decl.val.toIntern() };
3138231182 const layout_defined = decl.typeOf(mod).hasWellDefinedLayout(mod);
3138331183 break :blk ComptimePtrLoadKit{
31384 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
31385 .pointee = decl_tv,
31386 .is_mutable = false,
31184 .parent = if (layout_defined) .{ .val = decl_val, .byte_offset = 0 } else null,
31185 .pointee = decl_val,
3138731186 .ty_without_well_defined_layout = if (!layout_defined) decl.typeOf(mod) else null,
3138831187 };
3138931188 },
3139031189 .comptime_alloc => |alloc_index| kit: {
3139131190 const alloc = sema.getComptimeAlloc(alloc_index);
31392 const alloc_tv: TypedValue = .{
31393 .ty = alloc.ty,
31394 .val = alloc.val,
31395 };
31396 const layout_defined = alloc.ty.hasWellDefinedLayout(mod);
31191 const alloc_ty = alloc.val.typeOf(mod);
31192 const layout_defined = alloc_ty.hasWellDefinedLayout(mod);
3139731193 break :kit .{
31398 .parent = if (layout_defined) .{ .tv = alloc_tv, .byte_offset = 0 } else null,
31399 .pointee = alloc_tv,
31400 .is_mutable = true,
31401 .ty_without_well_defined_layout = if (!layout_defined) alloc.ty else null,
31194 .parent = if (layout_defined) .{ .val = alloc.val, .byte_offset = 0 } else null,
31195 .pointee = alloc.val,
31196 .ty_without_well_defined_layout = if (!layout_defined) alloc_ty else null,
3140231197 };
3140331198 },
3140431199 .anon_decl => |anon_decl| blk: {
3140531200 const decl_val = anon_decl.val;
3140631201 if (Value.fromInterned(decl_val).getVariable(mod) != null) return error.RuntimeLoad;
3140731202 const decl_ty = Type.fromInterned(ip.typeOf(decl_val));
31408 const decl_tv: TypedValue = .{ .ty = decl_ty, .val = Value.fromInterned(decl_val) };
31203 const decl_mv: MutableValue = .{ .interned = decl_val };
3140931204 const layout_defined = decl_ty.hasWellDefinedLayout(mod);
3141031205 break :blk ComptimePtrLoadKit{
31411 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
31412 .pointee = decl_tv,
31413 .is_mutable = false,
31206 .parent = if (layout_defined) .{ .val = decl_mv, .byte_offset = 0 } else null,
31207 .pointee = decl_mv,
3141431208 .ty_without_well_defined_layout = if (!layout_defined) decl_ty else null,
3141531209 };
3141631210 },
3141731211 .int => return error.RuntimeLoad,
3141831212 .eu_payload, .opt_payload => |container_ptr| blk: {
3141931213 const container_ty = Type.fromInterned(ip.typeOf(container_ptr)).childType(mod);
31420 const payload_ty = switch (ptr.addr) {
31421 .eu_payload => container_ty.errorUnionPayload(mod),
31422 .opt_payload => container_ty.optionalChild(mod),
31423 else => unreachable,
31424 };
3142531214 var deref = try sema.beginComptimePtrLoad(block, src, Value.fromInterned(container_ptr), container_ty);
3142631215
3142731216 // eu_payload and opt_payload never have a well-defined layout
......@@ -31430,15 +31219,14 @@ fn beginComptimePtrLoad(
3143031219 deref.ty_without_well_defined_layout = container_ty;
3143131220 }
3143231221
31433 if (deref.pointee) |*tv| {
31222 if (deref.pointee) |pointee| {
31223 const pointee_ty = pointee.typeOf(mod);
3143431224 const coerce_in_mem_ok =
31435 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
31436 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
31225 (try sema.coerceInMemoryAllowed(block, container_ty, pointee_ty, false, target, src, src)) == .ok or
31226 (try sema.coerceInMemoryAllowed(block, pointee_ty, container_ty, false, target, src, src)) == .ok;
3143731227 if (coerce_in_mem_ok) {
31438 const payload_val = switch (tv.val.ip_index) {
31439 .none => tv.val.cast(Value.Payload.SubValue).?.data,
31440 .null_value => return sema.fail(block, src, "attempt to use null value", .{}),
31441 else => Value.fromInterned(switch (ip.indexToKey(tv.val.toIntern())) {
31228 deref.pointee = switch (pointee) {
31229 .interned => |ip_index| .{ .interned = switch (ip.indexToKey(ip_index)) {
3144231230 .error_union => |error_union| switch (error_union.val) {
3144331231 .err_name => |err_name| return sema.fail(
3144431232 block,
......@@ -31453,23 +31241,20 @@ fn beginComptimePtrLoad(
3145331241 else => |payload| payload,
3145431242 },
3145531243 else => unreachable,
31456 }),
31244 } },
31245 .eu_payload, .opt_payload => |p| p.child.*,
31246 else => unreachable,
3145731247 };
31458 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
3145931248 break :blk deref;
3146031249 }
3146131250 }
3146231251 deref.pointee = null;
3146331252 break :blk deref;
3146431253 },
31465 .comptime_field => |comptime_field| blk: {
31466 const field_ty = Type.fromInterned(ip.typeOf(comptime_field));
31467 break :blk ComptimePtrLoadKit{
31468 .parent = null,
31469 .pointee = .{ .ty = field_ty, .val = Value.fromInterned(comptime_field) },
31470 .is_mutable = false,
31471 .ty_without_well_defined_layout = field_ty,
31472 };
31254 .comptime_field => |field_val| .{
31255 .parent = null,
31256 .pointee = .{ .interned = field_val },
31257 .ty_without_well_defined_layout = Type.fromInterned(ip.typeOf(field_val)),
3147331258 },
3147431259 .elem => |elem_ptr| blk: {
3147531260 const elem_ty = Type.fromInterned(ip.typeOf(elem_ptr.base)).elemType2(mod);
......@@ -31502,30 +31287,37 @@ fn beginComptimePtrLoad(
3150231287
3150331288 // If we're loading an elem that was derived from a different type
3150431289 // than the true type of the underlying decl, we cannot deref directly
31505 const ty_matches = if (deref.pointee != null and deref.pointee.?.ty.isArrayOrVector(mod)) x: {
31506 const deref_elem_ty = deref.pointee.?.ty.childType(mod);
31507 break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or
31508 (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok;
31290 const ty_matches = if (deref.pointee) |pointee| match: {
31291 const ty = pointee.typeOf(mod);
31292 if (!ty.isArrayOrVector(mod)) break :match false;
31293 const deref_elem_ty = ty.childType(mod);
31294 if ((try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok) break :match true;
31295 if ((try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok) break :match true;
31296 break :match false;
3150931297 } else false;
3151031298 if (!ty_matches) {
3151131299 deref.pointee = null;
3151231300 break :blk deref;
3151331301 }
3151431302
31515 var array_tv = deref.pointee.?;
31516 const check_len = array_tv.ty.arrayLenIncludingSentinel(mod);
31303 var array_val = deref.pointee.?;
31304 const check_len = array_val.typeOf(mod).arrayLenIncludingSentinel(mod);
3151731305 if (maybe_array_ty) |load_ty| {
3151831306 // It's possible that we're loading a [N]T, in which case we'd like to slice
3151931307 // the pointee array directly from our parent array.
3152031308 if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) {
3152131309 const len = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod));
3152231310 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
31523 deref.pointee = if (elem_ptr.index + len <= check_len) TypedValue{
31524 .ty = try mod.arrayType(.{
31525 .len = len,
31526 .child = elem_ty.toIntern(),
31527 }),
31528 .val = try array_tv.val.sliceArray(sema, elem_idx, elem_idx + len),
31311 deref.pointee = if (elem_ptr.index + len <= check_len) switch (array_val) {
31312 .aggregate => |a| .{ .aggregate = .{
31313 .ty = (try mod.arrayType(.{ .len = len, .child = elem_ty.toIntern() })).toIntern(),
31314 .elems = a.elems[elem_idx..][0..len],
31315 } },
31316 else => .{
31317 .interned = (try (Value.fromInterned(
31318 try array_val.intern(mod, sema.arena),
31319 ).sliceArray(sema, elem_idx, elem_idx + len))).toIntern(),
31320 },
3152931321 } else null;
3153031322 break :blk deref;
3153131323 }
......@@ -31536,18 +31328,12 @@ fn beginComptimePtrLoad(
3153631328 break :blk deref;
3153731329 }
3153831330 if (elem_ptr.index == check_len - 1) {
31539 if (array_tv.ty.sentinel(mod)) |sent| {
31540 deref.pointee = TypedValue{
31541 .ty = elem_ty,
31542 .val = sent,
31543 };
31331 if (array_val.typeOf(mod).sentinel(mod)) |sent| {
31332 deref.pointee = .{ .interned = sent.toIntern() };
3154431333 break :blk deref;
3154531334 }
3154631335 }
31547 deref.pointee = TypedValue{
31548 .ty = elem_ty,
31549 .val = try array_tv.val.elemValue(mod, @intCast(elem_ptr.index)),
31550 };
31336 deref.pointee = try array_val.getElem(mod, @intCast(elem_ptr.index));
3155131337 break :blk deref;
3155231338 },
3155331339 .field => |field_ptr| blk: {
......@@ -31571,37 +31357,17 @@ fn beginComptimePtrLoad(
3157131357 deref.ty_without_well_defined_layout = container_ty;
3157231358 }
3157331359
31574 const tv = deref.pointee orelse {
31575 deref.pointee = null;
31576 break :blk deref;
31577 };
31360 const pointee = deref.pointee orelse break :blk deref;
31361 const pointee_ty = pointee.typeOf(mod);
3157831362 const coerce_in_mem_ok =
31579 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or
31580 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;
31363 (try sema.coerceInMemoryAllowed(block, container_ty, pointee_ty, false, target, src, src)) == .ok or
31364 (try sema.coerceInMemoryAllowed(block, pointee_ty, container_ty, false, target, src, src)) == .ok;
3158131365 if (!coerce_in_mem_ok) {
3158231366 deref.pointee = null;
3158331367 break :blk deref;
3158431368 }
3158531369
31586 if (container_ty.isSlice(mod)) {
31587 deref.pointee = switch (field_index) {
31588 Value.slice_ptr_index => TypedValue{
31589 .ty = container_ty.slicePtrFieldType(mod),
31590 .val = tv.val.slicePtr(mod),
31591 },
31592 Value.slice_len_index => TypedValue{
31593 .ty = Type.usize,
31594 .val = Value.fromInterned(ip.indexToKey(try tv.val.intern(tv.ty, mod)).slice.len),
31595 },
31596 else => unreachable,
31597 };
31598 } else {
31599 const field_ty = container_ty.structFieldType(field_index, mod);
31600 deref.pointee = TypedValue{
31601 .ty = field_ty,
31602 .val = try tv.val.fieldValue(mod, field_index),
31603 };
31604 }
31370 deref.pointee = try pointee.getElem(mod, field_index);
3160531371 break :blk deref;
3160631372 },
3160731373 },
......@@ -31612,9 +31378,9 @@ fn beginComptimePtrLoad(
3161231378 else => unreachable,
3161331379 };
3161431380
31615 if (deref.pointee) |tv| {
31616 if (deref.parent == null and tv.ty.hasWellDefinedLayout(mod)) {
31617 deref.parent = .{ .tv = tv, .byte_offset = 0 };
31381 if (deref.pointee) |val| {
31382 if (deref.parent == null and val.typeOf(mod).hasWellDefinedLayout(mod)) {
31383 deref.parent = .{ .val = val, .byte_offset = 0 };
3161831384 }
3161931385 }
3162031386 return deref;
......@@ -38289,17 +38055,18 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value
3828938055 else => |e| return e,
3829038056 };
3829138057
38292 if (deref.pointee) |tv| {
38058 if (deref.pointee) |pointee| {
38059 const uncoerced_val = Value.fromInterned(try pointee.intern(mod, sema.arena));
38060 const ty = Type.fromInterned(mod.intern_pool.typeOf(uncoerced_val.toIntern()));
3829338061 const coerce_in_mem_ok =
38294 (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or
38295 (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok;
38062 (try sema.coerceInMemoryAllowed(block, load_ty, ty, false, target, src, src)) == .ok or
38063 (try sema.coerceInMemoryAllowed(block, ty, load_ty, false, target, src, src)) == .ok;
3829638064 if (coerce_in_mem_ok) {
3829738065 // We have a Value that lines up in virtual memory exactly with what we want to load,
3829838066 // and it is in-memory coercible to load_ty. It may be returned without modifications.
3829938067 // Move mutable decl values to the InternPool and assert other decls are already in
3830038068 // the InternPool.
38301 const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern();
38302 const coerced_val = try mod.getCoerced(Value.fromInterned(uncoerced_val), load_ty);
38069 const coerced_val = try mod.getCoerced(uncoerced_val, load_ty);
3830338070 return .{ .val = coerced_val };
3830438071 }
3830538072 }
......@@ -38313,21 +38080,35 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value
3831338080 const load_sz = try sema.typeAbiSize(load_ty);
3831438081
3831538082 // Try the smaller bit-cast first, since that's more efficient than using the larger `parent`
38316 if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(tv.ty))
38317 return DerefResult{ .val = (try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0)) orelse return .runtime_load };
38083 if (deref.pointee) |pointee| {
38084 const val_ip_index = try pointee.intern(mod, sema.arena);
38085 const val = Value.fromInterned(val_ip_index);
38086 const ty = Type.fromInterned(mod.intern_pool.typeOf(val_ip_index));
38087 if (load_sz <= try sema.typeAbiSize(ty)) {
38088 return .{ .val = (try sema.bitCastVal(block, src, val, ty, load_ty, 0)) orelse return .runtime_load };
38089 }
38090 }
3831838091
3831938092 // If that fails, try to bit-cast from the largest parent value with a well-defined layout
38320 if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent.tv.ty))
38321 return DerefResult{ .val = (try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset)) orelse return .runtime_load };
38093 if (deref.parent) |parent| {
38094 const parent_ip_index = try parent.val.intern(mod, sema.arena);
38095 const parent_val = Value.fromInterned(parent_ip_index);
38096 const parent_ty = Type.fromInterned(mod.intern_pool.typeOf(parent_ip_index));
38097 if (load_sz + parent.byte_offset <= try sema.typeAbiSize(parent_ty)) {
38098 return .{ .val = (try sema.bitCastVal(block, src, parent_val, parent_ty, load_ty, parent.byte_offset)) orelse return .runtime_load };
38099 }
38100 }
3832238101
3832338102 if (deref.ty_without_well_defined_layout) |bad_ty| {
3832438103 // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
3832538104 // is that some type we encountered when de-referencing does not have a well-defined layout.
38326 return DerefResult{ .needed_well_defined = bad_ty };
38105 return .{ .needed_well_defined = bad_ty };
3832738106 } else {
3832838107 // If all encountered types had well-defined layouts, the parent is the root decl and it just
3832938108 // wasn't big enough for the load.
38330 return DerefResult{ .out_of_bounds = deref.parent.?.tv.ty };
38109 const parent_ip_index = try deref.parent.?.val.intern(mod, sema.arena);
38110 const parent_ty = Type.fromInterned(mod.intern_pool.typeOf(parent_ip_index));
38111 return .{ .out_of_bounds = parent_ty };
3833138112 }
3833238113}
3833338114
src/Value.zig+6-2
......@@ -1627,7 +1627,9 @@ pub fn maybeElemValueFull(val: Value, sema: ?*Sema, mod: *Module, index: usize)
16271627 .ptr => |ptr| switch (ptr.addr) {
16281628 .decl => |decl| mod.declPtr(decl).val.maybeElemValueFull(sema, mod, index),
16291629 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).maybeElemValueFull(sema, mod, index),
1630 .comptime_alloc => |idx| if (sema) |s| s.getComptimeAlloc(idx).val.maybeElemValueFull(sema, mod, index) else null,
1630 .comptime_alloc => |idx| if (sema) |s| Value.fromInterned(
1631 try s.getComptimeAlloc(idx).val.intern(mod, s.arena),
1632 ).maybeElemValueFull(sema, mod, index) else null,
16311633 .int, .eu_payload => null,
16321634 .opt_payload => |base| Value.fromInterned(base).maybeElemValueFull(sema, mod, index),
16331635 .comptime_field => |field_val| Value.fromInterned(field_val).maybeElemValueFull(sema, mod, index),
......@@ -1697,7 +1699,9 @@ pub fn sliceArray(
16971699 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
16981700 .ptr => |ptr| switch (ptr.addr) {
16991701 .decl => |decl| try mod.declPtr(decl).val.sliceArray(sema, start, end),
1700 .comptime_alloc => |idx| sema.getComptimeAlloc(idx).val.sliceArray(sema, start, end),
1702 .comptime_alloc => |idx| try Value.fromInterned(
1703 try sema.getComptimeAlloc(idx).val.intern(mod, sema.arena),
1704 ).sliceArray(sema, start, end),
17011705 .comptime_field => |comptime_field| Value.fromInterned(comptime_field)
17021706 .sliceArray(sema, start, end),
17031707 .elem => |elem| Value.fromInterned(elem.base)
src/mutable_value.zig created+508
......@@ -0,0 +1,508 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const Allocator = std.mem.Allocator;
4const Zcu = @import("Module.zig");
5const InternPool = @import("InternPool.zig");
6const Type = @import("type.zig").Type;
7const Value = @import("Value.zig");
8
9/// We use a tagged union here because while it wastes a few bytes for some tags, having a fixed
10/// size for the type makes the common `aggregate` representation more efficient.
11/// For aggregates, the sentinel value, if any, *is* stored.
12pub const MutableValue = union(enum) {
13 /// An interned value.
14 interned: InternPool.Index,
15 /// An error union value which is a payload (not an error).
16 eu_payload: SubValue,
17 /// An optional value which is a payload (not `null`).
18 opt_payload: SubValue,
19 /// An aggregate consisting of a single repeated value.
20 repeated: SubValue,
21 /// An aggregate of `u8` consisting of "plain" bytes (no lazy or undefined elements).
22 bytes: Bytes,
23 /// An aggregate with arbitrary sub-values.
24 aggregate: Aggregate,
25 /// A slice, containing a pointer and length.
26 slice: Slice,
27 /// An instance of a union.
28 un: Union,
29
30 pub const SubValue = struct {
31 ty: InternPool.Index,
32 child: *MutableValue,
33 };
34 pub const Bytes = struct {
35 ty: InternPool.Index,
36 data: []u8,
37 };
38 pub const Aggregate = struct {
39 ty: InternPool.Index,
40 elems: []MutableValue,
41 };
42 pub const Slice = struct {
43 ty: InternPool.Index,
44 /// Must have the appropriate many-ptr type.
45 /// TODO: we want this to be an `InternPool.Index`, but `Sema.beginComptimePtrMutation` doesn't support it.
46 ptr: *MutableValue,
47 /// Must be of type `usize`.
48 /// TODO: we want this to be an `InternPool.Index`, but `Sema.beginComptimePtrMutation` doesn't support it.
49 len: *MutableValue,
50 };
51 pub const Union = struct {
52 ty: InternPool.Index,
53 tag: InternPool.Index,
54 payload: *MutableValue,
55 };
56
57 pub fn intern(mv: MutableValue, zcu: *Zcu, arena: Allocator) Allocator.Error!InternPool.Index {
58 const ip = &zcu.intern_pool;
59 const gpa = zcu.gpa;
60 return switch (mv) {
61 .interned => |ip_index| ip_index,
62 .eu_payload => |sv| try ip.get(gpa, .{ .error_union = .{
63 .ty = sv.ty,
64 .val = .{ .payload = try sv.child.intern(zcu, arena) },
65 } }),
66 .opt_payload => |sv| try ip.get(gpa, .{ .opt = .{
67 .ty = sv.ty,
68 .val = try sv.child.intern(zcu, arena),
69 } }),
70 .repeated => |sv| try ip.get(gpa, .{ .aggregate = .{
71 .ty = sv.ty,
72 .storage = .{ .repeated_elem = try sv.child.intern(zcu, arena) },
73 } }),
74 .bytes => |b| try ip.get(gpa, .{ .aggregate = .{
75 .ty = b.ty,
76 .storage = .{ .bytes = b.data },
77 } }),
78 .aggregate => |a| {
79 const elems = try arena.alloc(InternPool.Index, a.elems.len);
80 for (a.elems, elems) |mut_elem, *interned_elem| {
81 interned_elem.* = try mut_elem.intern(zcu, arena);
82 }
83 return ip.get(gpa, .{ .aggregate = .{
84 .ty = a.ty,
85 .storage = .{ .elems = elems },
86 } });
87 },
88 .slice => |s| try ip.get(gpa, .{ .slice = .{
89 .ty = s.ty,
90 .ptr = try s.ptr.intern(zcu, arena),
91 .len = try s.len.intern(zcu, arena),
92 } }),
93 .un => |u| try ip.get(gpa, .{ .un = .{
94 .ty = u.ty,
95 .tag = u.tag,
96 .val = try u.payload.intern(zcu, arena),
97 } }),
98 };
99 }
100
101 /// Un-interns the top level of this `MutableValue`, if applicable.
102 /// * Non-error error unions use `eu_payload`
103 /// * Non-null optionals use `eu_payload
104 /// * Slices use `slice`
105 /// * Unions use `un`
106 /// * Aggregates use `repeated` or `bytes` or `aggregate`
107 /// If `!allow_bytes`, the `bytes` representation will not be used.
108 /// If `!allow_repeated`, the `repeated` representation will not be used.
109 pub fn unintern(
110 mv: *MutableValue,
111 zcu: *Zcu,
112 arena: Allocator,
113 allow_bytes: bool,
114 allow_repeated: bool,
115 ) Allocator.Error!void {
116 const ip = &zcu.intern_pool;
117 const gpa = zcu.gpa;
118 switch (mv.*) {
119 .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
120 .opt => |opt| if (opt.val != .none) {
121 const mut_payload = try arena.create(MutableValue);
122 mut_payload.* = .{ .interned = opt.val };
123 mv.* = .{ .opt_payload = .{
124 .ty = opt.ty,
125 .child = mut_payload,
126 } };
127 },
128 .error_union => |eu| switch (eu.val) {
129 .err_name => {},
130 .payload => |payload| {
131 const mut_payload = try arena.create(MutableValue);
132 mut_payload.* = .{ .interned = payload };
133 mv.* = .{ .eu_payload = .{
134 .ty = eu.ty,
135 .child = mut_payload,
136 } };
137 },
138 },
139 .slice => |slice| {
140 const ptr = try arena.create(MutableValue);
141 const len = try arena.create(MutableValue);
142 ptr.* = .{ .interned = slice.ptr };
143 len.* = .{ .interned = slice.len };
144 mv.* = .{ .slice = .{
145 .ty = slice.ty,
146 .ptr = ptr,
147 .len = len,
148 } };
149 },
150 .un => |un| {
151 const payload = try arena.create(MutableValue);
152 payload.* = .{ .interned = un.val };
153 mv.* = .{ .un = .{
154 .ty = un.ty,
155 .tag = un.tag,
156 .payload = payload,
157 } };
158 },
159 .aggregate => |agg| switch (agg.storage) {
160 .bytes => |bytes| {
161 assert(bytes.len == ip.aggregateTypeLenIncludingSentinel(agg.ty));
162 assert(ip.childType(agg.ty) == .u8_type);
163 if (allow_bytes) {
164 const arena_bytes = try arena.alloc(u8, bytes.len);
165 @memcpy(arena_bytes, bytes);
166 mv.* = .{ .bytes = .{
167 .ty = agg.ty,
168 .data = arena_bytes,
169 } };
170 } else {
171 const mut_elems = try arena.alloc(MutableValue, bytes.len);
172 for (bytes, mut_elems) |b, *mut_elem| {
173 mut_elem.* = .{ .interned = try ip.get(gpa, .{ .int = .{
174 .ty = .u8_type,
175 .storage = .{ .u64 = b },
176 } }) };
177 }
178 mv.* = .{ .aggregate = .{
179 .ty = agg.ty,
180 .elems = mut_elems,
181 } };
182 }
183 },
184 .elems => |elems| {
185 assert(elems.len == ip.aggregateTypeLenIncludingSentinel(agg.ty));
186 const mut_elems = try arena.alloc(MutableValue, elems.len);
187 for (elems, mut_elems) |interned_elem, *mut_elem| {
188 mut_elem.* = .{ .interned = interned_elem };
189 }
190 mv.* = .{ .aggregate = .{
191 .ty = agg.ty,
192 .elems = mut_elems,
193 } };
194 },
195 .repeated_elem => |val| {
196 if (allow_repeated) {
197 const repeated_val = try arena.create(MutableValue);
198 repeated_val.* = .{ .interned = val };
199 mv.* = .{ .repeated = .{
200 .ty = agg.ty,
201 .child = repeated_val,
202 } };
203 } else {
204 const len = ip.aggregateTypeLenIncludingSentinel(agg.ty);
205 const mut_elems = try arena.alloc(MutableValue, @intCast(len));
206 @memset(mut_elems, .{ .interned = val });
207 mv.* = .{ .aggregate = .{
208 .ty = agg.ty,
209 .elems = mut_elems,
210 } };
211 }
212 },
213 },
214 .undef => |ty_ip| switch (Type.fromInterned(ty_ip).zigTypeTag(zcu)) {
215 .Struct, .Array, .Vector => |type_tag| {
216 const ty = Type.fromInterned(ty_ip);
217 const opt_sent = ty.sentinel(zcu);
218 if (type_tag == .Struct or opt_sent != null or !allow_repeated) {
219 const len_no_sent = ip.aggregateTypeLen(ty_ip);
220 const elems = try arena.alloc(MutableValue, @intCast(len_no_sent + @intFromBool(opt_sent != null)));
221 switch (type_tag) {
222 .Array, .Vector => {
223 const elem_ty = ip.childType(ty_ip);
224 const undef_elem = try ip.get(gpa, .{ .undef = elem_ty });
225 @memset(elems[0..@intCast(len_no_sent)], .{ .interned = undef_elem });
226 },
227 .Struct => for (elems[0..@intCast(len_no_sent)], 0..) |*mut_elem, i| {
228 const field_ty = ty.structFieldType(i, zcu).toIntern();
229 mut_elem.* = .{ .interned = try ip.get(gpa, .{ .undef = field_ty }) };
230 },
231 else => unreachable,
232 }
233 if (opt_sent) |s| elems[@intCast(len_no_sent)] = .{ .interned = s.toIntern() };
234 mv.* = .{ .aggregate = .{
235 .ty = ty_ip,
236 .elems = elems,
237 } };
238 } else {
239 const repeated_val = try arena.create(MutableValue);
240 repeated_val.* = .{
241 .interned = try ip.get(gpa, .{ .undef = ip.childType(ty_ip) }),
242 };
243 mv.* = .{ .repeated = .{
244 .ty = ty_ip,
245 .child = repeated_val,
246 } };
247 }
248 },
249 .Union => {
250 const payload = try arena.create(MutableValue);
251 // HACKHACK: this logic is silly, but Sema detects it and reverts the change where needed.
252 // See comment at the top of `Sema.beginComptimePtrMutationInner`.
253 payload.* = .{ .interned = .undef };
254 mv.* = .{ .un = .{
255 .ty = ty_ip,
256 .tag = .none,
257 .payload = payload,
258 } };
259 },
260 .Pointer => {
261 const ptr_ty = ip.indexToKey(ty_ip).ptr_type;
262 if (ptr_ty.flags.size != .Slice) return;
263 const ptr = try arena.create(MutableValue);
264 const len = try arena.create(MutableValue);
265 ptr.* = .{ .interned = try ip.get(gpa, .{ .undef = ip.slicePtrType(ty_ip) }) };
266 len.* = .{ .interned = try ip.get(gpa, .{ .undef = .usize_type }) };
267 mv.* = .{ .slice = .{
268 .ty = ty_ip,
269 .ptr = ptr,
270 .len = len,
271 } };
272 },
273 else => {},
274 },
275 else => {},
276 },
277 .bytes => |bytes| if (!allow_bytes) {
278 const elems = try arena.alloc(MutableValue, bytes.data.len);
279 for (bytes.data, elems) |byte, *interned_byte| {
280 interned_byte.* = .{ .interned = try ip.get(gpa, .{ .int = .{
281 .ty = .u8_type,
282 .storage = .{ .u64 = byte },
283 } }) };
284 }
285 mv.* = .{ .aggregate = .{
286 .ty = bytes.ty,
287 .elems = elems,
288 } };
289 },
290 else => {},
291 }
292 }
293
294 /// Get a pointer to the `MutableValue` associated with a field/element.
295 /// The returned pointer can be safety mutated through to modify the field value.
296 /// The returned pointer is valid until the representation of `mv` changes.
297 /// This function does *not* support accessing the ptr/len field of slices.
298 pub fn elem(
299 mv: *MutableValue,
300 zcu: *Zcu,
301 arena: Allocator,
302 field_idx: usize,
303 ) Allocator.Error!*MutableValue {
304 const ip = &zcu.intern_pool;
305 const gpa = zcu.gpa;
306 // Convert to the `aggregate` representation.
307 switch (mv) {
308 .eu_payload, .opt_payload, .slice, .un => unreachable,
309 .interned => {
310 try mv.unintern(zcu, arena, false, false);
311 },
312 .bytes => |bytes| {
313 const elems = try arena.alloc(MutableValue, bytes.data.len);
314 for (bytes.data, elems) |byte, interned_byte| {
315 interned_byte.* = try ip.get(gpa, .{ .int = .{
316 .ty = .u8_type,
317 .storage = .{ .u64 = byte },
318 } });
319 }
320 mv.* = .{ .aggregate = .{
321 .ty = bytes.ty,
322 .elems = elems,
323 } };
324 },
325 .repeated => |repeated| {
326 const len = ip.aggregateTypeLenIncludingSentinel(repeated.ty);
327 const elems = try arena.alloc(MutableValue, @intCast(len));
328 @memset(elems, repeated.child.*);
329 mv.* = .{ .aggregate = .{
330 .ty = repeated.ty,
331 .elems = elems,
332 } };
333 },
334 .aggregate => {},
335 }
336 return &mv.aggregate.elems[field_idx];
337 }
338
339 /// Modify a single field of a `MutableValue` which represents an aggregate or slice, leaving others
340 /// untouched. When an entire field must be modified, this should be used in preference to `elemPtr`
341 /// to allow for an optimal representation.
342 /// For slices, uses `Value.slice_ptr_index` and `Value.slice_len_index`.
343 pub fn setElem(
344 mv: *MutableValue,
345 zcu: *Zcu,
346 arena: Allocator,
347 field_idx: usize,
348 field_val: MutableValue,
349 ) Allocator.Error!void {
350 const ip = &zcu.intern_pool;
351 const is_trivial_int = field_val.isTrivialInt(zcu);
352 try mv.unintern(arena, is_trivial_int, true);
353 switch (mv) {
354 .interned,
355 .eu_payload,
356 .opt_payload,
357 .un,
358 => unreachable,
359 .slice => |*s| switch (field_idx) {
360 Value.slice_ptr_index => s.ptr = field_val,
361 Value.slice_len_index => s.len = field_val,
362 },
363 .bytes => |b| {
364 assert(is_trivial_int);
365 assert(field_val.typeOf() == Type.u8);
366 b.data[field_idx] = Value.fromInterned(field_val.interned).toUnsignedInt(zcu);
367 },
368 .repeated => |r| {
369 if (field_val.eqlTrivial(r.child.*)) return;
370 // We must switch to either the `aggregate` or the `bytes` representation.
371 const len_inc_sent = ip.aggregateTypeLenIncludingSentinel(r.ty);
372 if (ip.zigTypeTag(r.ty) != .Struct and
373 is_trivial_int and
374 Type.fromInterned(r.ty).childType(zcu) == .u8_type and
375 r.child.isTrivialInt(zcu))
376 {
377 // We can use the `bytes` representation.
378 const bytes = try arena.alloc(u8, @intCast(len_inc_sent));
379 const repeated_byte = Value.fromInterned(r.child.interned).getUnsignedInt(zcu);
380 @memset(bytes, repeated_byte);
381 bytes[field_idx] = Value.fromInterned(field_val.interned).getUnsignedInt(zcu);
382 mv.* = .{ .bytes = .{
383 .ty = r.ty,
384 .data = bytes,
385 } };
386 } else {
387 // We must use the `aggregate` representation.
388 const mut_elems = try arena.alloc(u8, @intCast(len_inc_sent));
389 @memset(mut_elems, r.child.*);
390 mut_elems[field_idx] = field_val;
391 mv.* = .{ .aggregate = .{
392 .ty = r.ty,
393 .elems = mut_elems,
394 } };
395 }
396 },
397 .aggregate => |a| {
398 a.elems[field_idx] = field_val;
399 const is_struct = ip.zigTypeTag(a.ty) == .Struct;
400 // Attempt to switch to a more efficient representation.
401 const is_repeated = for (a.elems) |e| {
402 if (!e.eqlTrivial(field_val)) break false;
403 } else true;
404 if (is_repeated) {
405 // Switch to `repeated` repr
406 const mut_repeated = try arena.create(MutableValue);
407 mut_repeated.* = field_val;
408 mv.* = .{ .repeated = .{
409 .ty = a.ty,
410 .child = mut_repeated,
411 } };
412 } else if (!is_struct and is_trivial_int and Type.fromInterned(a.ty).childType(zcu).toIntern() == .u8_type) {
413 // See if we can switch to `bytes` repr
414 for (a.elems) |e| {
415 switch (e) {
416 else => break,
417 .interned => |ip_index| switch (ip.indexToKey(ip_index)) {
418 else => break,
419 .int => |int| switch (int.storage) {
420 .u64, .i64, .big_int => {},
421 .lazy_align, .lazy_size => break,
422 },
423 },
424 }
425 } else {
426 const bytes = try arena.alloc(u8, a.elems.len);
427 for (a.elems, bytes) |elem_val, *b| {
428 b.* = Value.fromInterned(elem_val.interned).toUnsignedInt(zcu);
429 }
430 mv.* = .{ .bytes = .{
431 .ty = a.ty,
432 .data = bytes,
433 } };
434 }
435 }
436 },
437 }
438 }
439
440 /// Get the value of a single field of a `MutableValue` which represents an aggregate or slice.
441 /// For slices, uses `Value.slice_ptr_index` and `Value.slice_len_index`.
442 pub fn getElem(
443 mv: MutableValue,
444 zcu: *Zcu,
445 field_idx: usize,
446 ) Allocator.Error!MutableValue {
447 return switch (mv) {
448 .eu_payload,
449 .opt_payload,
450 => unreachable,
451 .interned => |ip_index| {
452 const ty = Type.fromInterned(zcu.intern_pool.typeOf(ip_index));
453 switch (ty.zigTypeTag(zcu)) {
454 .Array, .Vector => return .{ .interned = (try Value.fromInterned(ip_index).elemValue(zcu, field_idx)).toIntern() },
455 .Struct, .Union => return .{ .interned = (try Value.fromInterned(ip_index).fieldValue(zcu, field_idx)).toIntern() },
456 .Pointer => {
457 assert(ty.isSlice(zcu));
458 return switch (field_idx) {
459 Value.slice_ptr_index => .{ .interned = Value.fromInterned(ip_index).slicePtr(zcu).toIntern() },
460 Value.slice_len_index => .{ .interned = switch (zcu.intern_pool.indexToKey(ip_index)) {
461 .undef => try zcu.intern(.{ .undef = .usize_type }),
462 .slice => |s| s.len,
463 else => unreachable,
464 } },
465 else => unreachable,
466 };
467 },
468 else => unreachable,
469 }
470 },
471 .un => |un| {
472 // TODO assert the tag is correct
473 return un.payload.*;
474 },
475 .slice => |s| switch (field_idx) {
476 Value.slice_ptr_index => s.ptr.*,
477 Value.slice_len_index => s.len.*,
478 else => unreachable,
479 },
480 .bytes => |b| .{ .interned = try zcu.intern(.{ .int = .{
481 .ty = .u8_type,
482 .storage = .{ .u64 = b.data[field_idx] },
483 } }) },
484 .repeated => |r| r.child.*,
485 .aggregate => |a| a.elems[field_idx],
486 };
487 }
488
489 fn isTrivialInt(mv: MutableValue, zcu: *Zcu) bool {
490 return switch (mv) {
491 else => false,
492 .interned => |ip_index| switch (zcu.intern_pool.indexToKey(ip_index)) {
493 else => false,
494 .int => |int| switch (int.storage) {
495 .u64, .i64, .big_int => true,
496 .lazy_align, .lazy_size => false,
497 },
498 },
499 };
500 }
501
502 pub fn typeOf(mv: MutableValue, zcu: *Zcu) Type {
503 return switch (mv) {
504 .interned => |ip_index| Type.fromInterned(zcu.intern_pool.typeOf(ip_index)),
505 inline else => |x| Type.fromInterned(x.ty),
506 };
507 }
508};