zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 83e972b87f0dd87119733f39286b36f12659cde3 (tree)
parent 58eb5f6f3c3e7a286ac85bf65c3d9472624ae2f3
Author: Motiejus <motiejus@jakstys.lt>
Date:   Fri, 27 Feb 2026 01:06:38 +0000

sema: add resolveStructLayoutC to match Zig's resolveUnionLayout ordering

In the Zig compiler, resolveUnionLayout calls resolveStructLayout for each
struct-typed field BEFORE resolveUnionFully's field loop creates init values.
This means type entries (like ?u64) are created during layout resolution,
while init entries (like opt_null) come later in resolveStructFully.

Split resolveStructFullyC into resolveStructLayoutC (field types only) and
resolveStructFullyC (inits + recursive). Call resolveStructLayoutC in
Phase 2b of resolveUnionFullyC after tag values, matching the Zig compiler's
ordering.

Also add resolveTypeFullyC and resolveStructFieldTypesFully for recursive
type resolution matching Zig's Type.resolveFully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Diffstat:
Mstage0/sema.c | 310+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 272 insertions(+), 38 deletions(-)

diff --git a/stage0/sema.c b/stage0/sema.c @@ -3888,10 +3888,246 @@ static uint32_t findNamespaceForType(InternPoolIndex type_ip) { return UINT32_MAX; } +// Forward declarations for mutually recursive resolution. +static bool resolveStructLayoutC(uint32_t nav_idx); +static void resolveTypeFullyC(InternPoolIndex ip_idx); +static void resolveStructFullyC(uint32_t nav_idx); +static void resolveUnionFullyC(uint32_t nav_idx); + +// --- findNavForIPIndex --- +// Search all namespaces for a nav whose resolved_type matches ip_idx. +// Returns the nav index, or UINT32_MAX if not found. +static uint32_t findNavForIPIndex(InternPoolIndex ip_idx) { + for (uint32_t nsi = 0; nsi < s_num_namespaces; nsi++) { + const SemaNamespace* ns = &s_namespaces[nsi]; + for (uint32_t k = 0; k < ns->pub_nav_count; k++) { + if (ipGetNav(ns->pub_navs[k])->resolved_type == ip_idx) + return ns->pub_navs[k]; + } + for (uint32_t k = 0; k < ns->priv_nav_count; k++) { + if (ipGetNav(ns->priv_navs[k])->resolved_type == ip_idx) + return ns->priv_navs[k]; + } + } + return UINT32_MAX; +} + +// --- resolveTypeFullyC --- +// Recursively resolve a type, matching Zig's Type.resolveFully. +// For struct/union types: calls resolveStructFullyC/resolveUnionFullyC. +// For compound types (pointer, optional, array, slice): recurses on child. +// For simple types (int, float, bool, void, etc.): no-op. +static void resolveTypeFullyC(InternPoolIndex ip_idx) { + if (ip_idx == IP_INDEX_NONE || ip_idx >= s_module_ip->items_len) + return; + InternPoolKeyTag tag = s_module_ip->items[ip_idx].tag; + switch (tag) { + case IP_KEY_STRUCT_TYPE: { + uint32_t nav = findNavForIPIndex(ip_idx); + if (nav != UINT32_MAX) + resolveStructFullyC(nav); + break; + } + case IP_KEY_UNION_TYPE: { + uint32_t nav = findNavForIPIndex(ip_idx); + if (nav != UINT32_MAX) + resolveUnionFullyC(nav); + break; + } + case IP_KEY_PTR_TYPE: + resolveTypeFullyC(s_module_ip->items[ip_idx].data.ptr_type.child); + break; + case IP_KEY_OPT_TYPE: + resolveTypeFullyC(s_module_ip->items[ip_idx].data.opt_type); + break; + case IP_KEY_ARRAY_TYPE: + resolveTypeFullyC(s_module_ip->items[ip_idx].data.array_type.child); + break; + case IP_KEY_SLICE: + // Slice wraps a pointer type; resolve the pointer's child. + resolveTypeFullyC(s_module_ip->items[ip_idx].data.slice); + break; + default: + break; + } +} + +// --- resolveStructFieldTypesFully --- +// After struct field types have been resolved, recursively resolve each +// field type fully. Matches Zig's resolveStructFully loop: +// for (0..struct_type.field_types.len) |i| +// field_ty.resolveFully(pt); +// Re-parses the struct's ZIR fields to get the resolved type refs, then +// calls resolveTypeFullyC on each. +static void resolveStructFieldTypesFully(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; + + 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 || s_fields_len > 64) + 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; + + uint32_t bags_start = sei; + uint32_t bags_count = (s_fields_len + 7) / 8; + sei += bags_count; + + bool any_default_inits = (ssmall & (1 << 10)) != 0; + + // First pass: read field headers (same layout as + // resolveStructFieldTypesC). + 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; + } FieldInfo; + FieldInfo finfo[64]; + + 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 + + finfo[fi].has_type_body = f_has_type_body; + finfo[fi].type_body_len = 0; + finfo[fi].align_body_len = 0; + finfo[fi].init_body_len = 0; + + if (f_has_type_body) { + finfo[fi].type_body_len = zir->extra[sei]; + finfo[fi].type_ref = 0; + } else { + finfo[fi].type_ref = zir->extra[sei]; + } + sei++; + + if (f_has_align) { + finfo[fi].align_body_len = zir->extra[sei]; + sei++; + } + if (f_has_init && any_default_inits) { + finfo[fi].init_body_len = zir->extra[sei]; + sei++; + } + } + + // Second pass: resolve each field type and call resolveTypeFullyC. + for (uint32_t fi = 0; fi < s_fields_len; fi++) { + InternPoolIndex resolved = IP_INDEX_NONE; + if (finfo[fi].has_type_body && finfo[fi].type_body_len > 0) { + uint32_t tbl = finfo[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; + resolved = resolveZirTypeRef(zir, op, struct_ns, file_idx); + } + } else if (!finfo[fi].has_type_body + && finfo[fi].type_ref >= ZIR_REF_START_INDEX) { + resolved = resolveZirTypeRef( + zir, finfo[fi].type_ref, struct_ns, file_idx); + } + sei += finfo[fi].type_body_len; + sei += finfo[fi].align_body_len; + sei += finfo[fi].init_body_len; + + if (resolved != IP_INDEX_NONE) + resolveTypeFullyC(resolved); + } +} + +// --- resolveStructLayoutC --- +// Resolve struct field types (without inits or recursive resolution). +// Matches Zig's resolveStructLayout which is called during resolveUnionLayout. +// This creates type entries (like ?u64) during union layout resolution, +// before field init values (like opt_null) are created later in +// resolveStructFullyC. +static bool s_struct_layout_resolved[4096]; +static bool resolveStructLayoutC(uint32_t nav_idx) { + const Nav* nav = ipGetNav(nav_idx); + if (nav->resolved_type == IP_INDEX_NONE) + return false; + if (s_module_ip->items[nav->resolved_type].tag != IP_KEY_STRUCT_TYPE) + return false; + if (nav_idx < 4096 && s_struct_layout_resolved[nav_idx]) + return true; + if (nav_idx < 4096) + s_struct_layout_resolved[nav_idx] = true; + + // Find the struct's namespace. + uint32_t ns_idx = findNamespaceForType(nav->resolved_type); + if (ns_idx == UINT32_MAX) + return false; + + // 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 false; + 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 false; + + 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 false; + + // Resolve field types (creates pointer/slice/optional IP entries). + resolveStructFieldTypesC(zir, struct_inst, ns_idx, file_idx); + return true; +} + // --- resolveStructFullyC --- -// Second-phase resolution for a struct type: resolve inner declarations, -// field types, and field default values. Ported from Sema.zig +// Second-phase resolution for a struct type: resolve field default values +// and recursively resolve field types fully. Ported from Sema.zig // resolveStructFully (called after all BuiltinDecl types are created). +// Assumes resolveStructLayoutC has already been called (field types resolved). static bool s_struct_fully_resolved[4096]; static void resolveStructFullyC(uint32_t nav_idx) { const Nav* nav = ipGetNav(nav_idx); @@ -3904,6 +4140,9 @@ static void resolveStructFullyC(uint32_t nav_idx) { if (nav_idx < 4096) s_struct_fully_resolved[nav_idx] = true; + // Ensure layout is resolved first (idempotent). + resolveStructLayoutC(nav_idx); + // Find the struct's namespace. uint32_t ns_idx = findNamespaceForType(nav->resolved_type); if (ns_idx == UINT32_MAX) @@ -3933,11 +4172,12 @@ static void resolveStructFullyC(uint32_t nav_idx) { 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); + + // Recursively resolve field types (matching Zig's resolveStructFully + // field loop that calls field_ty.resolveFully on each field). + resolveStructFieldTypesFully(zir, struct_inst, ns_idx, file_idx); } // --- resolveUnionFullyC --- @@ -4016,8 +4256,9 @@ static void resolveUnionFullyC(uint32_t nav_idx) { extra_index += captures_len * 2; // skip captures // Phase 1: Resolve union field types in field order. - uint32_t resolved_struct_navs[64]; - uint32_t num_resolved_structs = 0; + // Store all resolved field type IP indices for Phase 3. + InternPoolIndex resolved_field_types[128]; + uint32_t num_resolved_fields = 0; { uint32_t field_extra = extra_index + decls_len + body_len; uint32_t num_bit_bags = (fields_len + 7) / 8; @@ -4035,32 +4276,8 @@ static void resolveUnionFullyC(uint32_t nav_idx) { ZirInstRef type_ref = zir->extra[cursor++]; InternPoolIndex resolved = resolveZirTypeRef(zir, type_ref, ns_idx, file_idx); - // Track struct-typed fields for deferred resolution in - // Phase 3. The Zig compiler creates all struct_type + - // ptr_nav pairs consecutively during union field - // iteration, then resolves their fields later. We must - // NOT call resolveStructFullyC here — that would create - // compound type entries (opt_type, ptr_type, slice) - // interleaved with the struct entries. - if (resolved != IP_INDEX_NONE && num_resolved_structs < 64 - && s_module_ip->items[resolved].tag - == IP_KEY_STRUCT_TYPE) { - for (uint32_t nsi = 0; nsi < s_num_namespaces; nsi++) { - const SemaNamespace* sns = &s_namespaces[nsi]; - bool found = false; - for (uint32_t k = 0; k < sns->pub_nav_count; k++) { - if (ipGetNav(sns->pub_navs[k])->resolved_type - == resolved) { - resolved_struct_navs[num_resolved_structs++] - = sns->pub_navs[k]; - found = true; - break; - } - } - if (found) - break; - } - } + if (resolved != IP_INDEX_NONE && num_resolved_fields < 128) + resolved_field_types[num_resolved_fields++] = resolved; } if (ft_has_align) cursor++; @@ -4093,11 +4310,28 @@ static void resolveUnionFullyC(uint32_t nav_idx) { (void)ipIntern(s_module_ip, ek); } - // Phase 3: Struct field resolution is deferred to resolveBuiltinDeclTypes - // which calls resolveStructFullyC for each nested type in the correct - // order, matching the Zig compiler's processing sequence. - (void)resolved_struct_navs; - (void)num_resolved_structs; + // Phase 2b: Resolve struct field types for each struct-typed field. + // Matches Zig's resolveUnionLayout which calls resolveStructLayout + // (and thus resolveStructFieldTypes) for each field type AFTER creating + // the tag enum. This creates type entries (like ?u64) during layout + // resolution, before field init values are created in Phase 3. + for (uint32_t i = 0; i < num_resolved_fields; i++) { + if (resolved_field_types[i] < s_module_ip->items_len + && s_module_ip->items[resolved_field_types[i]].tag + == IP_KEY_STRUCT_TYPE) { + uint32_t field_nav = findNavForIPIndex(resolved_field_types[i]); + if (field_nav != UINT32_MAX) + resolveStructLayoutC(field_nav); + } + } + + // Phase 3: Recursively resolve each field type fully. + // Matches Zig's resolveUnionFully field loop: + // for (0..union_obj.field_types.len) |field_index| + // field_ty.resolveFully(pt); + for (uint32_t i = 0; i < num_resolved_fields; i++) { + resolveTypeFullyC(resolved_field_types[i]); + } } // --- resolveUnionDeclFromZir ---