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 {...@@ -3598,7 +3598,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult {
35983598
3599 const old_has_tv = decl.has_tv;3599 const old_has_tv = decl.has_tv;
3600 // The following values are ignored if `!old_has_tv`3600 // 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;
3602 const old_val = decl.val;3602 const old_val = decl.val;
3603 const old_align = decl.alignment;3603 const old_align = decl.alignment;
3604 const old_linksection = decl.@"linksection";3604 const old_linksection = decl.@"linksection";
src/Sema.zig+219-438
...@@ -139,8 +139,7 @@ const MaybeComptimeAlloc = struct {...@@ -139,8 +139,7 @@ const MaybeComptimeAlloc = struct {
139};139};
140140
141const ComptimeAlloc = struct {141const ComptimeAlloc = struct {
142 ty: Type,142 val: MutableValue,
143 val: Value,
144 is_const: bool,143 is_const: bool,
145 /// `.none` indicates that the alignment is the natural alignment of `val`.144 /// `.none` indicates that the alignment is the natural alignment of `val`.
146 alignment: Alignment,145 alignment: Alignment,
...@@ -153,8 +152,7 @@ const ComptimeAlloc = struct {...@@ -153,8 +152,7 @@ const ComptimeAlloc = struct {
153fn newComptimeAlloc(sema: *Sema, block: *Block, ty: Type, alignment: Alignment) !ComptimeAllocIndex {152fn newComptimeAlloc(sema: *Sema, block: *Block, ty: Type, alignment: Alignment) !ComptimeAllocIndex {
154 const idx = sema.comptime_allocs.items.len;153 const idx = sema.comptime_allocs.items.len;
155 try sema.comptime_allocs.append(sema.gpa, .{154 try sema.comptime_allocs.append(sema.gpa, .{
156 .ty = ty,155 .val = .{ .interned = try sema.mod.intern(.{ .undef = ty.toIntern() }) },
157 .val = Value.fromInterned(try sema.mod.intern(.{ .undef = ty.toIntern() })),
158 .is_const = false,156 .is_const = false,
159 .alignment = alignment,157 .alignment = alignment,
160 .runtime_index = block.runtime_index,158 .runtime_index = block.runtime_index,
...@@ -175,6 +173,7 @@ const log = std.log.scoped(.sema);...@@ -175,6 +173,7 @@ const log = std.log.scoped(.sema);
175173
176const Sema = @This();174const Sema = @This();
177const Value = @import("Value.zig");175const Value = @import("Value.zig");
176const MutableValue = @import("mutable_value.zig").MutableValue;
178const Type = @import("type.zig").Type;177const Type = @import("type.zig").Type;
179const TypedValue = @import("TypedValue.zig");178const TypedValue = @import("TypedValue.zig");
180const Air = @import("Air.zig");179const Air = @import("Air.zig");
...@@ -3762,10 +3761,10 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro...@@ -3762,10 +3761,10 @@ fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
3762 if (!sema.isComptimeMutablePtr(ptr_val)) break :already_ct;3761 if (!sema.isComptimeMutablePtr(ptr_val)) break :already_ct;
3763 const alloc_index = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr.comptime_alloc;3762 const alloc_index = mod.intern_pool.indexToKey(ptr_val.toIntern()).ptr.addr.comptime_alloc;
3764 const ct_alloc = sema.getComptimeAlloc(alloc_index);3763 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);
3766 if (Value.fromInterned(interned).canMutateComptimeVarState(mod)) {3765 if (Value.fromInterned(interned).canMutateComptimeVarState(mod)) {
3767 // Preserve the comptime alloc, just make the pointer const.3766 // Preserve the comptime alloc, just make the pointer const.
3768 ct_alloc.val = Value.fromInterned(interned);3767 ct_alloc.val = .{ .interned = interned };
3769 ct_alloc.is_const = true;3768 ct_alloc.is_const = true;
3770 return sema.makePtrConst(block, alloc);3769 return sema.makePtrConst(block, alloc);
3771 } else {3770 } else {
...@@ -4030,7 +4029,7 @@ fn finishResolveComptimeKnownAllocPtr(...@@ -4030,7 +4029,7 @@ fn finishResolveComptimeKnownAllocPtr(
4030 const alloc_index = existing_comptime_alloc orelse a: {4029 const alloc_index = existing_comptime_alloc orelse a: {
4031 const idx = try sema.newComptimeAlloc(block, alloc_ty.childType(zcu), alloc_ty.ptrAlignment(zcu));4030 const idx = try sema.newComptimeAlloc(block, alloc_ty.childType(zcu), alloc_ty.ptrAlignment(zcu));
4032 const alloc = sema.getComptimeAlloc(idx);4031 const alloc = sema.getComptimeAlloc(idx);
4033 alloc.val = Value.fromInterned(result_val);4032 alloc.val = .{ .interned = result_val };
4034 break :a idx;4033 break :a idx;
4035 };4034 };
4036 sema.getComptimeAlloc(alloc_index).is_const = true;4035 sema.getComptimeAlloc(alloc_index).is_const = true;
...@@ -4193,7 +4192,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com...@@ -4193,7 +4192,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
4193 .anon_decl => |a| a.val,4192 .anon_decl => |a| a.val,
4194 .comptime_alloc => |i| val: {4193 .comptime_alloc => |i| val: {
4195 const alloc = sema.getComptimeAlloc(i);4194 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);
4197 },4196 },
4198 else => unreachable,4197 else => unreachable,
4199 };4198 };
...@@ -5597,7 +5596,7 @@ fn storeToInferredAllocComptime(...@@ -5597,7 +5596,7 @@ fn storeToInferredAllocComptime(
5597 } });5596 } });
5598 } else {5597 } else {
5599 const alloc_index = try sema.newComptimeAlloc(block, operand_ty, iac.alignment);5598 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() };
5601 iac.ptr = try zcu.intern(.{ .ptr = .{5600 iac.ptr = try zcu.intern(.{ .ptr = .{
5602 .ty = alloc_ty.toIntern(),5601 .ty = alloc_ty.toIntern(),
5603 .addr = .{ .comptime_alloc = alloc_index },5602 .addr = .{ .comptime_alloc = alloc_index },
...@@ -30655,21 +30654,22 @@ fn storePtrVal(...@@ -30655,21 +30654,22 @@ fn storePtrVal(
30655 .opv => {},30654 .opv => {},
30656 .direct => |val_ptr| {30655 .direct => |val_ptr| {
30657 if (mut_kit.root == .comptime_field) {30656 if (mut_kit.root == .comptime_field) {
30658 val_ptr.* = Value.fromInterned((try val_ptr.intern(operand_ty, mod)));30657 val_ptr.* = .{ .interned = try val_ptr.intern(mod, sema.arena) };
30659 if (!operand_val.eql(val_ptr.*, operand_ty, mod)) {30658 if (operand_val.toIntern() != val_ptr.interned) {
30660 // TODO use failWithInvalidComptimeFieldStore30659 // TODO use failWithInvalidComptimeFieldStore
30661 return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});30660 return sema.fail(block, src, "value stored in comptime field does not match the default value of the field", .{});
30662 }30661 }
30663 return;30662 return;
30664 }30663 }
30665 val_ptr.* = Value.fromInterned((try operand_val.intern(operand_ty, mod)));30664 val_ptr.* = .{ .interned = operand_val.toIntern() };
30666 },30665 },
30667 .reinterpret => |reinterpret| {30666 .reinterpret => |reinterpret| {
30668 try sema.resolveTypeLayout(mut_kit.ty);30667 try sema.resolveTypeLayout(mut_kit.ty);
30669 const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod));30668 const abi_size = try sema.usizeCast(block, src, mut_kit.ty.abiSize(mod));
30670 const buffer = try sema.gpa.alloc(u8, abi_size);30669 const buffer = try sema.gpa.alloc(u8, abi_size);
30671 defer sema.gpa.free(buffer);30670 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) {
30673 error.OutOfMemory => return error.OutOfMemory,30673 error.OutOfMemory => return error.OutOfMemory,
30674 error.ReinterpretDeclRef => unreachable,30674 error.ReinterpretDeclRef => unreachable,
30675 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already30675 error.IllDefinedMemoryLayout => unreachable, // Sema was supposed to emit a compile error already
...@@ -30693,7 +30693,7 @@ fn storePtrVal(...@@ -30693,7 +30693,7 @@ fn storePtrVal(
30693 error.IllDefinedMemoryLayout => unreachable,30693 error.IllDefinedMemoryLayout => unreachable,
30694 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),30694 error.Unimplemented => return sema.fail(block, src, "TODO: implement readFromMemory for type '{}'", .{mut_kit.ty.fmt(mod)}),
30695 };30695 };
30696 reinterpret.val_ptr.* = Value.fromInterned((try val.intern(mut_kit.ty, mod)));30696 reinterpret.val_ptr.* = .{ .interned = val.toIntern() };
30697 },30697 },
30698 .bad_decl_ty, .bad_ptr_ty => {30698 .bad_decl_ty, .bad_ptr_ty => {
30699 // TODO show the decl declaration site in a note and explain whether the decl30699 // TODO show the decl declaration site in a note and explain whether the decl
...@@ -30718,11 +30718,11 @@ const ComptimePtrMutationKit = struct {...@@ -30718,11 +30718,11 @@ const ComptimePtrMutationKit = struct {
30718 opv,30718 opv,
30719 /// The pointer type matches the actual comptime Value so a direct30719 /// The pointer type matches the actual comptime Value so a direct
30720 /// modification is possible.30720 /// modification is possible.
30721 direct: *Value,30721 direct: *MutableValue,
30722 /// The largest parent Value containing pointee and having a well-defined memory layout.30722 /// The largest parent Value containing pointee and having a well-defined memory layout.
30723 /// This is used for bitcasting, if direct dereferencing failed.30723 /// This is used for bitcasting, if direct dereferencing failed.
30724 reinterpret: struct {30724 reinterpret: struct {
30725 val_ptr: *Value,30725 val_ptr: *MutableValue,
30726 byte_offset: usize,30726 byte_offset: usize,
30727 /// If set, write the operand to packed memory30727 /// If set, write the operand to packed memory
30728 write_packed: bool = false,30728 write_packed: bool = false,
...@@ -30754,15 +30754,15 @@ fn beginComptimePtrMutation(...@@ -30754,15 +30754,15 @@ fn beginComptimePtrMutation(
30754 .decl, .anon_decl, .int => unreachable, // isComptimeMutablePtr has been checked already30754 .decl, .anon_decl, .int => unreachable, // isComptimeMutablePtr has been checked already
30755 .comptime_alloc => |alloc_index| {30755 .comptime_alloc => |alloc_index| {
30756 const alloc = sema.getComptimeAlloc(alloc_index);30756 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 });
30758 },30758 },
30759 .comptime_field => |comptime_field| {30759 .comptime_field => |comptime_field| {
30760 const duped = try sema.arena.create(Value);30760 const duped = try sema.arena.create(MutableValue);
30761 duped.* = Value.fromInterned(comptime_field);30761 duped.* = .{ .interned = comptime_field };
30762 return sema.beginComptimePtrMutationInner(30762 return sema.beginComptimePtrMutationInner(
30763 block,30763 block,
30764 src,30764 src,
30765 Type.fromInterned(mod.intern_pool.typeOf(comptime_field)),30765 duped.typeOf(mod),
30766 duped,30766 duped,
30767 ptr_elem_ty,30767 ptr_elem_ty,
30768 .comptime_field,30768 .comptime_field,
...@@ -30775,36 +30775,28 @@ fn beginComptimePtrMutation(...@@ -30775,36 +30775,28 @@ fn beginComptimePtrMutation(
30775 .opv => unreachable,30775 .opv => unreachable,
30776 .direct => |val_ptr| {30776 .direct => |val_ptr| {
30777 const payload_ty = parent.ty.errorUnionPayload(mod);30777 const payload_ty = parent.ty.errorUnionPayload(mod);
30778 if (val_ptr.ip_index == .none and val_ptr.tag() == .eu_payload) {30778 try val_ptr.unintern(mod, sema.arena, false, false);
30779 return ComptimePtrMutationKit{30779 if (val_ptr.* == .interned) {
30780 .root = parent.root,
30781 .pointee = .{ .direct = &val_ptr.castTag(.eu_payload).?.data },
30782 .ty = payload_ty,
30783 };
30784 } else {
30785 // An error union has been initialized to undefined at comptime and now we30780 // An error union has been initialized to undefined at comptime and now we
30786 // are for the first time setting the payload. We must change the30781 // are for the first time setting the payload. We must change the
30787 // representation of the error union from `undef` to `opt_payload`.30782 // representation of the error union to `eu_payload`.
3078830783 const child = try sema.arena.create(MutableValue);
30789 const payload = try sema.arena.create(Value.Payload.SubValue);30784 child.* = .{ .interned = try mod.intern(.{ .undef = payload_ty.toIntern() }) };
30790 payload.* = .{30785 val_ptr.* = .{ .eu_payload = .{
30791 .base = .{ .tag = .eu_payload },30786 .ty = parent.ty.toIntern(),
30792 .data = Value.fromInterned((try mod.intern(.{ .undef = payload_ty.toIntern() }))),30787 .child = child,
30793 };30788 } };
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 };
30802 }30789 }
30790 return .{
30791 .root = parent.root,
30792 .pointee = .{ .direct = val_ptr.eu_payload.child },
30793 .ty = payload_ty,
30794 };
30803 },30795 },
30804 .bad_decl_ty, .bad_ptr_ty => return parent,30796 .bad_decl_ty, .bad_ptr_ty => return parent,
30805 // Even though the parent value type has well-defined memory layout, our30797 // Even though the parent value type has well-defined memory layout, our
30806 // pointer type does not.30798 // pointer type does not.
30807 .reinterpret => return ComptimePtrMutationKit{30799 .reinterpret => return .{
30808 .root = parent.root,30800 .root = parent.root,
30809 .pointee = .bad_ptr_ty,30801 .pointee = .bad_ptr_ty,
30810 .ty = eu_ty,30802 .ty = eu_ty,
...@@ -30818,46 +30810,28 @@ fn beginComptimePtrMutation(...@@ -30818,46 +30810,28 @@ fn beginComptimePtrMutation(
30818 .opv => unreachable,30810 .opv => unreachable,
30819 .direct => |val_ptr| {30811 .direct => |val_ptr| {
30820 const payload_ty = parent.ty.optionalChild(mod);30812 const payload_ty = parent.ty.optionalChild(mod);
30821 switch (val_ptr.ip_index) {30813 try val_ptr.unintern(mod, sema.arena, false, false);
30822 .none => return ComptimePtrMutationKit{30814 if (val_ptr.* == .interned) {
30823 .root = parent.root,30815 // An optional has been initialized to undefined at comptime and now we
30824 .pointee = .{ .direct = &val_ptr.castTag(.opt_payload).?.data },30816 // are for the first time setting the payload. We must change the
30825 .ty = payload_ty,30817 // representation of the optional to `opt_payload`.
30826 },30818 const child = try sema.arena.create(MutableValue);
30827 else => {30819 child.* = .{ .interned = try mod.intern(.{ .undef = payload_ty.toIntern() }) };
30828 const payload_val = switch (mod.intern_pool.indexToKey(val_ptr.ip_index)) {30820 val_ptr.* = .{ .opt_payload = .{
30829 .undef => try mod.intern(.{ .undef = payload_ty.toIntern() }),30821 .ty = parent.ty.toIntern(),
30830 .opt => |opt| switch (opt.val) {30822 .child = child,
30831 .none => try mod.intern(.{ .undef = payload_ty.toIntern() }),30823 } };
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 },
30855 }30824 }
30825 return .{
30826 .root = parent.root,
30827 .pointee = .{ .direct = val_ptr.opt_payload.child },
30828 .ty = payload_ty,
30829 };
30856 },30830 },
30857 .bad_decl_ty, .bad_ptr_ty => return parent,30831 .bad_decl_ty, .bad_ptr_ty => return parent,
30858 // Even though the parent value type has well-defined memory layout, our30832 // Even though the parent value type has well-defined memory layout, our
30859 // pointer type does not.30833 // pointer type does not.
30860 .reinterpret => return ComptimePtrMutationKit{30834 .reinterpret => return .{
30861 .root = parent.root,30835 .root = parent.root,
30862 .pointee = .bad_ptr_ty,30836 .pointee = .bad_ptr_ty,
30863 .ty = opt_ty,30837 .ty = opt_ty,
...@@ -30916,106 +30890,28 @@ fn beginComptimePtrMutation(...@@ -30916,106 +30890,28 @@ fn beginComptimePtrMutation(
30916 };30890 };
30917 }30891 }
3091830892
30919 switch (val_ptr.ip_index) {30893 try val_ptr.unintern(mod, sema.arena, false, false);
30920 .none => switch (val_ptr.tag()) {30894
30921 .bytes => {30895 const aggregate = switch (val_ptr.*) {
30922 // An array is memory-optimized to store a slice of bytes, but we are about30896 .interned,
30923 // to modify an individual field and the representation has to change.30897 .bytes,
30924 // If we wanted to avoid this, there would need to be special detection30898 .repeated,
30925 // elsewhere to identify when writing a value to an array element that is stored30899 .eu_payload,
30926 // using the `bytes` tag, and handle it without making a call to this function.30900 .opt_payload,
30927 const arena = sema.arena;30901 .slice,
3092830902 .un,
30929 const bytes = val_ptr.castTag(.bytes).?.data;30903 => unreachable,
30930 const dest_len = parent.ty.arrayLenIncludingSentinel(mod);30904 .aggregate => |*a| a,
30931 // bytes.len may be one greater than dest_len because of the case when30905 };
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 ),
3098930906
30990 else => unreachable,30907 return sema.beginComptimePtrMutationInner(
30991 },30908 block,
30992 else => switch (mod.intern_pool.indexToKey(val_ptr.toIntern())) {30909 src,
30993 .undef => {30910 elem_ty,
30994 // An array has been initialized to undefined at comptime and now we30911 &aggregate.elems[@intCast(elem_ptr.index)],
30995 // are for the first time setting an element. We must change the representation30912 ptr_elem_ty,
30996 // of the array from `undef` to `array`.30913 parent.root,
30997 const arena = sema.arena;30914 );
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 }
31019 },30915 },
31020 else => {30916 else => {
31021 if (elem_ptr.index != 0) {30917 if (elem_ptr.index != 0) {
...@@ -31039,7 +30935,7 @@ fn beginComptimePtrMutation(...@@ -31039,7 +30935,7 @@ fn beginComptimePtrMutation(
31039 if (!base_elem_ty.hasWellDefinedLayout(mod)) {30935 if (!base_elem_ty.hasWellDefinedLayout(mod)) {
31040 // Even though the parent value type has well-defined memory layout, our30936 // Even though the parent value type has well-defined memory layout, our
31041 // pointer type does not.30937 // pointer type does not.
31042 return ComptimePtrMutationKit{30938 return .{
31043 .root = parent.root,30939 .root = parent.root,
31044 .pointee = .bad_ptr_ty,30940 .pointee = .bad_ptr_ty,
31045 .ty = base_elem_ty,30941 .ty = base_elem_ty,
...@@ -31049,7 +30945,7 @@ fn beginComptimePtrMutation(...@@ -31049,7 +30945,7 @@ fn beginComptimePtrMutation(
31049 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);30945 const elem_abi_size_u64 = try sema.typeAbiSize(base_elem_ty);
31050 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);30946 const elem_abi_size = try sema.usizeCast(block, src, elem_abi_size_u64);
31051 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);30947 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
31052 return ComptimePtrMutationKit{30948 return .{
31053 .root = parent.root,30949 .root = parent.root,
31054 .pointee = .{ .reinterpret = .{30950 .pointee = .{ .reinterpret = .{
31055 .val_ptr = reinterpret.val_ptr,30951 .val_ptr = reinterpret.val_ptr,
...@@ -31068,56 +30964,68 @@ fn beginComptimePtrMutation(...@@ -31068,56 +30964,68 @@ fn beginComptimePtrMutation(
31068 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(field_ptr.base), base_child_ty);30964 var parent = try sema.beginComptimePtrMutation(block, src, Value.fromInterned(field_ptr.base), base_child_ty);
31069 switch (parent.pointee) {30965 switch (parent.pointee) {
31070 .opv => unreachable,30966 .opv => unreachable,
31071 .direct => |val_ptr| switch (val_ptr.ip_index) {30967 .direct => |val_ptr| {
31072 .empty_struct => {30968 try val_ptr.unintern(mod, sema.arena, false, false);
31073 const duped = try sema.arena.create(Value);30969 switch (val_ptr.*) {
31074 duped.* = val_ptr.*;30970 .interned,
31075 return beginComptimePtrMutationInner(30971 .eu_payload,
31076 sema,30972 .opt_payload,
31077 block,30973 .repeated,
31078 src,30974 .bytes,
31079 parent.ty.structFieldType(field_index, mod),30975 => unreachable,
31080 duped,30976 .aggregate => |*a| return sema.beginComptimePtrMutationInner(
31081 ptr_elem_ty,
31082 parent.root,
31083 );
31084 },
31085 .none => switch (val_ptr.tag()) {
31086 .aggregate => return beginComptimePtrMutationInner(
31087 sema,
31088 block,30977 block,
31089 src,30978 src,
31090 parent.ty.structFieldType(field_index, mod),30979 parent.ty.structFieldType(field_index, mod),
31091 &val_ptr.castTag(.aggregate).?.data[field_index],30980 &a.elems[field_index],
31092 ptr_elem_ty,30981 ptr_elem_ty,
31093 parent.root,30982 parent.root,
31094 ),30983 ),
31095 .repeated => {30984 .slice => |*s| switch (field_index) {
31096 const arena = sema.arena;30985 Value.slice_ptr_index => return sema.beginComptimePtrMutationInner(
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,
31104 block,30986 block,
31105 src,30987 src,
31106 parent.ty.structFieldType(field_index, mod),30988 parent.ty.slicePtrFieldType(mod),
31107 &elems[field_index],30989 s.ptr,
31108 ptr_elem_ty,30990 ptr_elem_ty,
31109 parent.root,30991 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,
31111 },31002 },
31112 .@"union" => {31003 .un => |*un| {
31113 const payload = &val_ptr.castTag(.@"union").?.data;
31114 const layout = base_child_ty.containerLayout(mod);31004 const layout = base_child_ty.containerLayout(mod);
3111531005
31116 const tag_type = base_child_ty.unionTagTypeHypothetical(mod);31006 const tag_type = base_child_ty.unionTagTypeHypothetical(mod);
31117 const hypothetical_tag = try mod.enumValueFieldIndex(tag_type, field_index);31007 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) {
31119 // We need to set the active field of the union.31027 // We need to set the active field of the union.
31120 payload.tag = hypothetical_tag;31028 un.tag = hypothetical_tag.toIntern();
3112131029
31122 const field_ty = parent.ty.structFieldType(field_index, mod);31030 const field_ty = parent.ty.structFieldType(field_index, mod);
31123 return beginComptimePtrMutationInner(31031 return beginComptimePtrMutationInner(
...@@ -31125,7 +31033,7 @@ fn beginComptimePtrMutation(...@@ -31125,7 +31033,7 @@ fn beginComptimePtrMutation(
31125 block,31033 block,
31126 src,31034 src,
31127 field_ty,31035 field_ty,
31128 &payload.val,31036 un.payload,
31129 ptr_elem_ty,31037 ptr_elem_ty,
31130 parent.root,31038 parent.root,
31131 );31039 );
...@@ -31133,11 +31041,10 @@ fn beginComptimePtrMutation(...@@ -31133,11 +31041,10 @@ fn beginComptimePtrMutation(
31133 // Writing to a different field (a different or unknown tag is active) requires reinterpreting31041 // Writing to a different field (a different or unknown tag is active) requires reinterpreting
31134 // memory of the entire union, which requires knowing its abiSize.31042 // memory of the entire union, which requires knowing its abiSize.
31135 try sema.resolveTypeLayout(parent.ty);31043 try sema.resolveTypeLayout(parent.ty);
31136
31137 // This union value no longer has a well-defined tag type.31044 // This union value no longer has a well-defined tag type.
31138 // The reinterpretation will read it back out as .none.31045 // The reinterpretation will read it back out as .none.
31139 payload.val = try payload.val.unintern(sema.arena, mod);31046 try un.payload.unintern(mod, sema.arena, false, false);
31140 return ComptimePtrMutationKit{31047 return .{
31141 .root = parent.root,31048 .root = parent.root,
31142 .pointee = .{ .reinterpret = .{31049 .pointee = .{ .reinterpret = .{
31143 .val_ptr = val_ptr,31050 .val_ptr = val_ptr,
...@@ -31148,119 +31055,12 @@ fn beginComptimePtrMutation(...@@ -31148,119 +31055,12 @@ fn beginComptimePtrMutation(
31148 };31055 };
31149 }31056 }
31150 },31057 },
31151 .slice => switch (field_index) {31058 }
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 },
31259 },31059 },
31260 .reinterpret => |reinterpret| {31060 .reinterpret => |reinterpret| {
31261 const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod);31061 const field_offset_u64 = base_child_ty.structFieldOffset(field_index, mod);
31262 const field_offset = try sema.usizeCast(block, src, field_offset_u64);31062 const field_offset = try sema.usizeCast(block, src, field_offset_u64);
31263 return ComptimePtrMutationKit{31063 return .{
31264 .root = parent.root,31064 .root = parent.root,
31265 .pointee = .{ .reinterpret = .{31065 .pointee = .{ .reinterpret = .{
31266 .val_ptr = reinterpret.val_ptr,31066 .val_ptr = reinterpret.val_ptr,
...@@ -31280,7 +31080,7 @@ fn beginComptimePtrMutationInner(...@@ -31280,7 +31080,7 @@ fn beginComptimePtrMutationInner(
31280 block: *Block,31080 block: *Block,
31281 src: LazySrcLoc,31081 src: LazySrcLoc,
31282 decl_ty: Type,31082 decl_ty: Type,
31283 decl_val: *Value,31083 decl_val: *MutableValue,
31284 ptr_elem_ty: Type,31084 ptr_elem_ty: Type,
31285 root: ComptimePtrMutationKit.Root,31085 root: ComptimePtrMutationKit.Root,
31286) CompileError!ComptimePtrMutationKit {31086) CompileError!ComptimePtrMutationKit {
...@@ -31288,7 +31088,13 @@ fn beginComptimePtrMutationInner(...@@ -31288,7 +31088,13 @@ fn beginComptimePtrMutationInner(
31288 const target = mod.getTarget();31088 const target = mod.getTarget();
31289 const coerce_ok = (try sema.coerceInMemoryAllowed(block, ptr_elem_ty, decl_ty, true, target, src, src)) == .ok;31089 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
31293 if (coerce_ok) {31099 if (coerce_ok) {
31294 return ComptimePtrMutationKit{31100 return ComptimePtrMutationKit{
...@@ -31334,21 +31140,16 @@ fn beginComptimePtrMutationInner(...@@ -31334,21 +31140,16 @@ fn beginComptimePtrMutationInner(
31334 };31140 };
31335}31141}
3133631142
31337const TypedValueAndOffset = struct {
31338 tv: TypedValue,
31339 byte_offset: usize,
31340};
31341
31342const ComptimePtrLoadKit = struct {31143const ComptimePtrLoadKit = struct {
31343 /// The Value and Type corresponding to the pointee of the provided pointer.31144 /// The Value and Type corresponding to the pointee of the provided pointer.
31344 /// If a direct dereference is not possible, this is null.31145 /// If a direct dereference is not possible, this is null.
31345 pointee: ?TypedValue,31146 pointee: ?MutableValue,
31346 /// The largest parent Value containing `pointee` and having a well-defined memory layout.31147 /// The largest parent Value containing `pointee` and having a well-defined memory layout.
31347 /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null).31148 /// This is used for bitcasting, if direct dereferencing failed (i.e. `pointee` is null).
31348 parent: ?TypedValueAndOffset,31149 parent: ?struct {
31349 /// Whether the `pointee` could be mutated by further31150 val: MutableValue,
31350 /// semantic analysis and a copy must be performed.31151 byte_offset: usize,
31351 is_mutable: bool,31152 },
31352 /// If the root decl could not be used as `parent`, this is the type that31153 /// If the root decl could not be used as `parent`, this is the type that
31353 /// caused that by not having a well-defined layout31154 /// caused that by not having a well-defined layout
31354 ty_without_well_defined_layout: ?Type,31155 ty_without_well_defined_layout: ?Type,
...@@ -31375,53 +31176,41 @@ fn beginComptimePtrLoad(...@@ -31375,53 +31176,41 @@ fn beginComptimePtrLoad(
31375 .ptr => |ptr| switch (ptr.addr) {31176 .ptr => |ptr| switch (ptr.addr) {
31376 .decl => |decl_index| blk: {31177 .decl => |decl_index| blk: {
31377 const decl = mod.declPtr(decl_index);31178 const decl = mod.declPtr(decl_index);
31378 const decl_tv = try decl.typedValue(mod);
31379 try sema.declareDependency(.{ .decl_val = decl_index });31179 try sema.declareDependency(.{ .decl_val = decl_index });
31380 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad;31180 if (decl.val.getVariable(mod) != null) return error.RuntimeLoad;
3138131181 const decl_val: MutableValue = .{ .interned = decl.val.toIntern() };
31382 const layout_defined = decl.typeOf(mod).hasWellDefinedLayout(mod);31182 const layout_defined = decl.typeOf(mod).hasWellDefinedLayout(mod);
31383 break :blk ComptimePtrLoadKit{31183 break :blk ComptimePtrLoadKit{
31384 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,31184 .parent = if (layout_defined) .{ .val = decl_val, .byte_offset = 0 } else null,
31385 .pointee = decl_tv,31185 .pointee = decl_val,
31386 .is_mutable = false,
31387 .ty_without_well_defined_layout = if (!layout_defined) decl.typeOf(mod) else null,31186 .ty_without_well_defined_layout = if (!layout_defined) decl.typeOf(mod) else null,
31388 };31187 };
31389 },31188 },
31390 .comptime_alloc => |alloc_index| kit: {31189 .comptime_alloc => |alloc_index| kit: {
31391 const alloc = sema.getComptimeAlloc(alloc_index);31190 const alloc = sema.getComptimeAlloc(alloc_index);
31392 const alloc_tv: TypedValue = .{31191 const alloc_ty = alloc.val.typeOf(mod);
31393 .ty = alloc.ty,31192 const layout_defined = alloc_ty.hasWellDefinedLayout(mod);
31394 .val = alloc.val,
31395 };
31396 const layout_defined = alloc.ty.hasWellDefinedLayout(mod);
31397 break :kit .{31193 break :kit .{
31398 .parent = if (layout_defined) .{ .tv = alloc_tv, .byte_offset = 0 } else null,31194 .parent = if (layout_defined) .{ .val = alloc.val, .byte_offset = 0 } else null,
31399 .pointee = alloc_tv,31195 .pointee = alloc.val,
31400 .is_mutable = true,31196 .ty_without_well_defined_layout = if (!layout_defined) alloc_ty else null,
31401 .ty_without_well_defined_layout = if (!layout_defined) alloc.ty else null,
31402 };31197 };
31403 },31198 },
31404 .anon_decl => |anon_decl| blk: {31199 .anon_decl => |anon_decl| blk: {
31405 const decl_val = anon_decl.val;31200 const decl_val = anon_decl.val;
31406 if (Value.fromInterned(decl_val).getVariable(mod) != null) return error.RuntimeLoad;31201 if (Value.fromInterned(decl_val).getVariable(mod) != null) return error.RuntimeLoad;
31407 const decl_ty = Type.fromInterned(ip.typeOf(decl_val));31202 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 };
31409 const layout_defined = decl_ty.hasWellDefinedLayout(mod);31204 const layout_defined = decl_ty.hasWellDefinedLayout(mod);
31410 break :blk ComptimePtrLoadKit{31205 break :blk ComptimePtrLoadKit{
31411 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,31206 .parent = if (layout_defined) .{ .val = decl_mv, .byte_offset = 0 } else null,
31412 .pointee = decl_tv,31207 .pointee = decl_mv,
31413 .is_mutable = false,
31414 .ty_without_well_defined_layout = if (!layout_defined) decl_ty else null,31208 .ty_without_well_defined_layout = if (!layout_defined) decl_ty else null,
31415 };31209 };
31416 },31210 },
31417 .int => return error.RuntimeLoad,31211 .int => return error.RuntimeLoad,
31418 .eu_payload, .opt_payload => |container_ptr| blk: {31212 .eu_payload, .opt_payload => |container_ptr| blk: {
31419 const container_ty = Type.fromInterned(ip.typeOf(container_ptr)).childType(mod);31213 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 };
31425 var deref = try sema.beginComptimePtrLoad(block, src, Value.fromInterned(container_ptr), container_ty);31214 var deref = try sema.beginComptimePtrLoad(block, src, Value.fromInterned(container_ptr), container_ty);
3142631215
31427 // eu_payload and opt_payload never have a well-defined layout31216 // eu_payload and opt_payload never have a well-defined layout
...@@ -31430,15 +31219,14 @@ fn beginComptimePtrLoad(...@@ -31430,15 +31219,14 @@ fn beginComptimePtrLoad(
31430 deref.ty_without_well_defined_layout = container_ty;31219 deref.ty_without_well_defined_layout = container_ty;
31431 }31220 }
3143231221
31433 if (deref.pointee) |*tv| {31222 if (deref.pointee) |pointee| {
31223 const pointee_ty = pointee.typeOf(mod);
31434 const coerce_in_mem_ok =31224 const coerce_in_mem_ok =
31435 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or31225 (try sema.coerceInMemoryAllowed(block, container_ty, pointee_ty, false, target, src, src)) == .ok or
31436 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;31226 (try sema.coerceInMemoryAllowed(block, pointee_ty, container_ty, false, target, src, src)) == .ok;
31437 if (coerce_in_mem_ok) {31227 if (coerce_in_mem_ok) {
31438 const payload_val = switch (tv.val.ip_index) {31228 deref.pointee = switch (pointee) {
31439 .none => tv.val.cast(Value.Payload.SubValue).?.data,31229 .interned => |ip_index| .{ .interned = switch (ip.indexToKey(ip_index)) {
31440 .null_value => return sema.fail(block, src, "attempt to use null value", .{}),
31441 else => Value.fromInterned(switch (ip.indexToKey(tv.val.toIntern())) {
31442 .error_union => |error_union| switch (error_union.val) {31230 .error_union => |error_union| switch (error_union.val) {
31443 .err_name => |err_name| return sema.fail(31231 .err_name => |err_name| return sema.fail(
31444 block,31232 block,
...@@ -31453,23 +31241,20 @@ fn beginComptimePtrLoad(...@@ -31453,23 +31241,20 @@ fn beginComptimePtrLoad(
31453 else => |payload| payload,31241 else => |payload| payload,
31454 },31242 },
31455 else => unreachable,31243 else => unreachable,
31456 }),31244 } },
31245 .eu_payload, .opt_payload => |p| p.child.*,
31246 else => unreachable,
31457 };31247 };
31458 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
31459 break :blk deref;31248 break :blk deref;
31460 }31249 }
31461 }31250 }
31462 deref.pointee = null;31251 deref.pointee = null;
31463 break :blk deref;31252 break :blk deref;
31464 },31253 },
31465 .comptime_field => |comptime_field| blk: {31254 .comptime_field => |field_val| .{
31466 const field_ty = Type.fromInterned(ip.typeOf(comptime_field));31255 .parent = null,
31467 break :blk ComptimePtrLoadKit{31256 .pointee = .{ .interned = field_val },
31468 .parent = null,31257 .ty_without_well_defined_layout = Type.fromInterned(ip.typeOf(field_val)),
31469 .pointee = .{ .ty = field_ty, .val = Value.fromInterned(comptime_field) },
31470 .is_mutable = false,
31471 .ty_without_well_defined_layout = field_ty,
31472 };
31473 },31258 },
31474 .elem => |elem_ptr| blk: {31259 .elem => |elem_ptr| blk: {
31475 const elem_ty = Type.fromInterned(ip.typeOf(elem_ptr.base)).elemType2(mod);31260 const elem_ty = Type.fromInterned(ip.typeOf(elem_ptr.base)).elemType2(mod);
...@@ -31502,30 +31287,37 @@ fn beginComptimePtrLoad(...@@ -31502,30 +31287,37 @@ fn beginComptimePtrLoad(
3150231287
31503 // If we're loading an elem that was derived from a different type31288 // If we're loading an elem that was derived from a different type
31504 // than the true type of the underlying decl, we cannot deref directly31289 // 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: {31290 const ty_matches = if (deref.pointee) |pointee| match: {
31506 const deref_elem_ty = deref.pointee.?.ty.childType(mod);31291 const ty = pointee.typeOf(mod);
31507 break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or31292 if (!ty.isArrayOrVector(mod)) break :match false;
31508 (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok;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;
31509 } else false;31297 } else false;
31510 if (!ty_matches) {31298 if (!ty_matches) {
31511 deref.pointee = null;31299 deref.pointee = null;
31512 break :blk deref;31300 break :blk deref;
31513 }31301 }
3151431302
31515 var array_tv = deref.pointee.?;31303 var array_val = deref.pointee.?;
31516 const check_len = array_tv.ty.arrayLenIncludingSentinel(mod);31304 const check_len = array_val.typeOf(mod).arrayLenIncludingSentinel(mod);
31517 if (maybe_array_ty) |load_ty| {31305 if (maybe_array_ty) |load_ty| {
31518 // It's possible that we're loading a [N]T, in which case we'd like to slice31306 // It's possible that we're loading a [N]T, in which case we'd like to slice
31519 // the pointee array directly from our parent array.31307 // the pointee array directly from our parent array.
31520 if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) {31308 if (load_ty.isArrayOrVector(mod) and load_ty.childType(mod).eql(elem_ty, mod)) {
31521 const len = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod));31309 const len = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel(mod));
31522 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);31310 const elem_idx = try sema.usizeCast(block, src, elem_ptr.index);
31523 deref.pointee = if (elem_ptr.index + len <= check_len) TypedValue{31311 deref.pointee = if (elem_ptr.index + len <= check_len) switch (array_val) {
31524 .ty = try mod.arrayType(.{31312 .aggregate => |a| .{ .aggregate = .{
31525 .len = len,31313 .ty = (try mod.arrayType(.{ .len = len, .child = elem_ty.toIntern() })).toIntern(),
31526 .child = elem_ty.toIntern(),31314 .elems = a.elems[elem_idx..][0..len],
31527 }),31315 } },
31528 .val = try array_tv.val.sliceArray(sema, elem_idx, elem_idx + len),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 },
31529 } else null;31321 } else null;
31530 break :blk deref;31322 break :blk deref;
31531 }31323 }
...@@ -31536,18 +31328,12 @@ fn beginComptimePtrLoad(...@@ -31536,18 +31328,12 @@ fn beginComptimePtrLoad(
31536 break :blk deref;31328 break :blk deref;
31537 }31329 }
31538 if (elem_ptr.index == check_len - 1) {31330 if (elem_ptr.index == check_len - 1) {
31539 if (array_tv.ty.sentinel(mod)) |sent| {31331 if (array_val.typeOf(mod).sentinel(mod)) |sent| {
31540 deref.pointee = TypedValue{31332 deref.pointee = .{ .interned = sent.toIntern() };
31541 .ty = elem_ty,
31542 .val = sent,
31543 };
31544 break :blk deref;31333 break :blk deref;
31545 }31334 }
31546 }31335 }
31547 deref.pointee = TypedValue{31336 deref.pointee = try array_val.getElem(mod, @intCast(elem_ptr.index));
31548 .ty = elem_ty,
31549 .val = try array_tv.val.elemValue(mod, @intCast(elem_ptr.index)),
31550 };
31551 break :blk deref;31337 break :blk deref;
31552 },31338 },
31553 .field => |field_ptr| blk: {31339 .field => |field_ptr| blk: {
...@@ -31571,37 +31357,17 @@ fn beginComptimePtrLoad(...@@ -31571,37 +31357,17 @@ fn beginComptimePtrLoad(
31571 deref.ty_without_well_defined_layout = container_ty;31357 deref.ty_without_well_defined_layout = container_ty;
31572 }31358 }
3157331359
31574 const tv = deref.pointee orelse {31360 const pointee = deref.pointee orelse break :blk deref;
31575 deref.pointee = null;31361 const pointee_ty = pointee.typeOf(mod);
31576 break :blk deref;
31577 };
31578 const coerce_in_mem_ok =31362 const coerce_in_mem_ok =
31579 (try sema.coerceInMemoryAllowed(block, container_ty, tv.ty, false, target, src, src)) == .ok or31363 (try sema.coerceInMemoryAllowed(block, container_ty, pointee_ty, false, target, src, src)) == .ok or
31580 (try sema.coerceInMemoryAllowed(block, tv.ty, container_ty, false, target, src, src)) == .ok;31364 (try sema.coerceInMemoryAllowed(block, pointee_ty, container_ty, false, target, src, src)) == .ok;
31581 if (!coerce_in_mem_ok) {31365 if (!coerce_in_mem_ok) {
31582 deref.pointee = null;31366 deref.pointee = null;
31583 break :blk deref;31367 break :blk deref;
31584 }31368 }
3158531369
31586 if (container_ty.isSlice(mod)) {31370 deref.pointee = try pointee.getElem(mod, field_index);
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 }
31605 break :blk deref;31371 break :blk deref;
31606 },31372 },
31607 },31373 },
...@@ -31612,9 +31378,9 @@ fn beginComptimePtrLoad(...@@ -31612,9 +31378,9 @@ fn beginComptimePtrLoad(
31612 else => unreachable,31378 else => unreachable,
31613 };31379 };
3161431380
31615 if (deref.pointee) |tv| {31381 if (deref.pointee) |val| {
31616 if (deref.parent == null and tv.ty.hasWellDefinedLayout(mod)) {31382 if (deref.parent == null and val.typeOf(mod).hasWellDefinedLayout(mod)) {
31617 deref.parent = .{ .tv = tv, .byte_offset = 0 };31383 deref.parent = .{ .val = val, .byte_offset = 0 };
31618 }31384 }
31619 }31385 }
31620 return deref;31386 return deref;
...@@ -38289,17 +38055,18 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value...@@ -38289,17 +38055,18 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value
38289 else => |e| return e,38055 else => |e| return e,
38290 };38056 };
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()));
38293 const coerce_in_mem_ok =38061 const coerce_in_mem_ok =
38294 (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or38062 (try sema.coerceInMemoryAllowed(block, load_ty, ty, false, target, src, src)) == .ok or
38295 (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok;38063 (try sema.coerceInMemoryAllowed(block, ty, load_ty, false, target, src, src)) == .ok;
38296 if (coerce_in_mem_ok) {38064 if (coerce_in_mem_ok) {
38297 // We have a Value that lines up in virtual memory exactly with what we want to load,38065 // We have a Value that lines up in virtual memory exactly with what we want to load,
38298 // and it is in-memory coercible to load_ty. It may be returned without modifications.38066 // and it is in-memory coercible to load_ty. It may be returned without modifications.
38299 // Move mutable decl values to the InternPool and assert other decls are already in38067 // Move mutable decl values to the InternPool and assert other decls are already in
38300 // the InternPool.38068 // the InternPool.
38301 const uncoerced_val = if (deref.is_mutable) try tv.val.intern(tv.ty, mod) else tv.val.toIntern();38069 const coerced_val = try mod.getCoerced(uncoerced_val, load_ty);
38302 const coerced_val = try mod.getCoerced(Value.fromInterned(uncoerced_val), load_ty);
38303 return .{ .val = coerced_val };38070 return .{ .val = coerced_val };
38304 }38071 }
38305 }38072 }
...@@ -38313,21 +38080,35 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value...@@ -38313,21 +38080,35 @@ fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value
38313 const load_sz = try sema.typeAbiSize(load_ty);38080 const load_sz = try sema.typeAbiSize(load_ty);
3831438081
38315 // Try the smaller bit-cast first, since that's more efficient than using the larger `parent`38082 // 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))38083 if (deref.pointee) |pointee| {
38317 return DerefResult{ .val = (try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0)) orelse return .runtime_load };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
38319 // If that fails, try to bit-cast from the largest parent value with a well-defined layout38092 // 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))38093 if (deref.parent) |parent| {
38321 return DerefResult{ .val = (try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset)) orelse return .runtime_load };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
38323 if (deref.ty_without_well_defined_layout) |bad_ty| {38102 if (deref.ty_without_well_defined_layout) |bad_ty| {
38324 // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem38103 // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
38325 // is that some type we encountered when de-referencing does not have a well-defined layout.38104 // 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 };
38327 } else {38106 } else {
38328 // If all encountered types had well-defined layouts, the parent is the root decl and it just38107 // If all encountered types had well-defined layouts, the parent is the root decl and it just
38329 // wasn't big enough for the load.38108 // 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 };
38331 }38112 }
38332}38113}
3833338114
src/Value.zig+6-2
...@@ -1627,7 +1627,9 @@ pub fn maybeElemValueFull(val: Value, sema: ?*Sema, mod: *Module, index: usize)...@@ -1627,7 +1627,9 @@ pub fn maybeElemValueFull(val: Value, sema: ?*Sema, mod: *Module, index: usize)
1627 .ptr => |ptr| switch (ptr.addr) {1627 .ptr => |ptr| switch (ptr.addr) {
1628 .decl => |decl| mod.declPtr(decl).val.maybeElemValueFull(sema, mod, index),1628 .decl => |decl| mod.declPtr(decl).val.maybeElemValueFull(sema, mod, index),
1629 .anon_decl => |anon_decl| Value.fromInterned(anon_decl.val).maybeElemValueFull(sema, mod, index),1629 .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,
1631 .int, .eu_payload => null,1633 .int, .eu_payload => null,
1632 .opt_payload => |base| Value.fromInterned(base).maybeElemValueFull(sema, mod, index),1634 .opt_payload => |base| Value.fromInterned(base).maybeElemValueFull(sema, mod, index),
1633 .comptime_field => |field_val| Value.fromInterned(field_val).maybeElemValueFull(sema, mod, index),1635 .comptime_field => |field_val| Value.fromInterned(field_val).maybeElemValueFull(sema, mod, index),
...@@ -1697,7 +1699,9 @@ pub fn sliceArray(...@@ -1697,7 +1699,9 @@ pub fn sliceArray(
1697 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {1699 else => switch (mod.intern_pool.indexToKey(val.toIntern())) {
1698 .ptr => |ptr| switch (ptr.addr) {1700 .ptr => |ptr| switch (ptr.addr) {
1699 .decl => |decl| try mod.declPtr(decl).val.sliceArray(sema, start, end),1701 .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),
1701 .comptime_field => |comptime_field| Value.fromInterned(comptime_field)1705 .comptime_field => |comptime_field| Value.fromInterned(comptime_field)
1702 .sliceArray(sema, start, end),1706 .sliceArray(sema, start, end),
1703 .elem => |elem| Value.fromInterned(elem.base)1707 .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};