commit 1fbfe8c8711a3c2ecd3db4f5ff1877ac76d89a6b (tree)
parent c5d4c61cf607f3f6e2f520b4ce5406b8f176bdf9
Author: Motiejus <motiejus@jakstys.lt>
Date: Tue, 10 Mar 2026 07:41:10 +0000
sema: store zir_inst in namespace, extract param type helper
Refactor to match upstream Zig's type resolution architecture:
- Add zir_inst to ZcuNamespace, matching upstream's
LoadedStructType.zir_index / LoadedEnumType.zir_index. Set at
namespace creation time in resolveStructDeclFromZir,
resolveEnumDeclFromZir, resolveUnionDeclFromZir.
- Simplify resolveStructLayoutC, resolveStructFullyC,
resolveUnionFullyC to use ns->zir_inst instead of scanning nav
value bodies. Removes ~45 lines of value-body-scan code.
- Extract resolveParamTypeFromZir helper from inline param type
resolution in ensureNavValUpToDate. Ported from Sema.zig zirParam
type body resolution.
- Add setNavResolvedType helper that centralizes nav resolved_type
setting with namespace owner_type remap for shard simulation.
- Add @This() support in resolveZirTypeInst, ported from Sema.zig
zirThis.
- Add nav_ns_remap infrastructure for shard simulation correctness:
tracks nav→namespace mapping so owner_types stay valid after
clearing nav resolved_types.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
5 files changed, 144 insertions(+), 77 deletions(-)
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -3645,6 +3645,7 @@ InternPoolIndex resolveEnumDeclFromZir(
= sema->zcu->namespaces[ipGetNav(sema->ip, nav_idx)->namespace_idx]
.file_idx;
uint32_t ens = createNamespace(sema, enum_ip, enum_file_idx);
+ sema->zcu->namespaces[ens].zir_inst = enum_inst;
scanNamespaceC(sema, ens, decl_insts, decls_len, zir);
}
@@ -4068,6 +4069,13 @@ InternPoolIndex resolveZirTypeInst(Sema* sema, const Zir* zir, uint32_t inst,
if (tag == ZIR_INST_PTR_TYPE)
return resolveZirPtrTypeInst(sema, zir, inst, struct_ns, file_idx);
+ // @This() returns the owner type of the enclosing namespace.
+ // Ported from Sema.zig zirThis → block.getNamespace().owner_type.
+ if (tag == ZIR_INST_EXTENDED
+ && zir->inst_datas[inst].extended.opcode == ZIR_EXT_THIS) {
+ return sema->zcu->namespaces[struct_ns].owner_type;
+ }
+
if (tag == ZIR_INST_OPTIONAL_TYPE) {
ZirInstRef child_ref = zir->inst_datas[inst].un_node.operand;
InternPoolIndex child_ip
@@ -4176,8 +4184,7 @@ InternPoolIndex resolveZirTypeInst(Sema* sema, const Zir* zir, uint32_t inst,
InternPoolIndex alias_result
= resolveZirTypeRef(sema, tzir, op, tnns, tnfi);
if (alias_result != IP_INDEX_NONE) {
- Nav* wnav = ipGetNav(sema->ip, nav);
- wnav->resolved_type = alias_result;
+ setNavResolvedType(sema, nav, alias_result);
// Create ptr_nav for the alias declaration,
// matching analyzeNavRefInner which creates
// a ptr_nav for every resolved nav reference.
@@ -4889,6 +4896,7 @@ InternPoolIndex resolveStructDeclFromZir(
const Nav* nav = ipGetNav(sema->ip, nav_idx);
uint32_t ns_idx = createNamespace(
sema, struct_ip, sema->zcu->namespaces[nav->namespace_idx].file_idx);
+ sema->zcu->namespaces[ns_idx].zir_inst = struct_inst;
// Scan namespace declarations.
const uint32_t* decl_insts = &zir->extra[extra_index];
@@ -5087,8 +5095,9 @@ static void resolveStructFieldTypesFully(Sema* sema, const Zir* zir,
sei += finfo[fi].align_body_len;
sei += finfo[fi].init_body_len;
- if (resolved != IP_INDEX_NONE)
+ if (resolved != IP_INDEX_NONE) {
resolveTypeFullyC(sema, resolved);
+ }
}
}
@@ -5114,27 +5123,13 @@ static bool resolveStructLayoutC(Sema* sema, uint32_t nav_idx) {
if (ns_idx == UINT32_MAX)
return false;
- // Find the struct_decl ZIR instruction.
+ // Get the struct_decl ZIR instruction from the namespace.
+ // Matches upstream: struct_type.zir_index.resolve(ip).
uint32_t file_idx = sema->zcu->namespaces[ns_idx].file_idx;
if (!sema->zcu->files[file_idx].has_zir)
return false;
const Zir* zir = &sema->zcu->files[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;
- }
- }
+ uint32_t struct_inst = sema->zcu->namespaces[ns_idx].zir_inst;
if (struct_inst == UINT32_MAX)
return false;
@@ -5150,12 +5145,15 @@ static bool resolveStructLayoutC(Sema* sema, uint32_t nav_idx) {
// Assumes resolveStructLayoutC has already been called (field types resolved).
static void resolveStructFullyC(Sema* sema, uint32_t nav_idx) {
const Nav* nav = ipGetNav(sema->ip, nav_idx);
- if (nav->resolved_type == IP_INDEX_NONE)
+ if (nav->resolved_type == IP_INDEX_NONE) {
return;
- if (sema->ip->items[nav->resolved_type].tag != IP_KEY_STRUCT_TYPE)
+ }
+ if (sema->ip->items[nav->resolved_type].tag != IP_KEY_STRUCT_TYPE) {
return;
- if (nav_idx < 4096 && sema->zcu->struct_fully_resolved[nav_idx])
+ }
+ if (nav_idx < 4096 && sema->zcu->struct_fully_resolved[nav_idx]) {
return;
+ }
if (nav_idx < 4096)
sema->zcu->struct_fully_resolved[nav_idx] = true;
@@ -5167,27 +5165,13 @@ static void resolveStructFullyC(Sema* sema, uint32_t nav_idx) {
if (ns_idx == UINT32_MAX)
return;
- // Find the struct_decl ZIR instruction.
+ // Get the struct_decl ZIR instruction from the namespace.
+ // Matches upstream: struct_type.zir_index.resolve(ip).
uint32_t file_idx = sema->zcu->namespaces[ns_idx].file_idx;
if (!sema->zcu->files[file_idx].has_zir)
return;
const Zir* zir = &sema->zcu->files[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;
- }
- }
+ uint32_t struct_inst = sema->zcu->namespaces[ns_idx].zir_inst;
if (struct_inst == UINT32_MAX)
return;
@@ -5234,27 +5218,13 @@ static void resolveUnionFullyC(Sema* sema, uint32_t nav_idx) {
if (ns_idx == UINT32_MAX)
return;
- // Find the union_decl ZIR instruction from the nav's declaration body.
+ // Get the union_decl ZIR instruction from the namespace.
+ // Matches upstream: union_type.zir_index.resolve(ip).
uint32_t file_idx = sema->zcu->namespaces[ns_idx].file_idx;
if (!sema->zcu->files[file_idx].has_zir)
return;
const Zir* zir = &sema->zcu->files[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 union_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_UNION_DECL) {
- union_inst = inst;
- break;
- }
- }
+ uint32_t union_inst = sema->zcu->namespaces[ns_idx].zir_inst;
if (union_inst == UINT32_MAX)
return;
@@ -5567,6 +5537,7 @@ InternPoolIndex resolveUnionDeclFromZir(
= sema->zcu->namespaces[ipGetNav(sema->ip, nav_idx)->namespace_idx]
.file_idx;
uint32_t ns_idx = createNamespace(sema, union_ip, file_idx);
+ sema->zcu->namespaces[ns_idx].zir_inst = union_inst;
// Scan namespace declarations (creates Navs for sub-types like
// CommonOptions, X86RegparmOptions, etc.).
@@ -11454,7 +11425,6 @@ static void zirExport(Sema* sema, uint32_t inst) {
// Ported from Sema.zig: zirExport → resolveExportOptions →
// getBuiltinType(.ExportOptions) → ensureMemoizedStateResolved(.main).
ensureFullMemoizedStateC(sema);
-
uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index;
uint32_t exported_ref = sema->code.extra[payload_index];
if (exported_ref >= ZIR_REF_START_INDEX) {
diff --git a/stage0/zcu.c b/stage0/zcu.c
@@ -1,5 +1,6 @@
#include "zcu.h"
#include <stdlib.h>
+#include <string.h>
Zcu* zcuInit(Compilation* comp) {
Zcu* zcu = (Zcu*)calloc(1, sizeof(Zcu));
@@ -12,6 +13,7 @@ Zcu* zcuInit(Compilation* comp) {
zcu->builtin_file_idx = UINT32_MAX;
zcu->cg_builtin_ns_idx = UINT32_MAX;
zcu->cg_builtin_nav = UINT32_MAX;
+ memset(zcu->nav_ns_remap, 0xFF, sizeof(zcu->nav_ns_remap));
return zcu;
}
diff --git a/stage0/zcu.h b/stage0/zcu.h
@@ -33,6 +33,10 @@ typedef struct {
typedef struct {
InternPoolIndex owner_type; // type_struct IP index
uint32_t file_idx; // index into files[]
+ uint32_t
+ zir_inst; // ZIR instruction index of the type declaration
+ // (struct_decl/enum_decl/union_decl). Matches upstream's
+ // LoadedStructType.zir_index / LoadedEnumType.zir_index.
uint32_t pub_navs[ZCU_NS_MAX_NAVS];
uint32_t pub_nav_count;
uint32_t priv_navs[ZCU_NS_MAX_NAVS];
@@ -143,6 +147,13 @@ typedef struct Zcu {
} ct_struct_vals[ZCU_MAX_CT_STRUCT_VALS];
uint32_t num_ct_struct_vals;
+ // --- Namespace owner_type remap (shard simulation) ---
+ // After clearing nav resolved_types, namespace owner_types become stale.
+ // This maps nav_idx → ns_idx so ensureNavValUpToDate can update them.
+ // Built by ensureFullMemoizedStateC before clearing, consumed during
+ // re-resolution. UINT32_MAX means no mapping for that nav.
+ uint32_t nav_ns_remap[4096];
+
// --- Compilation config ---
Compilation* comp; // back-pointer; matches Zcu.comp in Zig
} Zcu;
diff --git a/stage0/zcu_per_thread.c b/stage0/zcu_per_thread.c
@@ -205,6 +205,7 @@ uint32_t createNamespace(
memset(ns, 0, sizeof(*ns));
ns->owner_type = owner_type;
ns->file_idx = file_idx;
+ ns->zir_inst = UINT32_MAX;
return idx;
}
@@ -443,6 +444,21 @@ uint32_t findNamespaceForType(const Sema* sema, InternPoolIndex type_ip) {
return UINT32_MAX;
}
+// Update nav->resolved_type and fix stale namespace owner_type.
+// After shard simulation clears nav resolved_types, namespace owner_types
+// become stale (pointing to preamble IP indices). When a nav is re-resolved,
+// this function uses the nav_ns_remap mapping to update the namespace.
+void setNavResolvedType(
+ Sema* sema, uint32_t nav_idx, InternPoolIndex type_ip) {
+ Nav* nav = ipGetNav(sema->ip, nav_idx);
+ nav->resolved_type = type_ip;
+ if (nav_idx < 4096 && sema->zcu->nav_ns_remap[nav_idx] != UINT32_MAX) {
+ sema->zcu->namespaces[sema->zcu->nav_ns_remap[nav_idx]].owner_type
+ = type_ip;
+ sema->zcu->nav_ns_remap[nav_idx] = UINT32_MAX;
+ }
+}
+
uint32_t findNavForIPIndex(Sema* sema, InternPoolIndex ip_idx) {
for (uint32_t nsi = 0; nsi < sema->zcu->num_namespaces; nsi++) {
const ZcuNamespace* ns = &sema->zcu->namespaces[nsi];
@@ -459,7 +475,7 @@ uint32_t findNavForIPIndex(Sema* sema, InternPoolIndex ip_idx) {
}
static InternPoolIndex analyzeNavValC(Sema* sema, uint32_t nav_idx) {
- Nav* nav = ipGetNav(sema->ip, nav_idx);
+ const Nav* nav = ipGetNav(sema->ip, nav_idx);
uint32_t ns_idx = nav->namespace_idx;
const ZcuNamespace* ns = &sema->zcu->namespaces[ns_idx];
uint32_t file_idx = ns->file_idx;
@@ -578,8 +594,7 @@ static InternPoolIndex analyzeNavValC(Sema* sema, uint32_t nav_idx) {
InternPoolIndex type_result
= resolveZirTypeRef(sema, zir, op, ns_idx, file_idx);
if (type_result != IP_INDEX_NONE) {
- nav = ipGetNav(sema->ip, nav_idx);
- nav->resolved_type = type_result;
+ setNavResolvedType(sema, nav_idx, type_result);
return type_result;
}
}
@@ -594,17 +609,41 @@ static InternPoolIndex analyzeNavValC(Sema* sema, uint32_t nav_idx) {
// This allows callers to get both the type and the value.
// Ported from PerThread.zig analyzeNav which resolves type and val
// separately.
- nav = ipGetNav(sema->ip, nav_idx);
if (type_body_result != IP_INDEX_NONE) {
- nav->resolved_type = type_body_result;
- nav->resolved_val = result_ip;
+ setNavResolvedType(sema, nav_idx, type_body_result);
+ ipGetNav(sema->ip, nav_idx)->resolved_val = result_ip;
} else {
- nav->resolved_type = result_ip;
+ setNavResolvedType(sema, nav_idx, result_ip);
}
return result_ip;
}
+// Resolve a single ZIR param instruction's type.
+// Extracts the type body from the param's ZIR layout, finds the
+// break_inline result, and resolves it as a type reference.
+// Returns IP_INDEX_NONE if the param type cannot be resolved
+// (generic, anytype, or unresolvable).
+// Ported from Sema.zig zirParam type body resolution.
+static InternPoolIndex resolveParamTypeFromZir(Sema* sema, const Zir* zir,
+ uint32_t param_inst, uint32_t ns_idx, uint32_t file_idx) {
+ // Param layout: extra[ppl+0]=name,
+ // extra[ppl+1]=type_raw (body_len:31|generic:1),
+ // extra[ppl+2..ppl+1+body_len] = body instructions.
+ uint32_t ppl = zir->inst_datas[param_inst].pl_tok.payload_index;
+ uint32_t type_raw = zir->extra[ppl + 1];
+ bool is_generic = (type_raw >> 31) & 1;
+ uint32_t type_body_len = type_raw & 0x7FFFFFFF;
+ if (is_generic || type_body_len < 1)
+ return IP_INDEX_NONE;
+ uint32_t last_ti = zir->extra[ppl + 2 + type_body_len - 1];
+ if (last_ti >= zir->inst_len
+ || zir->inst_tags[last_ti] != ZIR_INST_BREAK_INLINE)
+ return IP_INDEX_NONE;
+ ZirInstRef type_ref = zir->inst_datas[last_ti].break_data.operand;
+ return resolveZirTypeRef(sema, zir, type_ref, ns_idx, file_idx);
+}
+
InternPoolIndex ensureNavValUpToDate(Sema* sema, uint32_t nav_idx) {
const Nav* nav = ipGetNav(sema->ip, nav_idx);
@@ -679,8 +718,7 @@ InternPoolIndex ensureNavValUpToDate(Sema* sema, uint32_t nav_idx) {
// @This() resolves to the type that owns the namespace
// this declaration is declared in. E.g. `const V = @This();`
// inside a struct resolves V to that struct type.
- Nav* wnav = ipGetNav(sema->ip, nav_idx);
- wnav->resolved_type = ns->owner_type;
+ setNavResolvedType(sema, nav_idx, ns->owner_type);
InternPoolIndex ptr_type
= internPtrConst(sema, IP_INDEX_TYPE_TYPE);
(void)internNavPtr(sema, ptr_type, nav_idx);
@@ -734,9 +772,13 @@ InternPoolIndex ensureNavValUpToDate(Sema* sema, uint32_t nav_idx) {
}
}
- // Count parameters from param_block.
- // Ported from Zir.getParamBody.
+ // Count parameters and resolve param types from param_block.
+ // Ported from Sema.zig zirParam + funcCommon param type
+ // handling.
uint32_t param_count = 0;
+ InternPoolIndex param_type_buf[FUNC_TYPE_MAX_PARAMS];
+ memset(param_type_buf, 0, sizeof(param_type_buf));
+ bool has_param_types = false;
{
uint32_t pi = zir->inst_datas[inst].pl_node.payload_index;
uint32_t pb_inst = zir->extra[pi + fi.param_block_pi];
@@ -746,8 +788,17 @@ InternPoolIndex ensureNavValUpToDate(Sema* sema, uint32_t nav_idx) {
for (uint32_t pbi = 0; pbi < pb_len; pbi++) {
ZirInstTag ptag = zir->inst_tags[pb_body[pbi]];
if (ptag == ZIR_INST_PARAM
- || ptag == ZIR_INST_PARAM_COMPTIME
- || ptag == ZIR_INST_PARAM_ANYTYPE
+ || ptag == ZIR_INST_PARAM_COMPTIME) {
+ if (param_count < FUNC_TYPE_MAX_PARAMS) {
+ InternPoolIndex resolved = resolveParamTypeFromZir(
+ sema, zir, pb_body[pbi], ns_idx, file_idx);
+ if (resolved != IP_INDEX_NONE) {
+ param_type_buf[param_count] = resolved;
+ has_param_types = true;
+ }
+ }
+ param_count++;
+ } else if (ptag == ZIR_INST_PARAM_ANYTYPE
|| ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) {
param_count++;
}
@@ -789,11 +840,23 @@ InternPoolIndex ensureNavValUpToDate(Sema* sema, uint32_t nav_idx) {
}
cc = 1; // C calling convention (bootstrap only)
}
- InternPoolIndex ft
- = internFuncType(sema, ret_ty, param_count, cc, false, NULL);
+ // Fully resolve param types before creating func_type.
+ // Ported from Sema.zig resolveFnTypes which calls
+ // resolveFully on each param type after analyzeFnBodyInner.
+ // This creates child type hierarchy entries (e.g. std.Target
+ // for *const Target) that must appear before func_type in
+ // the IP, matching the Zig compiler.
+ if (has_param_types) {
+ for (uint32_t p = 0;
+ p < param_count && p < FUNC_TYPE_MAX_PARAMS; p++) {
+ if (param_type_buf[p] != IP_INDEX_NONE)
+ resolveTypeFullyC(sema, param_type_buf[p]);
+ }
+ }
+ InternPoolIndex ft = internFuncType(sema, ret_ty, param_count, cc,
+ false, has_param_types ? param_type_buf : NULL);
InternPoolIndex fd = internFuncDecl(sema, nav_idx, ft);
- Nav* wnav = ipGetNav(sema->ip, nav_idx);
- wnav->resolved_type = fd;
+ setNavResolvedType(sema, nav_idx, fd);
InternPoolIndex ptr_ty = internPtrConst(sema, ft);
(void)internNavPtr(sema, ptr_ty, nav_idx);
return fd;
@@ -825,8 +888,7 @@ InternPoolIndex ensureNavValUpToDate(Sema* sema, uint32_t nav_idx) {
if (struct_type == 0 && import_file_idx > 0)
break;
- Nav* wnav = ipGetNav(sema->ip, nav_idx);
- wnav->resolved_type = struct_type;
+ setNavResolvedType(sema, nav_idx, struct_type);
return struct_type;
}
}
@@ -1244,6 +1306,24 @@ void ensureFullMemoizedStateC(Sema* sema) {
for (int i = 0; i < NUM_BUILTIN_DECL_MAIN; i++)
sema->zcu->builtin_decl_values[i] = IP_INDEX_NONE;
+ // Build nav → namespace mapping before clearing, so that
+ // ensureNavValUpToDate can update stale namespace owner_types
+ // when navs are re-resolved in the main shard.
+ memset(sema->zcu->nav_ns_remap, 0xFF, sizeof(sema->zcu->nav_ns_remap));
+ for (uint32_t n = 0; n < sema->ip->nav_count && n < 4096; n++) {
+ const Nav* nav_ptr = ipGetNav(sema->ip, n);
+ if (nav_ptr->resolved_type != IP_INDEX_NONE
+ && nav_ptr->resolved_type >= sema->ip->skip_dedup_start
+ && nav_ptr->resolved_type < sema->ip->skip_dedup_end) {
+ for (uint32_t nsi = 0; nsi < sema->zcu->num_namespaces; nsi++) {
+ if (sema->zcu->namespaces[nsi].owner_type
+ == nav_ptr->resolved_type) {
+ sema->zcu->nav_ns_remap[n] = nsi;
+ break;
+ }
+ }
+ }
+ }
// Clear nav resolved_type for ALL navs resolved during preamble.
// Simulates Zig's per-shard isolation: main shard starts fresh.
for (uint32_t n = 0; n < sema->ip->nav_count; n++) {
diff --git a/stage0/zcu_per_thread.h b/stage0/zcu_per_thread.h
@@ -75,6 +75,10 @@ uint32_t findNavInNamespace(
uint32_t findNamespaceForType(
const struct Sema* sema, InternPoolIndex type_ip);
+// Set nav->resolved_type and update stale namespace owner_type.
+void setNavResolvedType(
+ struct Sema* sema, uint32_t nav_idx, InternPoolIndex type_ip);
+
// Find a nav whose resolved_type == ip_idx. Returns UINT32_MAX if not found.
uint32_t findNavForIPIndex(struct Sema* sema, InternPoolIndex ip_idx);