aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-01-14 00:23:27 -0500
committerGitHub <noreply@github.com>2022-01-14 00:23:27 -0500
commitec58ddf46c4e1ac060333c6d0780955acae22442 (patch)
tree16b1f97c8962817dccdcc90ffbd7169bb8171ef7 /src
parent0d45c72d3e4f38029a453443ae6a34c398f5c530 (diff)
parent336d0c97feabad4c93525ba6ef73a6b6163f49c7 (diff)
downloadzig-ec58ddf46c4e1ac060333c6d0780955acae22442.tar.gz
zig-ec58ddf46c4e1ac060333c6d0780955acae22442.zip
Merge pull request #10582 from ziglang/stage2-arrays
stage2: detection of comptime array literals
Diffstat (limited to 'src')
-rw-r--r--src/AstGen.zig9
-rw-r--r--src/Sema.zig304
-rw-r--r--src/Zir.zig5
-rw-r--r--src/codegen/c.zig76
-rw-r--r--src/print_zir.zig1
-rw-r--r--src/value.zig7
6 files changed, 304 insertions, 98 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig
index ed57f5a3cd..7c855fb62a 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -1418,7 +1418,13 @@ fn arrayInitExprRlPtrInner(
extra_index += 1;
_ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);
}
- _ = try gz.addPlNodePayloadIndex(.validate_array_init, node, payload_index);
+
+ const tag: Zir.Inst.Tag = if (gz.force_comptime)
+ .validate_array_init_comptime
+ else
+ .validate_array_init;
+
+ _ = try gz.addPlNodePayloadIndex(tag, node, payload_index);
return .void_value;
}
@@ -2317,6 +2323,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
.validate_struct_init,
.validate_struct_init_comptime,
.validate_array_init,
+ .validate_array_init_comptime,
.set_align_stack,
.set_cold,
.set_float_mode,
diff --git a/src/Sema.zig b/src/Sema.zig
index 165f629aab..7504352576 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -836,7 +836,12 @@ pub fn analyzeBody(
continue;
},
.validate_array_init => {
- try sema.zirValidateArrayInit(block, inst);
+ try sema.zirValidateArrayInit(block, inst, false);
+ i += 1;
+ continue;
+ },
+ .validate_array_init_comptime => {
+ try sema.zirValidateArrayInit(block, inst, true);
i += 1;
continue;
},
@@ -2815,13 +2820,18 @@ fn validateStructInit(
}
}
-fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
+fn zirValidateArrayInit(
+ sema: *Sema,
+ block: *Block,
+ inst: Zir.Inst.Index,
+ is_comptime: bool,
+) CompileError!void {
const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
const init_src = validate_inst.src();
const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
- const elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
- const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, elem_ptr_data.payload_index).data;
+ const first_elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
+ const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data;
const array_ptr = sema.resolveInst(elem_ptr_extra.ptr);
const array_ty = sema.typeOf(array_ptr).childType();
const array_len = array_ty.arrayLen();
@@ -2831,6 +2841,82 @@ fn zirValidateArrayInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
array_len, instrs.len,
});
}
+
+ if (is_comptime or block.is_comptime) {
+ // In this case the comptime machinery will have evaluated the store instructions
+ // at comptime and we have nothing to do here.
+ return;
+ }
+
+ var array_is_comptime = true;
+ var first_block_index: usize = std.math.maxInt(u32);
+
+ // Collect the comptime element values in case the array literal ends up
+ // being comptime-known.
+ const element_vals = try sema.arena.alloc(Value, instrs.len);
+ const opt_opv = try sema.typeHasOnePossibleValue(block, init_src, array_ty);
+ const air_tags = sema.air_instructions.items(.tag);
+ const air_datas = sema.air_instructions.items(.data);
+
+ for (instrs) |elem_ptr, i| {
+ const elem_ptr_data = sema.code.instructions.items(.data)[elem_ptr].pl_node;
+ const elem_src: LazySrcLoc = .{ .node_offset = elem_ptr_data.src_node };
+
+ // Determine whether the value stored to this pointer is comptime-known.
+
+ if (opt_opv) |opv| {
+ element_vals[i] = opv;
+ continue;
+ }
+
+ const elem_ptr_air_ref = sema.inst_map.get(elem_ptr).?;
+ const elem_ptr_air_inst = Air.refToIndex(elem_ptr_air_ref).?;
+ // Find the block index of the elem_ptr so that we can look at the next
+ // instruction after it within the same block.
+ // Possible performance enhancement: save the `block_index` between iterations
+ // of the for loop.
+ const next_air_inst = inst: {
+ var block_index = block.instructions.items.len - 1;
+ while (block.instructions.items[block_index] != elem_ptr_air_inst) {
+ block_index -= 1;
+ }
+ first_block_index = @minimum(first_block_index, block_index);
+ break :inst block.instructions.items[block_index + 1];
+ };
+
+ // If the next instructon is a store with a comptime operand, this element
+ // is comptime.
+ switch (air_tags[next_air_inst]) {
+ .store => {
+ const bin_op = air_datas[next_air_inst].bin_op;
+ if (bin_op.lhs != elem_ptr_air_ref) {
+ array_is_comptime = false;
+ continue;
+ }
+ if (try sema.resolveMaybeUndefValAllowVariables(block, elem_src, bin_op.rhs)) |val| {
+ element_vals[i] = val;
+ } else {
+ array_is_comptime = false;
+ }
+ continue;
+ },
+ else => {
+ array_is_comptime = false;
+ continue;
+ },
+ }
+ }
+
+ if (array_is_comptime) {
+ // Our task is to delete all the `elem_ptr` and `store` instructions, and insert
+ // instead a single `store` to the array_ptr with a comptime struct value.
+
+ block.instructions.shrinkRetainingCapacity(first_block_index);
+
+ const array_val = try Value.Tag.array.create(sema.arena, element_vals);
+ const array_init = try sema.addConstant(array_ty, array_val);
+ try sema.storePtr2(block, init_src, array_ptr, init_src, array_init, init_src, .store);
+ }
}
fn failWithBadMemberAccess(
@@ -14085,88 +14171,112 @@ fn beginComptimePtrMutation(
.elem_ptr => {
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
var parent = try beginComptimePtrMutation(sema, block, src, elem_ptr.array_ptr);
- const elem_ty = parent.ty.childType();
- switch (parent.val.tag()) {
- .undef => {
- // An array has been initialized to undefined at comptime and now we
- // are for the first time setting an element. We must change the representation
- // of the array from `undef` to `array`.
- const arena = parent.beginArena(sema.gpa);
- defer parent.finishArena();
+ switch (parent.ty.zigTypeTag()) {
+ .Array, .Vector => {
+ const check_len = parent.ty.arrayLenIncludingSentinel();
+ if (elem_ptr.index >= check_len) {
+ // TODO have the parent include the decl so we can say "declared here"
+ return sema.fail(block, src, "comptime store of index {d} out of bounds of array length {d}", .{
+ elem_ptr.index, check_len,
+ });
+ }
+ const elem_ty = parent.ty.childType();
+ switch (parent.val.tag()) {
+ .undef => {
+ // An array has been initialized to undefined at comptime and now we
+ // are for the first time setting an element. We must change the representation
+ // of the array from `undef` to `array`.
+ const arena = parent.beginArena(sema.gpa);
+ defer parent.finishArena();
+
+ const array_len_including_sentinel =
+ try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
+ const elems = try arena.alloc(Value, array_len_including_sentinel);
+ mem.set(Value, elems, Value.undef);
+
+ parent.val.* = try Value.Tag.array.create(arena, elems);
- const array_len_including_sentinel =
- try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
- const elems = try arena.alloc(Value, array_len_including_sentinel);
- mem.set(Value, elems, Value.undef);
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .val = &elems[elem_ptr.index],
+ .ty = elem_ty,
+ };
+ },
+ .bytes => {
+ // An array is memory-optimized to store a slice of bytes, but we are about
+ // to modify an individual field and the representation has to change.
+ // If we wanted to avoid this, there would need to be special detection
+ // elsewhere to identify when writing a value to an array element that is stored
+ // using the `bytes` tag, and handle it without making a call to this function.
+ const arena = parent.beginArena(sema.gpa);
+ defer parent.finishArena();
+
+ const bytes = parent.val.castTag(.bytes).?.data;
+ const dest_len = parent.ty.arrayLenIncludingSentinel();
+ // bytes.len may be one greater than dest_len because of the case when
+ // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
+ assert(bytes.len >= dest_len);
+ const elems = try arena.alloc(Value, @intCast(usize, dest_len));
+ for (elems) |*elem, i| {
+ elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
+ }
- parent.val.* = try Value.Tag.array.create(arena, elems);
+ parent.val.* = try Value.Tag.array.create(arena, elems);
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &elems[elem_ptr.index],
- .ty = elem_ty,
- };
- },
- .bytes => {
- // An array is memory-optimized to store a slice of bytes, but we are about
- // to modify an individual field and the representation has to change.
- // If we wanted to avoid this, there would need to be special detection
- // elsewhere to identify when writing a value to an array element that is stored
- // using the `bytes` tag, and handle it without making a call to this function.
- const arena = parent.beginArena(sema.gpa);
- defer parent.finishArena();
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .val = &elems[elem_ptr.index],
+ .ty = elem_ty,
+ };
+ },
+ .repeated => {
+ // An array is memory-optimized to store only a single element value, and
+ // that value is understood to be the same for the entire length of the array.
+ // However, now we want to modify an individual field and so the
+ // representation has to change. If we wanted to avoid this, there would
+ // need to be special detection elsewhere to identify when writing a value to an
+ // array element that is stored using the `repeated` tag, and handle it
+ // without making a call to this function.
+ const arena = parent.beginArena(sema.gpa);
+ defer parent.finishArena();
+
+ const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);
+ const array_len_including_sentinel =
+ try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
+ const elems = try arena.alloc(Value, array_len_including_sentinel);
+ mem.set(Value, elems, repeated_val);
+
+ parent.val.* = try Value.Tag.array.create(arena, elems);
- const bytes = parent.val.castTag(.bytes).?.data;
- const dest_len = parent.ty.arrayLenIncludingSentinel();
- // bytes.len may be one greater than dest_len because of the case when
- // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted.
- assert(bytes.len >= dest_len);
- const elems = try arena.alloc(Value, @intCast(usize, dest_len));
- for (elems) |*elem, i| {
- elem.* = try Value.Tag.int_u64.create(arena, bytes[i]);
- }
+ return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .val = &elems[elem_ptr.index],
+ .ty = elem_ty,
+ };
+ },
- parent.val.* = try Value.Tag.array.create(arena, elems);
+ .array => return ComptimePtrMutationKit{
+ .decl_ref_mut = parent.decl_ref_mut,
+ .val = &parent.val.castTag(.array).?.data[elem_ptr.index],
+ .ty = elem_ty,
+ },
- return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &elems[elem_ptr.index],
- .ty = elem_ty,
- };
+ else => unreachable,
+ }
},
- .repeated => {
- // An array is memory-optimized to store only a single element value, and
- // that value is understood to be the same for the entire length of the array.
- // However, now we want to modify an individual field and so the
- // representation has to change. If we wanted to avoid this, there would
- // need to be special detection elsewhere to identify when writing a value to an
- // array element that is stored using the `repeated` tag, and handle it
- // without making a call to this function.
- const arena = parent.beginArena(sema.gpa);
- defer parent.finishArena();
-
- const repeated_val = try parent.val.castTag(.repeated).?.data.copy(arena);
- const array_len_including_sentinel =
- try sema.usizeCast(block, src, parent.ty.arrayLenIncludingSentinel());
- const elems = try arena.alloc(Value, array_len_including_sentinel);
- mem.set(Value, elems, repeated_val);
-
- parent.val.* = try Value.Tag.array.create(arena, elems);
-
+ else => {
+ if (elem_ptr.index != 0) {
+ // TODO include a "declared here" note for the decl
+ return sema.fail(block, src, "out of bounds comptime store of index {d}", .{
+ elem_ptr.index,
+ });
+ }
return ComptimePtrMutationKit{
.decl_ref_mut = parent.decl_ref_mut,
- .val = &elems[elem_ptr.index],
- .ty = elem_ty,
+ .val = parent.val,
+ .ty = parent.ty,
};
},
-
- .array => return ComptimePtrMutationKit{
- .decl_ref_mut = parent.decl_ref_mut,
- .val = &parent.val.castTag(.array).?.data[elem_ptr.index],
- .ty = elem_ty,
- },
-
- else => unreachable,
}
},
.field_ptr => {
@@ -14296,15 +14406,41 @@ fn beginComptimePtrLoad(
.elem_ptr => {
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
const parent = try beginComptimePtrLoad(sema, block, src, elem_ptr.array_ptr);
- const elem_ty = parent.ty.childType();
- const elem_size = elem_ty.abiSize(target);
- return ComptimePtrLoadKit{
- .root_val = parent.root_val,
- .val = try parent.val.elemValue(sema.arena, elem_ptr.index),
- .ty = elem_ty,
- .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + elem_size * elem_ptr.index),
- .is_mutable = parent.is_mutable,
- };
+ switch (parent.ty.zigTypeTag()) {
+ .Array, .Vector => {
+ const check_len = parent.ty.arrayLenIncludingSentinel();
+ if (elem_ptr.index >= check_len) {
+ // TODO have the parent include the decl so we can say "declared here"
+ return sema.fail(block, src, "comptime load of index {d} out of bounds of array length {d}", .{
+ elem_ptr.index, check_len,
+ });
+ }
+ const elem_ty = parent.ty.childType();
+ const elem_size = elem_ty.abiSize(target);
+ return ComptimePtrLoadKit{
+ .root_val = parent.root_val,
+ .val = try parent.val.elemValue(sema.arena, elem_ptr.index),
+ .ty = elem_ty,
+ .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + elem_size * elem_ptr.index),
+ .is_mutable = parent.is_mutable,
+ };
+ },
+ else => {
+ if (elem_ptr.index != 0) {
+ // TODO have the parent include the decl so we can say "declared here"
+ return sema.fail(block, src, "out of bounds comptime load of index {d}", .{
+ elem_ptr.index,
+ });
+ }
+ return ComptimePtrLoadKit{
+ .root_val = parent.root_val,
+ .val = parent.val,
+ .ty = parent.ty,
+ .byte_offset = parent.byte_offset,
+ .is_mutable = parent.is_mutable,
+ };
+ },
+ }
},
.field_ptr => {
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
diff --git a/src/Zir.zig b/src/Zir.zig
index 68c1b9df48..a1b5fa2188 100644
--- a/src/Zir.zig
+++ b/src/Zir.zig
@@ -663,6 +663,9 @@ pub const Inst = struct {
/// because it must use one of them to find out the array type.
/// Uses the `pl_node` field. Payload is `Block`.
validate_array_init,
+ /// Same as `validate_array_init` but additionally communicates that the
+ /// resulting array initialization value is within a comptime scope.
+ validate_array_init_comptime,
/// A struct literal with a specified type, with no fields.
/// Uses the `un_node` field.
struct_init_empty,
@@ -1087,6 +1090,7 @@ pub const Inst = struct {
.validate_struct_init,
.validate_struct_init_comptime,
.validate_array_init,
+ .validate_array_init_comptime,
.struct_init_empty,
.struct_init,
.struct_init_ref,
@@ -1341,6 +1345,7 @@ pub const Inst = struct {
.validate_struct_init = .pl_node,
.validate_struct_init_comptime = .pl_node,
.validate_array_init = .pl_node,
+ .validate_array_init_comptime = .pl_node,
.struct_init_empty = .un_node,
.field_type = .pl_node,
.field_type_ref = .pl_node,
diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index 05ec1b1a88..58bf919b1f 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -44,7 +44,7 @@ const BlockData = struct {
result: CValue,
};
-pub const CValueMap = std.AutoHashMap(Air.Inst.Index, CValue);
+pub const CValueMap = std.AutoHashMap(Air.Inst.Ref, CValue);
pub const TypedefMap = std.ArrayHashMap(
Type,
struct { name: []const u8, rendered: []u8 },
@@ -110,11 +110,29 @@ pub const Function = struct {
func: *Module.Fn,
fn resolveInst(f: *Function, inst: Air.Inst.Ref) !CValue {
- if (f.air.value(inst)) |_| {
- return CValue{ .constant = inst };
+ const gop = try f.value_map.getOrPut(inst);
+ if (gop.found_existing) return gop.value_ptr.*;
+
+ const val = f.air.value(inst).?;
+ const ty = f.air.typeOf(inst);
+ switch (ty.zigTypeTag()) {
+ .Array => {
+ const writer = f.object.code_header.writer();
+ const decl_c_value = f.allocLocalValue();
+ gop.value_ptr.* = decl_c_value;
+ try writer.writeAll("static ");
+ try f.object.dg.renderTypeAndName(writer, ty, decl_c_value, .Const);
+ try writer.writeAll(" = ");
+ try f.object.dg.renderValue(writer, ty, val);
+ try writer.writeAll(";\n ");
+ return decl_c_value;
+ },
+ else => {
+ const result = CValue{ .constant = inst };
+ gop.value_ptr.* = result;
+ return result;
+ },
}
- const index = Air.refToIndex(inst).?;
- return f.value_map.get(index).?; // Assertion means instruction does not dominate usage.
}
fn allocLocalValue(f: *Function) CValue {
@@ -154,6 +172,8 @@ pub const Function = struct {
pub const Object = struct {
dg: DeclGen,
code: std.ArrayList(u8),
+ /// Goes before code. Initialized and deinitialized in `genFunc`.
+ code_header: std.ArrayList(u8) = undefined,
indent_writer: IndentWriter(std.ArrayList(u8).Writer),
fn writer(o: *Object) IndentWriter(std.ArrayList(u8).Writer).Writer {
@@ -218,12 +238,18 @@ pub const DeclGen = struct {
// Determine if we must pointer cast.
if (ty.eql(decl.ty)) {
try writer.writeByte('&');
- } else {
- try writer.writeAll("(");
- try dg.renderType(writer, ty);
- try writer.writeAll(")&");
+ try dg.renderDeclName(decl, writer);
+ return;
}
+
+ try writer.writeAll("((");
+ try dg.renderType(writer, ty);
+ try writer.writeAll(")&");
+ try dg.renderDeclName(decl, writer);
+ try writer.writeByte(')');
+ return;
}
+
try dg.renderDeclName(decl, writer);
}
@@ -1010,6 +1036,10 @@ pub fn genFunc(f: *Function) !void {
defer tracy.end();
const o = &f.object;
+
+ o.code_header = std.ArrayList(u8).init(f.object.dg.gpa);
+ defer o.code_header.deinit();
+
const is_global = o.dg.module.decl_exports.contains(f.func.owner_decl);
const fwd_decl_writer = o.dg.fwd_decl.writer();
if (is_global) {
@@ -1020,12 +1050,26 @@ pub fn genFunc(f: *Function) !void {
try o.indent_writer.insertNewline();
try o.dg.renderFunctionSignature(o.writer(), is_global);
-
try o.writer().writeByte(' ');
+
+ // In case we need to use the header, populate it with a copy of the function
+ // signature here. We anticipate a brace, newline, and space.
+ try o.code_header.ensureUnusedCapacity(o.code.items.len + 3);
+ o.code_header.appendSliceAssumeCapacity(o.code.items);
+ o.code_header.appendSliceAssumeCapacity("{\n ");
+ const empty_header_len = o.code_header.items.len;
+
const main_body = f.air.getMainBody();
try genBody(f, main_body);
try o.indent_writer.insertNewline();
+
+ // If we have a header to insert, append the body to the header
+ // and then return the result, freeing the body.
+ if (o.code_header.items.len > empty_header_len) {
+ try o.code_header.appendSlice(o.code.items[empty_header_len..]);
+ mem.swap(std.ArrayList(u8), &o.code, &o.code_header);
+ }
}
pub fn genDecl(o: *Object) !void {
@@ -1289,7 +1333,7 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
};
switch (result_value) {
.none => {},
- else => try f.value_map.putNoClobber(inst, result_value),
+ else => try f.value_map.putNoClobber(Air.indexToRef(inst), result_value),
}
}
@@ -2189,7 +2233,15 @@ fn airCall(f: *Function, inst: Air.Inst.Index) !CValue {
fn airDbgStmt(f: *Function, inst: Air.Inst.Index) !CValue {
const dbg_stmt = f.air.instructions.items(.data)[inst].dbg_stmt;
const writer = f.object.writer();
- try writer.print("#line {d}\n", .{dbg_stmt.line + 1});
+ // TODO re-evaluate whether to emit these or not. If we naively emit
+ // these directives, the output file will report bogus line numbers because
+ // every newline after the #line directive adds one to the line.
+ // We also don't print the filename yet, so the output is strictly unhelpful.
+ // If we wanted to go this route, we would need to go all the way and not output
+ // newlines until the next dbg_stmt occurs.
+ // Perhaps an additional compilation option is in order?
+ //try writer.print("#line {d}\n", .{dbg_stmt.line + 1});
+ try writer.print("/* file:{d}:{d} */\n", .{ dbg_stmt.line + 1, dbg_stmt.column + 1 });
return CValue.none;
}
diff --git a/src/print_zir.zig b/src/print_zir.zig
index a8b9ba3800..e367cf056d 100644
--- a/src/print_zir.zig
+++ b/src/print_zir.zig
@@ -369,6 +369,7 @@ const Writer = struct {
.validate_struct_init,
.validate_struct_init_comptime,
.validate_array_init,
+ .validate_array_init_comptime,
.c_import,
=> try self.writePlNodeBlock(stream, inst),
diff --git a/src/value.zig b/src/value.zig
index 1e7f713307..1993a853cd 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -1817,8 +1817,13 @@ pub const Value = extern union {
.decl_ref => return val.castTag(.decl_ref).?.data.val.elemValueAdvanced(index, arena, buffer),
.decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.val.elemValueAdvanced(index, arena, buffer),
+ .elem_ptr => {
+ const data = val.castTag(.elem_ptr).?.data;
+ return data.array_ptr.elemValueAdvanced(index + data.index, arena, buffer);
+ },
- // The child type of arrays which have only one possible value need to have only one possible value itself.
+ // The child type of arrays which have only one possible value need
+ // to have only one possible value itself.
.the_only_possible_value => return val,
else => unreachable,