authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-03-11 14:22:53-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-03-14 21:42:43-07:00
log5fa057053ce32274b878077e5abff82335530fc8
tree79350e7a8e22ba6fa27477b15ef0c7d01cd79059
parent54426bdc82ad361bb580f9678dd23417c350282a

stage2 sema: Respect container_ty of parent ptrs

The core change here is that we no longer blindly trust that parent pointers (.elem_ptr, .field_ptr, .eu_payload_ptr, .union_payload_ptr) were derived from the "true" type of the underlying decl. When types diverge, direct dereference fails and we are forced to bitcast, as usual. In order to maximize our chances to have a successful bitcast, this includes several changes to the dereference procedure: - `root` is now `parent` and is the largest Value containing the dereference target, with the condition that its layout and the byte offset of the target within are both well-defined. - If the target cannot be dereferenced directly, because the pointers were not derived from the true type of the underlying decl, then it is returned as null. - `beginComptimePtrDeref` now accepts an optional array_ty param, which is used to directly dereference an array from an elem_ptr, if necessary. This allows us to dereference array types without well-defined layouts (e.g. `[N]?u8`) at an offset The load_ty also allows us to correctly "over-read" an .elem_ptr to an array of [N]T, if necessary. This makes direct dereference work for array types even in the presence of an offset, which is necessary if the array has no well-defined layout (e.g. loading from `[6]?u8`)

3 files changed, 239 insertions(+), 151 deletions(-)

src/Sema.zig+209-151
......@@ -12609,6 +12609,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
1260912609 enum_obj.* = .{
1261012610 .owner_decl = new_decl,
1261112611 .tag_ty = Type.initTag(.@"null"),
12612 .tag_ty_inferred = true,
1261212613 .fields = .{},
1261312614 .values = .{},
1261412615 .node_offset = src.node_offset,
......@@ -17590,6 +17591,14 @@ fn beginComptimePtrMutation(
1759017591 src: LazySrcLoc,
1759117592 ptr_val: Value,
1759217593) CompileError!ComptimePtrMutationKit {
17594
17595 // TODO: Update this to behave like `beginComptimePtrLoad` and properly check/use
17596 // `container_ty` and `array_ty`, instead of trusting that the parent decl type
17597 // matches the type used to derive the elem_ptr/field_ptr/etc.
17598 //
17599 // This is needed because the types will not match if the pointer we're mutating
17600 // through is reinterpreting comptime memory.
17601
1759317602 switch (ptr_val.tag()) {
1759417603 .decl_ref_mut => {
1759517604 const decl_ref_mut = ptr_val.castTag(.decl_ref_mut).?.data;
......@@ -17850,163 +17859,191 @@ fn beginComptimePtrMutation(
1785017859 }
1785117860}
1785217861
17853const ComptimePtrLoadKit = struct {
17854 /// The Value of the Decl that owns this memory.
17855 root_val: Value,
17856 /// The Type of the Decl that owns this memory.
17857 root_ty: Type,
17858 /// Parent Value.
17859 val: Value,
17860 /// The Type of the parent Value.
17861 ty: Type,
17862const TypedValueAndOffset = struct {
17863 tv: TypedValue,
1786217864 /// The starting byte offset of `val` from `root_val`.
1786317865 /// If the type does not have a well-defined memory layout, this is null.
17864 byte_offset: ?usize,
17865 /// Whether the `root_val` could be mutated by further
17866 byte_offset: usize,
17867};
17868
17869const ComptimePtrLoadKit = struct {
17870 /// The Value and Type corresponding to the target of the provided pointer.
17871 /// If a direct dereference is not possible, this is null.
17872 target: ?TypedValue,
17873 /// The largest parent Value containing `target` and having a well-defined memory layout.
17874 /// This is used for bitcasting, if direct dereferencing failed (i.e. `target` is null).
17875 parent: ?TypedValueAndOffset,
17876 /// Whether the `target` could be mutated by further
1786617877 /// semantic analysis and a copy must be performed.
1786717878 is_mutable: bool,
17879 /// If the root decl could not be used as `parent`, this is the type that
17880 /// caused that by not having a well-defined layout
17881 ty_without_well_defined_layout: ?Type,
1786817882};
1786917883
1787017884const ComptimePtrLoadError = CompileError || error{
1787117885 RuntimeLoad,
1787217886};
1787317887
17888/// If `maybe_array_ty` is provided, it will be used to directly dereference an
17889/// .elem_ptr of type T to a value of [N]T, if necessary.
1787417890fn beginComptimePtrLoad(
1787517891 sema: *Sema,
1787617892 block: *Block,
1787717893 src: LazySrcLoc,
1787817894 ptr_val: Value,
17895 maybe_array_ty: ?Type,
1787917896) ComptimePtrLoadError!ComptimePtrLoadKit {
1788017897 const target = sema.mod.getTarget();
17881 switch (ptr_val.tag()) {
17882 .decl_ref => {
17883 const decl = ptr_val.castTag(.decl_ref).?.data;
17884 const decl_val = try decl.value();
17885 if (decl_val.tag() == .variable) return error.RuntimeLoad;
17886 return ComptimePtrLoadKit{
17887 .root_val = decl_val,
17888 .root_ty = decl.ty,
17889 .val = decl_val,
17890 .ty = decl.ty,
17891 .byte_offset = 0,
17892 .is_mutable = false,
17898 var deref: ComptimePtrLoadKit = switch (ptr_val.tag()) {
17899 .decl_ref,
17900 .decl_ref_mut,
17901 => blk: {
17902 const decl = switch (ptr_val.tag()) {
17903 .decl_ref => ptr_val.castTag(.decl_ref).?.data,
17904 .decl_ref_mut => ptr_val.castTag(.decl_ref_mut).?.data.decl,
17905 else => unreachable,
1789317906 };
17894 },
17895 .decl_ref_mut => {
17896 const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl;
17897 const decl_val = try decl.value();
17898 if (decl_val.tag() == .variable) return error.RuntimeLoad;
17899 return ComptimePtrLoadKit{
17900 .root_val = decl_val,
17901 .root_ty = decl.ty,
17902 .val = decl_val,
17903 .ty = decl.ty,
17904 .byte_offset = 0,
17905 .is_mutable = true,
17907 const is_mutable = ptr_val.tag() == .decl_ref_mut;
17908 const decl_tv = try decl.typedValue();
17909 if (decl_tv.val.tag() == .variable) return error.RuntimeLoad;
17910
17911 const layout_defined = try sema.typeHasWellDefinedLayout(block, src, decl.ty);
17912 break :blk ComptimePtrLoadKit{
17913 .parent = if (layout_defined) .{ .tv = decl_tv, .byte_offset = 0 } else null,
17914 .target = decl_tv,
17915 .is_mutable = is_mutable,
17916 .ty_without_well_defined_layout = if (!layout_defined) decl.ty else null,
1790617917 };
1790717918 },
17908 .elem_ptr => {
17919
17920 .elem_ptr => blk: {
1790917921 const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
17910 const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr);
17911 switch (parent.ty.zigTypeTag()) {
17912 .Array, .Vector => {
17913 const check_len = parent.ty.arrayLenIncludingSentinel();
17914 if (elem_ptr.index >= check_len) {
17915 // TODO have the parent include the decl so we can say "declared here"
17916 return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{
17917 elem_ptr.index, check_len,
17918 });
17922 const elem_ty = elem_ptr.elem_ty;
17923 var deref = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr, null);
17924
17925 if (elem_ptr.index != 0) {
17926 if (try sema.typeHasWellDefinedLayout(block, src, elem_ty)) {
17927 if (deref.parent) |*parent| {
17928 // Update the byte offset (in-place)
17929 const elem_size = try sema.typeAbiSize(block, src, elem_ty);
17930 const offset = parent.byte_offset + elem_size * elem_ptr.index;
17931 parent.byte_offset = try sema.usizeCast(block, src, offset);
1791917932 }
17920 const elem_ty = parent.ty.childType();
17921 const byte_offset: ?usize = bo: {
17922 if (try sema.typeRequiresComptime(block, src, elem_ty)) {
17923 break :bo null;
17924 } else {
17925 if (parent.byte_offset) |off| {
17926 try sema.resolveTypeLayout(block, src, elem_ty);
17927 const elem_size = elem_ty.abiSize(target);
17928 break :bo try sema.usizeCast(block, src, off + elem_size * elem_ptr.index);
17929 } else {
17930 break :bo null;
17931 }
17932 }
17933 };
17934 return ComptimePtrLoadKit{
17935 .root_val = parent.root_val,
17936 .root_ty = parent.root_ty,
17937 .val = try parent.val.elemValue(sema.arena, elem_ptr.index),
17938 .ty = elem_ty,
17939 .byte_offset = byte_offset,
17940 .is_mutable = parent.is_mutable,
17941 };
17942 },
17943 else => {
17944 if (elem_ptr.index != 0) {
17945 // TODO have the parent include the decl so we can say "declared here"
17946 return sema.fail(block, src, "out of bounds comptime load of index {d}", .{
17947 elem_ptr.index,
17948 });
17949 }
17950 return ComptimePtrLoadKit{
17951 .root_val = parent.root_val,
17952 .root_ty = parent.root_ty,
17953 .val = parent.val,
17954 .ty = parent.ty,
17955 .byte_offset = parent.byte_offset,
17956 .is_mutable = parent.is_mutable,
17957 };
17958 },
17933 } else {
17934 deref.parent = null;
17935 deref.ty_without_well_defined_layout = elem_ty;
17936 }
17937 }
17938
17939 // If we're loading an elem_ptr that was derived from a different type
17940 // than the true type of the underlying decl, we cannot deref directly
17941 const ty_matches = if (deref.target != null and deref.target.?.ty.isArrayLike()) x: {
17942 const deref_elem_ty = deref.target.?.ty.childType();
17943 break :x (try sema.coerceInMemoryAllowed(block, deref_elem_ty, elem_ty, false, target, src, src)) == .ok or
17944 (try sema.coerceInMemoryAllowed(block, elem_ty, deref_elem_ty, false, target, src, src)) == .ok;
17945 } else false;
17946 if (!ty_matches) {
17947 deref.target = null;
17948 break :blk deref;
1795917949 }
17950
17951 var array_tv = deref.target.?;
17952 const check_len = array_tv.ty.arrayLenIncludingSentinel();
17953 if (elem_ptr.index >= check_len) {
17954 // TODO have the deref include the decl so we can say "declared here"
17955 return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{
17956 elem_ptr.index, check_len,
17957 });
17958 }
17959
17960 if (maybe_array_ty) |load_ty| {
17961 // It's possible that we're loading a [N]T, in which case we'd like to slice
17962 // the target array directly from our parent array.
17963 if (load_ty.isArrayLike() and load_ty.childType().eql(elem_ty)) {
17964 const N = try sema.usizeCast(block, src, load_ty.arrayLenIncludingSentinel());
17965 deref.target = if (elem_ptr.index + N <= check_len) TypedValue{
17966 .ty = try Type.array(sema.arena, N, null, elem_ty),
17967 .val = try array_tv.val.sliceArray(sema.arena, elem_ptr.index, elem_ptr.index + N),
17968 } else null;
17969 break :blk deref;
17970 }
17971 }
17972
17973 deref.target = .{
17974 .ty = elem_ty,
17975 .val = try array_tv.val.elemValue(sema.arena, elem_ptr.index),
17976 };
17977 break :blk deref;
1796017978 },
17961 .field_ptr => {
17979
17980 .field_ptr => blk: {
1796217981 const field_ptr = ptr_val.castTag(.field_ptr).?.data;
17963 const parent = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr);
1796417982 const field_index = @intCast(u32, field_ptr.field_index);
17965 const byte_offset: ?usize = bo: {
17966 if (try sema.typeRequiresComptime(block, src, parent.ty)) {
17967 break :bo null;
17968 } else {
17969 if (parent.byte_offset) |off| {
17970 try sema.resolveTypeLayout(block, src, parent.ty);
17971 const field_offset = parent.ty.structFieldOffset(field_index, target);
17972 break :bo try sema.usizeCast(block, src, off + field_offset);
17973 } else {
17974 break :bo null;
17975 }
17983 const field_ty = field_ptr.container_ty.structFieldType(field_index);
17984 var deref = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr, field_ptr.container_ty);
17985
17986 if (try sema.typeHasWellDefinedLayout(block, src, field_ptr.container_ty)) {
17987 if (deref.parent) |*parent| {
17988 // Update the byte offset (in-place)
17989 try sema.resolveTypeLayout(block, src, field_ptr.container_ty);
17990 const field_offset = field_ptr.container_ty.structFieldOffset(field_index, target);
17991 parent.byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset);
1797617992 }
17977 };
17978 return ComptimePtrLoadKit{
17979 .root_val = parent.root_val,
17980 .root_ty = parent.root_ty,
17981 .val = try parent.val.fieldValue(sema.arena, field_index),
17982 .ty = parent.ty.structFieldType(field_index),
17983 .byte_offset = byte_offset,
17984 .is_mutable = parent.is_mutable,
17985 };
17986 },
17987 .eu_payload_ptr => {
17988 const err_union_ptr = ptr_val.castTag(.eu_payload_ptr).?.data;
17989 const parent = try beginComptimePtrLoad(sema, block, src, err_union_ptr.container_ptr);
17990 return ComptimePtrLoadKit{
17991 .root_val = parent.root_val,
17992 .root_ty = parent.root_ty,
17993 .val = parent.val.castTag(.eu_payload).?.data,
17994 .ty = parent.ty.errorUnionPayload(),
17995 .byte_offset = null,
17996 .is_mutable = parent.is_mutable,
17997 };
17993 } else {
17994 deref.parent = null;
17995 deref.ty_without_well_defined_layout = field_ptr.container_ty;
17996 }
17997
17998 if (deref.target) |*tv| {
17999 const coerce_in_mem_ok =
18000 (try sema.coerceInMemoryAllowed(block, field_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or
18001 (try sema.coerceInMemoryAllowed(block, tv.ty, field_ptr.container_ty, false, target, src, src)) == .ok;
18002 if (coerce_in_mem_ok) {
18003 deref.target = TypedValue{
18004 .ty = field_ty,
18005 .val = try tv.val.fieldValue(sema.arena, field_index),
18006 };
18007 break :blk deref;
18008 }
18009 }
18010 deref.target = null;
18011 break :blk deref;
1799818012 },
17999 .opt_payload_ptr => {
18000 const opt_ptr = ptr_val.castTag(.opt_payload_ptr).?.data;
18001 const parent = try beginComptimePtrLoad(sema, block, src, opt_ptr.container_ptr);
18002 return ComptimePtrLoadKit{
18003 .root_val = parent.root_val,
18004 .root_ty = parent.root_ty,
18005 .val = parent.val.castTag(.opt_payload).?.data,
18006 .ty = try parent.ty.optionalChildAlloc(sema.arena),
18007 .byte_offset = null,
18008 .is_mutable = parent.is_mutable,
18013
18014 .opt_payload_ptr,
18015 .eu_payload_ptr,
18016 => blk: {
18017 const payload_ptr = ptr_val.cast(Value.Payload.PayloadPtr).?.data;
18018 const payload_ty = switch (ptr_val.tag()) {
18019 .eu_payload_ptr => payload_ptr.container_ty.errorUnionPayload(),
18020 .opt_payload_ptr => try payload_ptr.container_ty.optionalChildAlloc(sema.arena),
18021 else => unreachable,
1800918022 };
18023 var deref = try beginComptimePtrLoad(sema, block, src, payload_ptr.container_ptr, payload_ptr.container_ty);
18024
18025 // eu_payload_ptr and opt_payload_ptr never have a well-defined layout
18026 if (deref.parent != null) {
18027 deref.parent = null;
18028 deref.ty_without_well_defined_layout = payload_ptr.container_ty;
18029 }
18030
18031 if (deref.target) |*tv| {
18032 const coerce_in_mem_ok =
18033 (try sema.coerceInMemoryAllowed(block, payload_ptr.container_ty, tv.ty, false, target, src, src)) == .ok or
18034 (try sema.coerceInMemoryAllowed(block, tv.ty, payload_ptr.container_ty, false, target, src, src)) == .ok;
18035 if (coerce_in_mem_ok) {
18036 const payload_val = switch (ptr_val.tag()) {
18037 .eu_payload_ptr => tv.val.castTag(.eu_payload).?.data,
18038 .opt_payload_ptr => tv.val.castTag(.opt_payload).?.data,
18039 else => unreachable,
18040 };
18041 tv.* = TypedValue{ .ty = payload_ty, .val = payload_val };
18042 break :blk deref;
18043 }
18044 }
18045 deref.target = null;
18046 break :blk deref;
1801018047 },
1801118048
1801218049 .zero,
......@@ -18021,7 +18058,14 @@ fn beginComptimePtrLoad(
1802118058 => return error.RuntimeLoad,
1802218059
1802318060 else => unreachable,
18061 };
18062
18063 if (deref.target) |tv| {
18064 if (deref.parent == null and tv.ty.hasWellDefinedLayout()) {
18065 deref.parent = .{ .tv = tv, .byte_offset = 0 };
18066 }
1802418067 }
18068 return deref;
1802518069}
1802618070
1802718071fn bitCast(
......@@ -21106,39 +21150,53 @@ pub fn analyzeAddrspace(
2110621150/// Asserts the value is a pointer and dereferences it.
2110721151/// Returns `null` if the pointer contents cannot be loaded at comptime.
2110821152fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
21109 const target = sema.mod.getTarget();
2111021153 const load_ty = ptr_ty.childType();
21111 const parent = sema.beginComptimePtrLoad(block, src, ptr_val) catch |err| switch (err) {
21154 const target = sema.mod.getTarget();
21155 const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) {
2111221156 error.RuntimeLoad => return null,
2111321157 else => |e| return e,
2111421158 };
21115 // We have a Value that lines up in virtual memory exactly with what we want to load.
21116 // If the Type is in-memory coercable to `load_ty`, it may be returned without modifications.
21117 const coerce_in_mem_ok =
21118 (try sema.coerceInMemoryAllowed(block, load_ty, parent.ty, false, target, src, src)) == .ok or
21119 (try sema.coerceInMemoryAllowed(block, parent.ty, load_ty, false, target, src, src)) == .ok;
21120 if (coerce_in_mem_ok) {
21121 if (parent.is_mutable) {
21122 // The decl whose value we are obtaining here may be overwritten with
21123 // a different value upon further semantic analysis, which would
21124 // invalidate this memory. So we must copy here.
21125 return try parent.val.copy(sema.arena);
21159
21160 if (deref.target) |tv| {
21161 const coerce_in_mem_ok =
21162 (try sema.coerceInMemoryAllowed(block, load_ty, tv.ty, false, target, src, src)) == .ok or
21163 (try sema.coerceInMemoryAllowed(block, tv.ty, load_ty, false, target, src, src)) == .ok;
21164 if (coerce_in_mem_ok) {
21165 // We have a Value that lines up in virtual memory exactly with what we want to load,
21166 // and it is in-memory coercible to load_ty. It may be returned without modifications.
21167 if (deref.is_mutable) {
21168 // The decl whose value we are obtaining here may be overwritten with
21169 // a different value upon further semantic analysis, which would
21170 // invalidate this memory. So we must copy here.
21171 return try tv.val.copy(sema.arena);
21172 }
21173 return tv.val;
2112621174 }
21127 return parent.val;
2112821175 }
2112921176
21130 // The type is not in-memory coercable, so it must be bitcasted according
21131 // to the pointer type we are performing the load through.
21177 // The type is not in-memory coercible or the direct dereference failed, so it must
21178 // be bitcast according to the pointer type we are performing the load through.
21179 if (!(try sema.typeHasWellDefinedLayout(block, src, load_ty)))
21180 return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{load_ty});
21181
21182 const load_sz = try sema.typeAbiSize(block, src, load_ty);
21183
21184 // Try the smaller bit-cast first, since that's more efficient than using the larger `parent`
21185 if (deref.target) |tv| if (load_sz <= try sema.typeAbiSize(block, src, tv.ty))
21186 return try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0);
2113221187
21133 // TODO emit a compile error if the types are not allowed to be bitcasted
21188 // If that fails, try to bit-cast from the largest parent value with a well-defined layout
21189 if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(block, src, parent.tv.ty))
21190 return try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset);
2113421191
21135 if (parent.ty.abiSize(target) >= load_ty.abiSize(target)) {
21136 // The Type it is stored as in the compiler has an ABI size greater or equal to
21137 // the ABI size of `load_ty`. We may perform the bitcast based on
21138 // `parent.val` alone (more efficient).
21139 return try sema.bitCastVal(block, src, parent.val, parent.ty, load_ty, 0);
21192 if (deref.ty_without_well_defined_layout) |bad_ty| {
21193 // We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
21194 // is that some type we encountered when de-referencing does not have a well-defined layout.
21195 return sema.fail(block, src, "comptime dereference requires {} to have a well-defined layout, but it does not.", .{bad_ty});
2114021196 } else {
21141 return try sema.bitCastVal(block, src, parent.root_val, parent.root_ty, load_ty, parent.byte_offset.?);
21197 // If all encountered types had well-defined layouts, the parent is the root decl and it just
21198 // wasn't big enough for the load.
21199 return sema.fail(block, src, "dereference of {} exceeds bounds of containing decl of type {}", .{ ptr_ty, deref.parent.?.tv.ty });
2114221200 }
2114321201}
2114421202
src/type.zig+7
......@@ -4400,6 +4400,13 @@ pub const Type = extern union {
44004400 };
44014401 }
44024402
4403 pub fn isArrayLike(ty: Type) bool {
4404 return switch (ty.zigTypeTag()) {
4405 .Array, .Vector => true,
4406 else => false,
4407 };
4408 }
4409
44034410 pub fn isIndexable(ty: Type) bool {
44044411 return switch (ty.zigTypeTag()) {
44054412 .Array, .Vector => true,
src/value.zig+23
......@@ -2412,6 +2412,29 @@ pub const Value = extern union {
24122412 }
24132413 }
24142414
2415 // Asserts that the provided start/end are in-bounds.
2416 pub fn sliceArray(val: Value, arena: Allocator, start: usize, end: usize) error{OutOfMemory}!Value {
2417 return switch (val.tag()) {
2418 .empty_array_sentinel => if (start == 0 and end == 1) val else Value.initTag(.empty_array),
2419 .bytes => Tag.bytes.create(arena, val.castTag(.bytes).?.data[start..end]),
2420 .array => Tag.array.create(arena, val.castTag(.array).?.data[start..end]),
2421 .slice => sliceArray(val.castTag(.slice).?.data.ptr, arena, start, end),
2422
2423 .decl_ref => sliceArray(val.castTag(.decl_ref).?.data.val, arena, start, end),
2424 .decl_ref_mut => sliceArray(val.castTag(.decl_ref_mut).?.data.decl.val, arena, start, end),
2425 .elem_ptr => blk: {
2426 const elem_ptr = val.castTag(.elem_ptr).?.data;
2427 break :blk sliceArray(elem_ptr.array_ptr, arena, start + elem_ptr.index, end + elem_ptr.index);
2428 },
2429
2430 .repeated,
2431 .the_only_possible_value,
2432 => val,
2433
2434 else => unreachable,
2435 };
2436 }
2437
24152438 pub fn fieldValue(val: Value, allocator: Allocator, index: usize) error{OutOfMemory}!Value {
24162439 _ = allocator;
24172440 switch (val.tag()) {