commit 371691e020937eb42ca97efc54f3a2cdacc24884 (tree)
parent 19266219a50c34675f528baaf4c30294d4621b7c
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Thu, 26 Feb 2026 08:56:18 +0000
sema: resolve struct field types/inits, union inner decls (309 IP entries match)
Port struct field type resolution (resolveStructFieldTypesC) and field
default value resolution (resolveStructFieldInitsC) from upstream Zig.
Add resolveStructInnerDeclsC for interleaved inner declaration ordering.
Key changes:
- resolveZirTypeRef: resolve ZIR type references to IP indices
- resolveStructFieldTypesC: resolve struct field types from ZIR bodies
- resolveStructFieldInitsC: resolve field defaults (enum_tag, opt_null, int)
- resolveStructInnerDeclsC: resolve inner struct declarations only
- isNavStructDecl: check if a nav is a direct struct_decl without resolving
- Fix IP_KEY_OPT data format (plain InternPoolIndex, not struct)
- Expand enum search to file root namespace for GlobalLinkage etc.
- Add s_struct_fully_resolved guard to prevent double resolution
- IP_KEY_PTR_TYPE_CHILD support in intern_pool.c
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
3 files changed, 842 insertions(+), 63 deletions(-)
diff --git a/stage0/intern_pool.c b/stage0/intern_pool.c
@@ -120,6 +120,19 @@ static uint32_t ipHashKey(const InternPoolKey* key) {
h = ipHashCombine(h, key->data.opt_payload.ty);
h = ipHashCombine(h, key->data.opt_payload.val);
break;
+ case IP_KEY_FUNC_TYPE:
+ h = ipHashCombine(h, key->data.func_type.return_type);
+ h = ipHashCombine(h, key->data.func_type.param_count);
+ h = ipHashCombine(h, key->data.func_type.cc);
+ h = ipHashCombine(h, (uint32_t)key->data.func_type.is_var_args);
+ h = ipHashCombine(h, (uint32_t)key->data.func_type.is_generic);
+ h = ipHashCombine(h, (uint32_t)key->data.func_type.is_noinline);
+ h = ipHashCombine(h, key->data.func_type.comptime_bits);
+ h = ipHashCombine(h, key->data.func_type.noalias_bits);
+ break;
+ case IP_KEY_SLICE:
+ h = ipHashCombine(h, key->data.slice);
+ break;
default:
/* For other tag types, just use the tag hash. */
break;
@@ -209,6 +222,19 @@ static bool ipKeysEqual(const InternPoolKey* a, const InternPoolKey* b) {
case IP_KEY_OPT_PAYLOAD:
return a->data.opt_payload.ty == b->data.opt_payload.ty
&& a->data.opt_payload.val == b->data.opt_payload.val;
+ case IP_KEY_FUNC_TYPE:
+ return a->data.func_type.return_type == b->data.func_type.return_type
+ && a->data.func_type.param_count == b->data.func_type.param_count
+ && a->data.func_type.cc == b->data.func_type.cc
+ && a->data.func_type.is_var_args == b->data.func_type.is_var_args
+ && a->data.func_type.is_generic == b->data.func_type.is_generic
+ && a->data.func_type.is_noinline == b->data.func_type.is_noinline
+ && a->data.func_type.comptime_bits
+ == b->data.func_type.comptime_bits
+ && a->data.func_type.noalias_bits
+ == b->data.func_type.noalias_bits;
+ case IP_KEY_SLICE:
+ return a->data.slice == b->data.slice;
default:
/* Fallback: memcmp the entire data union. */
return memcmp(&a->data, &b->data, sizeof(a->data)) == 0;
@@ -476,15 +502,23 @@ InternPool ipInit(void) {
ipMakePtrType(IP_INDEX_U8_TYPE, IP_INDEX_ZERO_U8,
PTR_FLAGS_SIZE_MANY | PTR_FLAGS_IS_CONST));
- // Index 50: []const u8
- ipAppendItem(&ip,
- ipMakePtrType(IP_INDEX_U8_TYPE, IP_INDEX_NONE,
- PTR_FLAGS_SIZE_SLICE | PTR_FLAGS_IS_CONST));
+ // Index 50: []const u8 — stored as type_slice wrapping [*]const u8
+ {
+ InternPoolKey sk;
+ memset(&sk, 0, sizeof(sk));
+ sk.tag = IP_KEY_SLICE;
+ sk.data.slice = IP_INDEX_MANYPTR_CONST_U8_TYPE; // index 48
+ ipAppendItem(&ip, sk);
+ }
- // Index 51: [:0]const u8
- ipAppendItem(&ip,
- ipMakePtrType(IP_INDEX_U8_TYPE, IP_INDEX_ZERO_U8,
- PTR_FLAGS_SIZE_SLICE | PTR_FLAGS_IS_CONST));
+ // Index 51: [:0]const u8 — stored as type_slice wrapping [*:0]const u8
+ {
+ InternPoolKey sk;
+ memset(&sk, 0, sizeof(sk));
+ sk.tag = IP_KEY_SLICE;
+ sk.data.slice = IP_INDEX_MANYPTR_CONST_U8_SENTINEL_0_TYPE; // 49
+ ipAppendItem(&ip, sk);
+ }
// Indices 52-98: vector types
// Matching InternPool.Index enum order exactly.
@@ -822,6 +856,7 @@ InternPoolIndex ipTypeOf(const InternPool* ip, InternPoolIndex index) {
case IP_KEY_FUNC_TYPE:
case IP_KEY_ERROR_SET_TYPE:
case IP_KEY_INFERRED_ERROR_SET_TYPE:
+ case IP_KEY_SLICE:
return IP_INDEX_TYPE_TYPE;
case IP_KEY_UNDEF:
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -2945,6 +2945,373 @@ static uint32_t findEnumFieldByName(
return UINT32_MAX;
}
+// --- getEnumFieldIntVal ---
+// Get the IP index of the integer value for a given field index in an enum.
+// Parses the enum_decl ZIR to find the tag type and computes the value.
+// For auto-enums, values are 0, 1, 2, ... For explicit enums, reads the
+// explicit values from ZIR. Returns IP_INDEX_NONE on failure.
+static InternPoolIndex getEnumFieldIntVal(
+ const Zir* zir, uint32_t enum_inst, uint32_t target_field_idx) {
+ uint16_t small = zir->inst_datas[enum_inst].extended.small;
+ uint32_t operand = zir->inst_datas[enum_inst].extended.operand;
+
+ bool has_tag_type = (small & (1 << 0)) != 0;
+ bool has_captures_len = (small & (1 << 1)) != 0;
+ bool has_body_len = (small & (1 << 2)) != 0;
+ bool has_fields_len = (small & (1 << 3)) != 0;
+ bool has_decls_len = (small & (1 << 4)) != 0;
+
+ uint32_t extra_index = operand + 6; // skip header
+
+ // Find the tag type.
+ uint32_t tag_type_ref = 0;
+ if (has_tag_type)
+ tag_type_ref = zir->extra[extra_index++];
+
+ uint32_t captures_len = 0;
+ if (has_captures_len)
+ captures_len = zir->extra[extra_index++];
+ if (has_body_len)
+ extra_index++;
+ uint32_t fields_len = 0;
+ if (has_fields_len)
+ fields_len = zir->extra[extra_index++];
+ if (has_decls_len)
+ extra_index++;
+ extra_index += captures_len * 2;
+ (void)extra_index;
+
+ // Resolve the tag type IP index.
+ InternPoolIndex int_tag_type = IP_INDEX_NONE;
+ if (has_tag_type && tag_type_ref != 0) {
+ if (tag_type_ref < ZIR_REF_START_INDEX) {
+ int_tag_type = tag_type_ref;
+ } else {
+ uint32_t tt_inst = tag_type_ref - ZIR_REF_START_INDEX;
+ if (tt_inst < zir->inst_len
+ && zir->inst_tags[tt_inst] == ZIR_INST_INT_TYPE) {
+ uint16_t bits = zir->inst_datas[tt_inst].int_type.bit_count;
+ uint8_t zir_sign
+ = zir->inst_datas[tt_inst].int_type.signedness;
+ uint8_t ip_sign = (zir_sign == 0) ? 1 : 0;
+ InternPoolKey itk;
+ memset(&itk, 0, sizeof(itk));
+ itk.tag = IP_KEY_INT_TYPE;
+ itk.data.int_type.bits = bits;
+ itk.data.int_type.signedness = ip_sign;
+ int_tag_type = ipIntern(s_module_ip, itk);
+ }
+ }
+ }
+
+ // For auto enums, infer tag type from field count.
+ if (!has_tag_type) {
+ uint32_t bits = 0;
+ if (fields_len > 1) {
+ uint32_t n = fields_len - 1;
+ while (n > 0) {
+ bits++;
+ n >>= 1;
+ }
+ }
+ if (bits == 0)
+ bits = 1;
+ InternPoolKey itk;
+ memset(&itk, 0, sizeof(itk));
+ itk.tag = IP_KEY_INT_TYPE;
+ itk.data.int_type.bits = (uint16_t)bits;
+ itk.data.int_type.signedness = 0;
+ int_tag_type = ipIntern(s_module_ip, itk);
+ }
+
+ if (int_tag_type == IP_INDEX_NONE)
+ return IP_INDEX_NONE;
+
+ // Compute the field's integer value (auto-increment, skipping
+ // explicit values). Simple case: auto-enum field_idx maps to value.
+ return internTypedInt(int_tag_type, target_field_idx);
+}
+
+// Forward declaration: resolves a ZIR instruction to an IP type index.
+static InternPoolIndex resolveZirTypeInst(
+ const Zir* zir, uint32_t inst, uint32_t struct_ns, uint32_t file_idx);
+
+// --- resolveZirPtrTypeInst ---
+// Resolve a ZIR ptr_type instruction to create IP pointer/slice type entries.
+// For slices: creates a many-pointer type + slice type.
+// For single/many/c pointers: creates a pointer type.
+// Returns the final IP index for the type.
+// Ported from Sema.zig zirPtrType.
+static InternPoolIndex resolveZirPtrTypeInst(const Zir* zir, uint32_t inst) {
+ uint8_t flags = zir->inst_datas[inst].ptr_type.flags;
+ uint8_t size = zir->inst_datas[inst].ptr_type.size;
+ uint32_t pi = zir->inst_datas[inst].ptr_type.payload_index;
+
+ // extra[pi] = elem_type Ref, extra[pi+1] = src_node.
+ uint32_t elem_ref = zir->extra[pi];
+ bool has_sentinel = (flags & (1 << 3)) != 0;
+ bool has_align = (flags & (1 << 4)) != 0;
+ bool has_addrspace = (flags & (1 << 5)) != 0;
+ bool has_bit_range = (flags & (1 << 6)) != 0;
+ bool is_mutable = (flags & (1 << 1)) != 0;
+
+ // Read trailing sentinel if present.
+ uint32_t trail = pi + 2;
+ InternPoolIndex sentinel_ip = IP_INDEX_NONE;
+ if (has_sentinel)
+ sentinel_ip = zir->extra[trail++];
+ if (has_align)
+ trail++;
+ if (has_addrspace)
+ trail++;
+ if (has_bit_range)
+ trail += 2;
+ (void)trail;
+
+ // Resolve the element type. For pre-interned refs, direct mapping.
+ InternPoolIndex child_ip;
+ if (elem_ref < ZIR_REF_START_INDEX) {
+ child_ip = elem_ref;
+ } else {
+ // Instruction ref — recurse for known types.
+ uint32_t child_inst = elem_ref - ZIR_REF_START_INDEX;
+ if (child_inst < zir->inst_len
+ && zir->inst_tags[child_inst] == ZIR_INST_PTR_TYPE) {
+ child_ip = resolveZirPtrTypeInst(zir, child_inst);
+ } else if (child_inst < zir->inst_len
+ && zir->inst_tags[child_inst] == ZIR_INST_OPTIONAL_TYPE) {
+ // optional_type uses node data: inst_datas[inst].node is src_node,
+ // but the operand is inst_datas[inst].un_node.operand.
+ // Actually, optional_type uses .un_node: { operand: Ref, node: i32
+ // } For now, skip — will handle when needed.
+ return IP_INDEX_NONE;
+ } else {
+ return IP_INDEX_NONE;
+ }
+ }
+ if (child_ip == IP_INDEX_NONE)
+ return IP_INDEX_NONE;
+
+ // Build the pointer flags.
+ uint32_t ptr_flags = 0;
+ if (size == 2) {
+ // Slice: create many-pointer first, then slice wrapper.
+ ptr_flags = PTR_FLAGS_SIZE_MANY;
+ } else {
+ ptr_flags = (uint32_t)size;
+ }
+ if (!is_mutable)
+ ptr_flags |= PTR_FLAGS_IS_CONST;
+
+ // Create the pointer type.
+ InternPoolKey ptr_key;
+ memset(&ptr_key, 0, sizeof(ptr_key));
+ ptr_key.tag = IP_KEY_PTR_TYPE;
+ ptr_key.data.ptr_type.child = child_ip;
+ ptr_key.data.ptr_type.sentinel = sentinel_ip;
+ ptr_key.data.ptr_type.flags = ptr_flags;
+ ptr_key.data.ptr_type.packed_offset = 0;
+ InternPoolIndex ptr_ip = ipIntern(s_module_ip, ptr_key);
+
+ if (size == 2) {
+ // Slice type wrapping the many-pointer.
+ InternPoolKey slice_key;
+ memset(&slice_key, 0, sizeof(slice_key));
+ slice_key.tag = IP_KEY_SLICE;
+ slice_key.data.slice = ptr_ip;
+ return ipIntern(s_module_ip, slice_key);
+ }
+ return ptr_ip;
+}
+
+// --- resolveZirTypeInst ---
+// Resolve a ZIR instruction to an IP type index.
+// Handles: ptr_type, optional_type, decl_val, int_type.
+// For decl_val, searches struct_ns then file root namespace.
+// Ported from Sema.zig resolveInst dispatch for type instructions.
+static InternPoolIndex resolveZirTypeInst(
+ const Zir* zir, uint32_t inst, uint32_t struct_ns, uint32_t file_idx) {
+ if (inst >= zir->inst_len)
+ return IP_INDEX_NONE;
+ ZirInstTag tag = zir->inst_tags[inst];
+
+ if (tag == ZIR_INST_PTR_TYPE)
+ return resolveZirPtrTypeInst(zir, inst);
+
+ if (tag == ZIR_INST_OPTIONAL_TYPE) {
+ ZirInstRef child_ref = zir->inst_datas[inst].un_node.operand;
+ InternPoolIndex child_ip;
+ if (child_ref < ZIR_REF_START_INDEX) {
+ child_ip = child_ref;
+ } else {
+ child_ip = resolveZirTypeInst(
+ zir, child_ref - ZIR_REF_START_INDEX, struct_ns, file_idx);
+ }
+ if (child_ip == IP_INDEX_NONE)
+ return IP_INDEX_NONE;
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_OPT_TYPE;
+ key.data.opt_type = child_ip;
+ return ipIntern(s_module_ip, key);
+ }
+
+ if (tag == ZIR_INST_DECL_VAL) {
+ uint32_t name_str = zir->inst_datas[inst].str_tok.start;
+ const char* name = (const char*)(zir->string_bytes + name_str);
+ // Search struct's own namespace first, then file root.
+ uint32_t nav = findNavInNamespace(struct_ns, name);
+ if (nav == UINT32_MAX) {
+ uint32_t root_ns = s_file_namespace[file_idx];
+ nav = findNavInNamespace(root_ns, name);
+ }
+ if (nav != UINT32_MAX)
+ return ensureNavValUpToDate(nav);
+ }
+
+ if (tag == ZIR_INST_INT_TYPE) {
+ uint16_t bits = zir->inst_datas[inst].int_type.bit_count;
+ // Our astgen uses inverted signedness: 1=unsigned, 0=signed.
+ // IP convention: 0=unsigned, 1=signed. Invert to match.
+ uint8_t signedness = zir->inst_datas[inst].int_type.signedness ? 0 : 1;
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_INT_TYPE;
+ key.data.int_type.bits = bits;
+ key.data.int_type.signedness = signedness;
+ return ipIntern(s_module_ip, key);
+ }
+
+ return IP_INDEX_NONE;
+}
+
+// --- resolveZirTypeRef ---
+// Resolve a ZIR Ref to an IP type index.
+// For pre-interned refs, returns the ref directly.
+// For instruction refs, dispatches to resolveZirTypeInst.
+static InternPoolIndex resolveZirTypeRef(
+ const Zir* zir, ZirInstRef ref, uint32_t struct_ns, uint32_t file_idx) {
+ if (ref < ZIR_REF_START_INDEX)
+ return ref;
+ return resolveZirTypeInst(
+ zir, ref - ZIR_REF_START_INDEX, struct_ns, file_idx);
+}
+
+// --- resolveStructFieldTypesC ---
+// Resolve struct field types from ZIR, creating any needed IP entries
+// for compound types (pointers, slices, optionals, etc.).
+// Ported from Sema.zig structFields + resolveStructFully.
+static void resolveStructFieldTypesC(const Zir* zir, uint32_t struct_inst,
+ uint32_t struct_ns, uint32_t file_idx) {
+ if (zir->inst_tags[struct_inst] != ZIR_INST_EXTENDED)
+ return;
+ if (zir->inst_datas[struct_inst].extended.opcode != ZIR_EXT_STRUCT_DECL)
+ return;
+
+ uint16_t ssmall = zir->inst_datas[struct_inst].extended.small;
+ uint32_t soperand = zir->inst_datas[struct_inst].extended.operand;
+
+ bool s_has_captures = (ssmall & (1 << 0)) != 0;
+ bool s_has_fields = (ssmall & (1 << 1)) != 0;
+ bool s_has_decls = (ssmall & (1 << 2)) != 0;
+ bool s_has_backing_int = (ssmall & (1 << 3)) != 0;
+
+ uint32_t sei = soperand + 6; // skip header
+
+ uint32_t s_captures_len = 0;
+ uint32_t s_fields_len = 0;
+ uint32_t s_decls_len = 0;
+ if (s_has_captures)
+ s_captures_len = zir->extra[sei++];
+ if (s_has_fields)
+ s_fields_len = zir->extra[sei++];
+ if (s_has_decls)
+ s_decls_len = zir->extra[sei++];
+
+ if (s_fields_len == 0)
+ return;
+
+ sei += s_captures_len * 2;
+ if (s_has_backing_int) {
+ uint32_t bi_body = zir->extra[sei++];
+ sei += (bi_body == 0) ? 1 : bi_body;
+ }
+ sei += s_decls_len;
+
+ // Parse field bit bags.
+ uint32_t bags_start = sei;
+ uint32_t bags_count = (s_fields_len + 7) / 8;
+ sei += bags_count;
+
+ typedef struct {
+ uint32_t type_body_len;
+ uint32_t align_body_len;
+ uint32_t init_body_len;
+ bool has_type_body;
+ uint32_t type_ref; // only valid if !has_type_body
+ } FieldTypeInfo;
+ FieldTypeInfo field_info[64];
+ if (s_fields_len > 64)
+ return;
+
+ bool any_default_inits = (ssmall & (1 << 10)) != 0;
+
+ for (uint32_t fi = 0; fi < s_fields_len; fi++) {
+ uint32_t bag_i = fi / 8;
+ uint32_t bit_off = (fi % 8) * 4;
+ uint32_t bits = (zir->extra[bags_start + bag_i] >> bit_off) & 0xf;
+ bool f_has_align = (bits & 1) != 0;
+ bool f_has_init = ((bits >> 1) & 1) != 0;
+ bool f_has_type_body = ((bits >> 3) & 1) != 0;
+
+ sei++; // skip field_name
+
+ field_info[fi].has_type_body = f_has_type_body;
+ field_info[fi].type_body_len = 0;
+ field_info[fi].align_body_len = 0;
+ field_info[fi].init_body_len = 0;
+
+ if (f_has_type_body) {
+ field_info[fi].type_body_len = zir->extra[sei];
+ field_info[fi].type_ref = 0;
+ } else {
+ field_info[fi].type_ref = zir->extra[sei];
+ }
+ sei++;
+
+ if (f_has_align) {
+ field_info[fi].align_body_len = zir->extra[sei];
+ sei++;
+ }
+ if (f_has_init && any_default_inits) {
+ field_info[fi].init_body_len = zir->extra[sei];
+ sei++;
+ }
+ }
+
+ // Second pass: process type bodies via resolveZirTypeRef.
+ for (uint32_t fi = 0; fi < s_fields_len; fi++) {
+ if (field_info[fi].has_type_body && field_info[fi].type_body_len > 0) {
+ // Find the break_inline at the end of the type body.
+ uint32_t tbl = field_info[fi].type_body_len;
+ uint32_t last_zi = zir->extra[sei + tbl - 1];
+ if (last_zi < zir->inst_len
+ && zir->inst_tags[last_zi] == ZIR_INST_BREAK_INLINE) {
+ ZirInstRef op = zir->inst_datas[last_zi].break_data.operand;
+ (void)resolveZirTypeRef(zir, op, struct_ns, file_idx);
+ }
+ } else if (!field_info[fi].has_type_body
+ && field_info[fi].type_ref >= ZIR_REF_START_INDEX) {
+ // Non-body field type that's an instruction ref.
+ (void)resolveZirTypeRef(
+ zir, field_info[fi].type_ref, struct_ns, file_idx);
+ }
+ sei += field_info[fi].type_body_len;
+ sei += field_info[fi].align_body_len;
+ sei += field_info[fi].init_body_len;
+ }
+}
+
// --- resolveStructFieldInitsC ---
// Evaluate struct field default values from ZIR init bodies.
// For each field with an init body, reads the break_inline operand,
@@ -2955,8 +3322,8 @@ static uint32_t findEnumFieldByName(
// zir - the ZIR containing the struct_decl
// struct_inst - ZIR instruction index of the struct_decl
// struct_ns - the struct's namespace index in s_namespaces
-static void resolveStructFieldInitsC(
- const Zir* zir, uint32_t struct_inst, uint32_t struct_ns) {
+static void resolveStructFieldInitsC(const Zir* zir, uint32_t struct_inst,
+ uint32_t struct_ns, uint32_t file_idx) {
if (zir->inst_tags[struct_inst] != ZIR_INST_EXTENDED)
return;
if (zir->inst_datas[struct_inst].extended.opcode != ZIR_EXT_STRUCT_DECL)
@@ -3002,11 +3369,13 @@ static void resolveStructFieldInitsC(
uint32_t bags_count = (s_fields_len + 7) / 8;
sei += bags_count;
- // First pass: collect type_body_len, align_body_len, init_body_len.
+ // First pass: collect field info including type refs.
typedef struct {
uint32_t type_body_len;
uint32_t align_body_len;
uint32_t init_body_len;
+ uint32_t type_ref; // valid when has_type_body == false
+ bool has_type_body;
} FieldInfo;
FieldInfo field_info[32]; // max fields we handle
if (s_fields_len > 32)
@@ -3020,17 +3389,20 @@ static void resolveStructFieldInitsC(
bool f_has_init = ((bits >> 1) & 1) != 0;
bool f_has_type_body = ((bits >> 3) & 1) != 0;
(void)f_has_align;
- (void)f_has_type_body;
sei++; // skip field_name
field_info[fi].type_body_len = 0;
field_info[fi].align_body_len = 0;
field_info[fi].init_body_len = 0;
+ field_info[fi].type_ref = 0;
+ field_info[fi].has_type_body = f_has_type_body;
if (f_has_type_body)
field_info[fi].type_body_len = zir->extra[sei];
- sei++; // type_body_len or type_ref (always present)
+ else
+ field_info[fi].type_ref = zir->extra[sei];
+ sei++;
if (f_has_align) {
field_info[fi].align_body_len = zir->extra[sei];
@@ -3042,6 +3414,18 @@ static void resolveStructFieldInitsC(
}
}
+ // Compute type body start positions from cumulative offsets.
+ uint32_t type_body_starts[32];
+ {
+ uint32_t pos = sei;
+ for (uint32_t fi = 0; fi < s_fields_len; fi++) {
+ type_body_starts[fi] = pos;
+ pos += field_info[fi].type_body_len;
+ pos += field_info[fi].align_body_len;
+ pos += field_info[fi].init_body_len;
+ }
+ }
+
// Second pass: iterate through bodies and evaluate init bodies.
for (uint32_t fi = 0; fi < s_fields_len; fi++) {
sei += field_info[fi].type_body_len;
@@ -3062,9 +3446,32 @@ static void resolveStructFieldInitsC(
ZirInstRef operand = zir->inst_datas[last_zi].break_data.operand;
- // Pre-interned null → opt_null already created by caller.
- if (operand == ZIR_REF_NULL_VALUE)
+ // Null default → create opt_null(field_type).
+ if (operand == ZIR_REF_NULL_VALUE) {
+ // Resolve the field type to get the optional type IP index.
+ InternPoolIndex opt_ty = IP_INDEX_NONE;
+ if (field_info[fi].has_type_body
+ && field_info[fi].type_body_len > 0) {
+ uint32_t tbs = type_body_starts[fi];
+ uint32_t tbl = field_info[fi].type_body_len;
+ uint32_t tb_last = zir->extra[tbs + tbl - 1];
+ if (tb_last < zir->inst_len
+ && zir->inst_tags[tb_last] == ZIR_INST_BREAK_INLINE) {
+ ZirInstRef type_op
+ = zir->inst_datas[tb_last].break_data.operand;
+ opt_ty
+ = resolveZirTypeRef(zir, type_op, struct_ns, file_idx);
+ }
+ }
+ if (opt_ty != IP_INDEX_NONE) {
+ InternPoolKey ok;
+ memset(&ok, 0, sizeof(ok));
+ ok.tag = IP_KEY_OPT;
+ ok.data.opt = opt_ty;
+ (void)ipIntern(s_module_ip, ok);
+ }
continue;
+ }
// Must be a ZIR instruction ref.
if (operand < ZIR_REF_START_INDEX)
@@ -3073,69 +3480,122 @@ static void resolveStructFieldInitsC(
if (operand_inst >= zir->inst_len)
continue;
- // Determine the field name from the operand instruction.
+ // Case 1: enum literal default (e.g., rw: Rw = .read).
const char* lit_name = NULL;
if (zir->inst_tags[operand_inst] == ZIR_INST_ENUM_LITERAL) {
- // Enum literal: .generic, .eic, etc.
uint32_t str_start = zir->inst_datas[operand_inst].str_tok.start;
lit_name = (const char*)&zir->string_bytes[str_start];
} else if (zir->inst_tags[operand_inst] == ZIR_INST_DECL_LITERAL) {
- // Decl literal: .generic, .eic compiled as decl reference.
- // Payload is Field: { lhs: Ref, field_name_start: u32 }.
uint32_t pi = zir->inst_datas[operand_inst].pl_node.payload_index;
uint32_t name_start = zir->extra[pi + 1];
lit_name = (const char*)&zir->string_bytes[name_start];
}
if (lit_name != NULL) {
+ // Find matching enum type in the struct's namespace,
+ // then fall back to the file root namespace.
+ uint32_t ns_search[2];
+ ns_search[0] = struct_ns;
+ ns_search[1] = s_file_namespace[file_idx];
+ bool found = false;
+ for (uint32_t nk = 0; nk < 2 && !found; nk++) {
+ const SemaNamespace* sns = &s_namespaces[ns_search[nk]];
+ for (uint32_t j = 0; j < sns->pub_nav_count; j++) {
+ const Nav* enav = ipGetNav(sns->pub_navs[j]);
+ if (enav->resolved_type == IP_INDEX_NONE)
+ continue;
+ if (s_module_ip->items[enav->resolved_type].tag
+ != IP_KEY_ENUM_TYPE)
+ continue;
- // Find enum types in the struct's namespace and match
- // the field name.
- const SemaNamespace* sns = &s_namespaces[struct_ns];
- for (uint32_t j = 0; j < sns->pub_nav_count; j++) {
- const Nav* enav = ipGetNav(sns->pub_navs[j]);
- if (enav->resolved_type == IP_INDEX_NONE)
- continue;
- if (s_module_ip->items[enav->resolved_type].tag
- != IP_KEY_ENUM_TYPE)
- continue;
+ InternPoolIndex enum_ip = enav->resolved_type;
+ const uint32_t* ebody = NULL;
+ uint32_t ebody_len = 0;
+ getValueBodyFromZir(
+ zir, enav->zir_index, &ebody, &ebody_len);
+ uint32_t ed_inst = UINT32_MAX;
+ for (uint32_t ei = 0; ei < ebody_len; ei++) {
+ uint32_t einst = ebody[ei];
+ if (einst < zir->inst_len
+ && zir->inst_tags[einst] == ZIR_INST_EXTENDED
+ && zir->inst_datas[einst].extended.opcode
+ == ZIR_EXT_ENUM_DECL) {
+ ed_inst = einst;
+ break;
+ }
+ }
+ if (ed_inst == UINT32_MAX)
+ continue;
+ uint32_t field_idx
+ = findEnumFieldByName(zir, ed_inst, lit_name);
+ if (field_idx == UINT32_MAX)
+ continue;
- InternPoolIndex enum_ip = enav->resolved_type;
- // Get the actual enum_decl instruction from
- // the nav's declaration body.
- const uint32_t* ebody = NULL;
- uint32_t ebody_len = 0;
- getValueBodyFromZir(zir, enav->zir_index, &ebody, &ebody_len);
- uint32_t ed_inst = UINT32_MAX;
- for (uint32_t ei = 0; ei < ebody_len; ei++) {
- uint32_t einst = ebody[ei];
- if (einst < zir->inst_len
- && zir->inst_tags[einst] == ZIR_INST_EXTENDED
- && zir->inst_datas[einst].extended.opcode
- == ZIR_EXT_ENUM_DECL) {
- ed_inst = einst;
+ // Find the enum's tag type from ZIR and compute
+ // the int value for this field.
+ InternPoolIndex int_val
+ = getEnumFieldIntVal(zir, ed_inst, field_idx);
+ if (int_val == IP_INDEX_NONE)
break;
- }
+
+ InternPoolKey etk;
+ memset(&etk, 0, sizeof(etk));
+ etk.tag = IP_KEY_ENUM_TAG;
+ etk.data.enum_tag.ty = enum_ip;
+ etk.data.enum_tag.int_val = int_val;
+ (void)ipIntern(s_module_ip, etk);
+ found = true;
+ break;
}
- if (ed_inst == UINT32_MAX)
- continue;
- uint32_t field_idx
- = findEnumFieldByName(zir, ed_inst, lit_name);
- if (field_idx == UINT32_MAX)
- continue;
+ }
+ continue;
+ }
- // Create enum_tag. The int value IP index is at
- // enum_ip + 2 + field_idx (enum_type,
- // int_backing_type, then sequential values).
- InternPoolKey etk;
- memset(&etk, 0, sizeof(etk));
- etk.tag = IP_KEY_ENUM_TAG;
- etk.data.enum_tag.ty = enum_ip;
- etk.data.enum_tag.int_val = enum_ip + 2 + field_idx;
- (void)ipIntern(s_module_ip, etk);
- break; // found the matching enum
+ // Case 2: integer literal default (e.g., locality: u2 = 3).
+ if (zir->inst_tags[operand_inst] == ZIR_INST_INT) {
+ uint64_t val = zir->inst_datas[operand_inst].int_val;
+ InternPoolIndex field_ty = IP_INDEX_NONE;
+
+ if (!field_info[fi].has_type_body
+ && field_info[fi].type_ref < ZIR_REF_START_INDEX) {
+ // Simple pre-interned type ref.
+ field_ty = field_info[fi].type_ref;
+ } else if (field_info[fi].has_type_body
+ && field_info[fi].type_body_len > 0) {
+ // Resolve the field type from the type body.
+ // Find the break_inline at end of type body.
+ uint32_t tbs = type_body_starts[fi];
+ uint32_t tbl = field_info[fi].type_body_len;
+ uint32_t tb_last = zir->extra[tbs + tbl - 1];
+ if (tb_last < zir->inst_len
+ && zir->inst_tags[tb_last] == ZIR_INST_BREAK_INLINE) {
+ ZirInstRef type_op
+ = zir->inst_datas[tb_last].break_data.operand;
+ if (type_op < ZIR_REF_START_INDEX) {
+ field_ty = type_op;
+ } else {
+ uint32_t ti = type_op - ZIR_REF_START_INDEX;
+ if (ti < zir->inst_len
+ && zir->inst_tags[ti] == ZIR_INST_INT_TYPE) {
+ uint16_t bits
+ = zir->inst_datas[ti].int_type.bit_count;
+ uint8_t zsign
+ = zir->inst_datas[ti].int_type.signedness;
+ uint8_t ip_sign = (zsign == 0) ? 1 : 0;
+ InternPoolKey itk;
+ memset(&itk, 0, sizeof(itk));
+ itk.tag = IP_KEY_INT_TYPE;
+ itk.data.int_type.bits = bits;
+ itk.data.int_type.signedness = ip_sign;
+ field_ty = ipIntern(s_module_ip, itk);
+ }
+ }
+ }
}
+
+ if (field_ty != IP_INDEX_NONE)
+ (void)internTypedInt(field_ty, val);
}
}
}
@@ -3177,6 +3637,17 @@ static InternPoolIndex resolveStructDeclFromZir(
extra_index += captures_len * 2; // skip captures
+ // Skip backing_int if present (bit 3 of small).
+ bool has_backing_int = (small & (1 << 3)) != 0;
+ if (has_backing_int) {
+ uint32_t backing_int_body_len = zir->extra[extra_index++];
+ if (backing_int_body_len == 0) {
+ extra_index++; // skip backing_int_ref
+ } else {
+ extra_index += backing_int_body_len; // skip body instructions
+ }
+ }
+
// Create type_struct IP entry.
InternPoolKey key;
memset(&key, 0, sizeof(key));
@@ -3204,6 +3675,130 @@ static InternPoolIndex resolveStructDeclFromZir(
return struct_ip;
}
+// --- findNamespaceForType ---
+// Find the namespace owned by a given type IP index.
+static uint32_t findNamespaceForType(InternPoolIndex type_ip) {
+ for (uint32_t i = 0; i < s_num_namespaces; i++) {
+ if (s_namespaces[i].owner_type == type_ip)
+ return i;
+ }
+ return UINT32_MAX;
+}
+
+// --- isNavStructDecl ---
+// Check if a nav's ZIR body contains a struct_decl (without resolving it).
+static bool isNavStructDecl(uint32_t nav_idx) {
+ const Nav* nav = ipGetNav(nav_idx);
+ uint32_t ns_idx = nav->namespace_idx;
+ const SemaNamespace* ns = &s_namespaces[ns_idx];
+ uint32_t file_idx = ns->file_idx;
+ if (!s_loaded_modules[file_idx].has_zir)
+ return false;
+ const Zir* zir = &s_loaded_modules[file_idx].zir;
+ const uint32_t* body = NULL;
+ uint32_t body_len = 0;
+ getValueBodyFromZir(zir, nav->zir_index, &body, &body_len);
+ if (!body || body_len == 0)
+ return false;
+ for (uint32_t i = 0; i < body_len; i++) {
+ uint32_t inst = body[i];
+ if (inst < zir->inst_len && zir->inst_tags[inst] == ZIR_INST_EXTENDED
+ && zir->inst_datas[inst].extended.opcode == ZIR_EXT_STRUCT_DECL)
+ return true;
+ }
+ return false;
+}
+
+// --- resolveStructInnerDeclsC ---
+// Phase 1 resolution: resolve inner struct declarations of a struct
+// (creating nested struct types) WITHOUT resolving field types, field
+// default values, or inner enum types. Enum inner declarations are
+// deferred to the second phase to match the Zig compiler's order.
+static void resolveStructInnerDeclsC(uint32_t nav_idx) {
+ const Nav* nav = ipGetNav(nav_idx);
+ if (nav->resolved_type == IP_INDEX_NONE)
+ return;
+ if (s_module_ip->items[nav->resolved_type].tag != IP_KEY_STRUCT_TYPE)
+ return;
+ uint32_t ns_idx = findNamespaceForType(nav->resolved_type);
+ if (ns_idx == UINT32_MAX)
+ return;
+ const SemaNamespace* sns = &s_namespaces[ns_idx];
+ for (uint32_t i = 0; i < sns->pub_nav_count; i++) {
+ if (!isNavStructDecl(sns->pub_navs[i]))
+ continue;
+ (void)ensureNavValUpToDate(sns->pub_navs[i]);
+ resolveStructInnerDeclsC(sns->pub_navs[i]);
+ }
+ for (uint32_t i = 0; i < sns->priv_nav_count; i++) {
+ if (!isNavStructDecl(sns->priv_navs[i]))
+ continue;
+ (void)ensureNavValUpToDate(sns->priv_navs[i]);
+ resolveStructInnerDeclsC(sns->priv_navs[i]);
+ }
+}
+
+// --- resolveStructFullyC ---
+// Second-phase resolution for a struct type: resolve inner declarations,
+// field types, and field default values. Ported from Sema.zig
+// resolveStructFully (called after all BuiltinDecl types are created).
+static bool s_struct_fully_resolved[4096];
+static void resolveStructFullyC(uint32_t nav_idx) {
+ const Nav* nav = ipGetNav(nav_idx);
+ if (nav->resolved_type == IP_INDEX_NONE)
+ return;
+ if (s_module_ip->items[nav->resolved_type].tag != IP_KEY_STRUCT_TYPE)
+ return;
+ if (nav_idx < 4096 && s_struct_fully_resolved[nav_idx])
+ return;
+ if (nav_idx < 4096)
+ s_struct_fully_resolved[nav_idx] = true;
+
+ // Find the struct's namespace.
+ uint32_t ns_idx = findNamespaceForType(nav->resolved_type);
+ if (ns_idx == UINT32_MAX)
+ return;
+
+ // Resolve inner declarations (inner enum/struct/union types).
+ {
+ const SemaNamespace* sns = &s_namespaces[ns_idx];
+ for (uint32_t i = 0; i < sns->pub_nav_count; i++)
+ (void)ensureNavValUpToDate(sns->pub_navs[i]);
+ for (uint32_t i = 0; i < sns->priv_nav_count; i++)
+ (void)ensureNavValUpToDate(sns->priv_navs[i]);
+ }
+
+ // Find the struct_decl ZIR instruction.
+ uint32_t file_idx = s_namespaces[ns_idx].file_idx;
+ if (!s_loaded_modules[file_idx].has_zir)
+ return;
+ const Zir* zir = &s_loaded_modules[file_idx].zir;
+
+ const uint32_t* vbody = NULL;
+ uint32_t vbody_len = 0;
+ getValueBodyFromZir(zir, nav->zir_index, &vbody, &vbody_len);
+ if (!vbody)
+ return;
+
+ uint32_t struct_inst = UINT32_MAX;
+ for (uint32_t i = 0; i < vbody_len; i++) {
+ uint32_t inst = vbody[i];
+ if (inst < zir->inst_len && zir->inst_tags[inst] == ZIR_INST_EXTENDED
+ && zir->inst_datas[inst].extended.opcode == ZIR_EXT_STRUCT_DECL) {
+ struct_inst = inst;
+ break;
+ }
+ }
+ if (struct_inst == UINT32_MAX)
+ return;
+
+ // Resolve field types (creates pointer/slice/optional IP entries).
+ resolveStructFieldTypesC(zir, struct_inst, ns_idx, file_idx);
+
+ // Resolve field default values (creates enum_tag, opt_null, etc.).
+ resolveStructFieldInitsC(zir, struct_inst, ns_idx, file_idx);
+}
+
// --- resolveUnionDeclFromZir ---
// Parse a union_decl extended ZIR instruction and create IP entries.
// Creates: type_union IP entry, namespace, scanNamespace for declarations,
@@ -3283,13 +3878,17 @@ static InternPoolIndex resolveUnionDeclFromZir(
// Resolve declarations inside the union namespace.
// This creates struct types for sub-types like CommonOptions.
// Each sub-type creates: type_struct + ptr_nav.
+ // After each struct, resolve its inner STRUCT declarations
+ // (but not enums) to match the Zig compiler's interleaved order.
{
const SemaNamespace* uns = &s_namespaces[ns_idx];
for (uint32_t i = 0; i < uns->pub_nav_count; i++) {
(void)ensureNavValUpToDate(uns->pub_navs[i]);
+ resolveStructInnerDeclsC(uns->pub_navs[i]);
}
for (uint32_t i = 0; i < uns->priv_nav_count; i++) {
(void)ensureNavValUpToDate(uns->priv_navs[i]);
+ resolveStructInnerDeclsC(uns->priv_navs[i]);
}
}
@@ -3335,7 +3934,9 @@ static InternPoolIndex resolveUnionDeclFromZir(
// Resolve inner declarations of sub-type structs.
// Each struct created by resolveStructDeclFromZir has a namespace.
// Iterate over the namespaces created during sub-type resolution
- // and resolve their inner declarations (inner enums).
+ // and resolve their inner declarations (inner enums, nested structs).
+ // Uses resolveStructInnerDeclsC to recursively create nested types
+ // matching the Zig compiler's eager inner type resolution.
{
const SemaNamespace* uns = &s_namespaces[ns_idx];
for (uint32_t i = 0; i < uns->pub_nav_count; i++) {
@@ -3348,9 +3949,11 @@ static InternPoolIndex resolveUnionDeclFromZir(
const SemaNamespace* sns = &s_namespaces[nsi];
for (uint32_t j = 0; j < sns->pub_nav_count; j++) {
(void)ensureNavValUpToDate(sns->pub_navs[j]);
+ resolveStructInnerDeclsC(sns->pub_navs[j]);
}
for (uint32_t j = 0; j < sns->priv_nav_count; j++) {
(void)ensureNavValUpToDate(sns->priv_navs[j]);
+ resolveStructInnerDeclsC(sns->priv_navs[j]);
}
break;
}
@@ -3405,7 +4008,8 @@ static InternPoolIndex resolveUnionDeclFromZir(
for (uint32_t nsi = 0; nsi < s_num_namespaces; nsi++) {
if (s_namespaces[nsi].owner_type
== subnav->resolved_type) {
- resolveStructFieldInitsC(zir, sd_inst, nsi);
+ resolveStructFieldInitsC(
+ zir, sd_inst, nsi, file_idx);
break;
}
}
@@ -3623,11 +4227,51 @@ static void internStringLiteral(const char* str) {
(void)ipIntern(s_module_ip, bytes2_key);
}
+// --- internFuncType ---
+// Create a function type IP entry.
+// Ported from InternPool.zig getFuncType.
+static InternPoolIndex internFuncType(InternPoolIndex return_type,
+ uint32_t param_count, uint8_t cc, bool is_noinline) {
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_FUNC_TYPE;
+ key.data.func_type.return_type = return_type;
+ key.data.func_type.param_count = param_count;
+ key.data.func_type.cc = cc;
+ key.data.func_type.is_noinline = is_noinline;
+ return ipIntern(s_module_ip, key);
+}
+
+// --- internFuncDecl ---
+// Create a func_decl IP entry for a function declaration.
+// Ported from InternPool.zig getFuncDecl.
+static InternPoolIndex internFuncDecl(
+ uint32_t owner_nav, InternPoolIndex func_type) {
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_FUNC;
+ key.data.func_decl.owner_nav = owner_nav;
+ key.data.func_decl.ty = func_type;
+ return ipIntern(s_module_ip, key);
+}
+
+// --- resolveNestedTypeDecl ---
+// Resolve a nested type declaration: ensureNavValUpToDate +
+// resolveStructFullyC. Returns the nav index (or UINT32_MAX if not found).
+static uint32_t resolveNestedTypeDecl(uint32_t parent_ns, const char* name) {
+ uint32_t nav = findNavInNamespace(parent_ns, name);
+ if (nav != UINT32_MAX) {
+ (void)ensureNavValUpToDate(nav);
+ resolveStructFullyC(nav);
+ }
+ return nav;
+}
+
// --- resolveBuiltinDeclTypes ---
// Resolve BuiltinDecl types from std.builtin namespace.
// Ported from Sema.zig analyzeMemoizedState (main stage).
// This resolves types in BuiltinDecl order: Signedness, AddressSpace,
-// CallingConvention, etc.
+// CallingConvention, returnError, StackTrace, SourceLocation, etc.
static void resolveBuiltinDeclTypes(uint32_t builtin_ns_idx) {
// BuiltinDecl types in order (main stage only).
// Each is looked up in std.builtin and resolved.
@@ -3644,6 +4288,103 @@ static void resolveBuiltinDeclTypes(uint32_t builtin_ns_idx) {
if (nav != UINT32_MAX)
(void)ensureNavValUpToDate(nav);
}
+
+ // returnError: kind=func. Create noinline fn() void type and func_decl.
+ // Ported from analyzeMemoizedState .func handling +
+ // getExpectedBuiltinFnType for .returnError.
+ {
+ uint32_t nav = findNavInNamespace(builtin_ns_idx, "returnError");
+ if (nav != UINT32_MAX) {
+ // cc=0 is auto (CallingConvention.Tag first variant).
+ InternPoolIndex ft
+ = internFuncType(IP_INDEX_VOID_TYPE, 0, 0, true);
+ InternPoolIndex fd = internFuncDecl(nav, ft);
+ Nav* n = ipGetNav(nav);
+ n->resolved_type = fd;
+ InternPoolIndex ptr_ty = internPtrConst(ft);
+ (void)internNavPtr(ptr_ty, nav);
+ }
+ }
+
+ // Remaining BuiltinDecl types (main stage, type kind).
+ static const char* const builtin_type_names2[] = {
+ "StackTrace",
+ "SourceLocation",
+ "CallModifier",
+ "AtomicOrder",
+ "AtomicRmwOp",
+ "ReduceOp",
+ "FloatMode",
+ "PrefetchOptions",
+ "ExportOptions",
+ "ExternOptions",
+ "BranchHint",
+ "Type",
+ };
+ uint32_t n_types2
+ = sizeof(builtin_type_names2) / sizeof(builtin_type_names2[0]);
+ for (uint32_t i = 0; i < n_types2; i++) {
+ uint32_t nav
+ = findNavInNamespace(builtin_ns_idx, builtin_type_names2[i]);
+ if (nav != UINT32_MAX) {
+ (void)ensureNavValUpToDate(nav);
+ resolveStructFullyC(nav);
+ }
+ }
+
+ // Nested Type.* types in BuiltinDecl order.
+ // Ported from analyzeMemoizedState nested access pattern.
+ uint32_t type_nav = findNavInNamespace(builtin_ns_idx, "Type");
+ if (type_nav == UINT32_MAX)
+ return;
+ uint32_t type_ns = findNamespaceForType(ipGetNav(type_nav)->resolved_type);
+ if (type_ns == UINT32_MAX)
+ return;
+
+ // Type.Fn
+ uint32_t fn_nav = resolveNestedTypeDecl(type_ns, "Fn");
+
+ // Type.Fn.Param
+ if (fn_nav != UINT32_MAX) {
+ uint32_t fn_ns = findNamespaceForType(ipGetNav(fn_nav)->resolved_type);
+ if (fn_ns != UINT32_MAX)
+ (void)resolveNestedTypeDecl(fn_ns, "Param");
+ }
+
+ // Type.Int, Type.Float, Type.Pointer
+ (void)resolveNestedTypeDecl(type_ns, "Int");
+ (void)resolveNestedTypeDecl(type_ns, "Float");
+ uint32_t pointer_nav = resolveNestedTypeDecl(type_ns, "Pointer");
+
+ // Type.Pointer.Size
+ if (pointer_nav != UINT32_MAX) {
+ uint32_t ptr_ns
+ = findNamespaceForType(ipGetNav(pointer_nav)->resolved_type);
+ if (ptr_ns != UINT32_MAX)
+ (void)resolveNestedTypeDecl(ptr_ns, "Size");
+ }
+
+ // Remaining Type.* direct children.
+ static const char* const type_children[] = {
+ "Array",
+ "Vector",
+ "Optional",
+ "Error",
+ "ErrorUnion",
+ "EnumField",
+ "Enum",
+ "Union",
+ "UnionField",
+ "Struct",
+ "StructField",
+ "ContainerLayout",
+ "Opaque",
+ "Declaration",
+ };
+ uint32_t n_children = sizeof(type_children) / sizeof(type_children[0]);
+ for (uint32_t i = 0; i < n_children; i++) {
+ (void)resolveNestedTypeDecl(type_ns, type_children[i]);
+ }
}
// --- resolveStartComptimePreamble ---
diff --git a/stage0/verbose_intern_pool.c b/stage0/verbose_intern_pool.c
@@ -163,6 +163,9 @@ void verboseIpPrint(FILE* out, const InternPool* ip) {
case IP_KEY_OPT:
fprintf(out, " ty=%u", key.data.opt);
break;
+ case IP_KEY_SLICE:
+ fprintf(out, " ptr=%u", key.data.slice);
+ break;
case IP_KEY_OPT_PAYLOAD:
fprintf(out, " ty=%u val=%u", key.data.opt_payload.ty,
key.data.opt_payload.val);