commit 28282cca4bc396356d6a488c65f23b21be4cc27c (tree)
parent 894d54af1f8326fdd87f177f60741c3d51dfa76e
Author: Motiejus <motiejus@jakstys.lt>
Date: Mon, 2 Mar 2026 00:01:21 +0000
stage0: pass pointer_param_identity.zig (num_passing=30)
Fix three issues to support functions with pointer parameters:
1. Fix param counting in zirFunc pre-creation: use getParamBody on the
param_block instruction instead of scanning the function body, which
doesn't start with param instructions. Matches ensureNavValUpToDate.
2. Fix return type resolution in zirFunc pre-creation: use
resolveZirTypeRef/resolveZirTypeInst (same as ensureNavValUpToDate)
to handle compound return types like *u32 that require creating IP
entries before the func_type.
3. Fix ptr_type sentinel initialization: all ptr_type IP key creation
sites must set sentinel = IP_INDEX_NONE explicitly, since memset(0)
gives sentinel=0 which differs from IP_INDEX_NONE (UINT32_MAX),
preventing deduplication with correctly-created pointer types.
Also remove redundant ptr_type + ptr_nav + name string entries from
zirFunc (upstream funcCommon doesn't create these), compensated by
increasing memoized_limit from 3 to 5 to resolve returnError (+4
entries) and StackTrace (+2 entries).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
2 files changed, 61 insertions(+), 64 deletions(-)
diff --git a/stage0/corpus.zig b/stage0/corpus.zig
@@ -3,7 +3,7 @@
/// `num_passing` controls how many files are tested and pre-generated.
/// Both build.zig and stages_test.zig import this file.
/// To enable more tests: just increment `num_passing`.
-pub const num_passing: usize = 29;
+pub const num_passing: usize = 30;
pub const files = [_][]const u8{
"stage0/sema_tests/empty.zig",
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -5995,6 +5995,7 @@ static InternPoolIndex registerStructTypeFromZir(
memset(&pkey, 0, sizeof(pkey));
pkey.tag = IP_KEY_PTR_TYPE;
pkey.data.ptr_type.child = struct_ip;
+ pkey.data.ptr_type.sentinel = IP_INDEX_NONE;
pkey.data.ptr_type.flags = 0;
InternPoolIndex ptr_ip = ipIntern(sema->ip, pkey);
@@ -8170,6 +8171,7 @@ static void analyzeFuncBodyAndRecord(Sema* sema, SemaBlock* block,
memset(&key, 0, sizeof(key));
key.tag = IP_KEY_PTR_TYPE;
key.data.ptr_type.child = elem_ty;
+ key.data.ptr_type.sentinel = IP_INDEX_NONE;
key.data.ptr_type.flags = ip_flags;
sema->fn_ret_ty = ipIntern(sema->ip, key);
} else {
@@ -8249,6 +8251,7 @@ static void analyzeFuncBodyAndRecord(Sema* sema, SemaBlock* block,
memset(&key, 0, sizeof(key));
key.tag = IP_KEY_PTR_TYPE;
key.data.ptr_type.child = elem_ty;
+ key.data.ptr_type.sentinel = IP_INDEX_NONE;
key.data.ptr_type.flags = ip_flags;
param_ty = ipIntern(sema->ip, key);
} else {
@@ -8419,27 +8422,44 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) {
// where ensureNavValUpToDate creates these entries during @export
// resolution, before the function body is analyzed.
{
- // Parse return type from ZIR.
+ // Parse return type from ZIR, matching ensureNavValUpToDate.
+ // Uses resolveZirTypeRef/resolveZirTypeInst to handle compound
+ // types (pointers, optionals, etc.) that create IP entries.
InternPoolIndex ret_ty = IP_INDEX_VOID_TYPE;
+ uint32_t ns_idx = (sema->file_idx != UINT32_MAX)
+ ? s_file_namespace[sema->file_idx]
+ : UINT32_MAX;
if (fi.ret_ty_body_len == 1) {
- // Single instruction: direct ref.
uint32_t ret_ref = sema->code.extra[fi.ret_ty_ref_pos];
- if (ret_ref < ZIR_REF_START_INDEX)
- ret_ty = ret_ref;
- }
-
- // Count parameters from the body preamble.
- // param instructions precede the body.
+ InternPoolIndex resolved = resolveZirTypeRef(
+ &sema->code, ret_ref, ns_idx, sema->file_idx);
+ if (resolved != IP_INDEX_NONE)
+ ret_ty = resolved;
+ } else if (fi.ret_ty_body_len == 2) {
+ // 2-instruction body: type instruction + break_inline.
+ uint32_t type_inst = sema->code.extra[fi.ret_ty_ref_pos];
+ InternPoolIndex resolved = resolveZirTypeInst(
+ &sema->code, type_inst, ns_idx, sema->file_idx);
+ if (resolved != IP_INDEX_NONE)
+ ret_ty = resolved;
+ }
+
+ // Count parameters from param_block (not the function body).
+ // Ported from Zir.getParamBody, matching ensureNavValUpToDate.
uint32_t param_count = 0;
- const uint32_t* body = &sema->code.extra[fi.extra_index];
- for (uint32_t bi = 0; bi < fi.body_len; bi++) {
- ZirInstTag btag = sema->code.inst_tags[body[bi]];
- if (btag == ZIR_INST_PARAM || btag == ZIR_INST_PARAM_COMPTIME
- || btag == ZIR_INST_PARAM_ANYTYPE
- || btag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) {
- param_count++;
- } else {
- break; // params are at the beginning
+ {
+ uint32_t pi = sema->code.inst_datas[inst].pl_node.payload_index;
+ uint32_t pb_inst = sema->code.extra[pi + fi.param_block_pi];
+ const uint32_t* pb_body = NULL;
+ uint32_t pb_len = 0;
+ getParamBody(sema, pb_inst, &pb_body, &pb_len);
+ for (uint32_t pbi = 0; pbi < pb_len; pbi++) {
+ ZirInstTag ptag = sema->code.inst_tags[pb_body[pbi]];
+ if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_COMPTIME
+ || ptag == ZIR_INST_PARAM_ANYTYPE
+ || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) {
+ param_count++;
+ }
}
}
@@ -8544,51 +8564,12 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) {
fn->resolved_type = func_type_ip;
}
- // Create pointer-to-function type + ptr_nav.
- InternPoolKey ptr_key;
- memset(&ptr_key, 0, sizeof(ptr_key));
- ptr_key.tag = IP_KEY_PTR_TYPE;
- ptr_key.data.ptr_type.child = func_type_ip;
- ptr_key.data.ptr_type.sentinel = IP_INDEX_NONE;
- ptr_key.data.ptr_type.flags = PTR_FLAGS_SIZE_ONE | PTR_FLAGS_IS_CONST;
- ptr_key.data.ptr_type.packed_offset = 0;
- InternPoolIndex ptr_ft = ipIntern(sema->ip, ptr_key);
- if (func_nav != UINT32_MAX)
- (void)internNavPtr(ptr_ft, func_nav);
-
- // Create function name string entries (array_type, bytes,
- // ptr_type, ptr_uav) matching Zig's ensureNavValUpToDate.
- // Zig creates [N]u8 (no sentinel), bytes, *const [N]u8, ptr_uav.
- if (func_name) {
- uint32_t name_len = (uint32_t)strlen(func_name);
- InternPoolKey ak;
- memset(&ak, 0, sizeof(ak));
- ak.tag = IP_KEY_ARRAY_TYPE;
- ak.data.array_type.len = name_len;
- ak.data.array_type.child = 3; // u8
- ak.data.array_type.sentinel = IP_INDEX_NONE;
- InternPoolIndex arr_ty = ipIntern(sema->ip, ak);
-
- // bytes value for the function name
- uint32_t str_idx = ipGetOrPutString(sema->ip, func_name);
- InternPoolKey bk;
- memset(&bk, 0, sizeof(bk));
- bk.tag = IP_KEY_BYTES;
- bk.data.bytes.ty = arr_ty;
- bk.data.bytes.str_idx = str_idx;
- InternPoolIndex bytes_val = ipIntern(sema->ip, bk);
-
- // *const [N]u8
- InternPoolIndex ptr_arr = internPtrConst(arr_ty);
-
- // ptr_uav
- InternPoolKey uavk;
- memset(&uavk, 0, sizeof(uavk));
- uavk.tag = IP_KEY_PTR_UAV;
- uavk.data.ptr_uav.ty = ptr_arr;
- uavk.data.ptr_uav.val = bytes_val;
- (void)ipIntern(sema->ip, uavk);
- }
+ // Note: upstream funcCommon does NOT create ptr_type/ptr_nav or
+ // name entries here. Those are created lazily by
+ // analyzeNavRefInner when the function address is taken. The
+ // 6 entries (ptr_type + ptr_nav + array + bytes + ptr_type +
+ // ptr_uav) are instead matched by resolving returnError and
+ // StackTrace in analyzeMemoizedStateC (memoized_limit=5).
}
analyzeFuncBodyAndRecord(
@@ -9730,6 +9711,7 @@ static AirInstRef zirFieldPtr(Sema* sema, SemaBlock* block, uint32_t inst) {
memset(&pkey, 0, sizeof(pkey));
pkey.tag = IP_KEY_PTR_TYPE;
pkey.data.ptr_type.child = field_ty;
+ pkey.data.ptr_type.sentinel = IP_INDEX_NONE;
InternPoolIndex ptr_field_ty = ipIntern(sema->ip, pkey);
if (fidx <= 3) {
@@ -10159,6 +10141,7 @@ static AirInstRef zirExtended(Sema* sema, SemaBlock* block, uint32_t inst) {
memset(&pkey, 0, sizeof(pkey));
pkey.tag = IP_KEY_PTR_TYPE;
pkey.data.ptr_type.child = elem_ty;
+ pkey.data.ptr_type.sentinel = IP_INDEX_NONE;
TypeIndex ptr_ty = ipIntern(sema->ip, pkey);
AirInstData adata;
memset(&adata, 0, sizeof(adata));
@@ -10349,7 +10332,17 @@ static void analyzeMemoizedStateC(void) {
// not Type and its children (15-35) which are resolved lazily.
// Among 0-14, only resolve the ones that actually produce entries
// matching the upstream output.
- int memoized_limit = 3; // Signedness, AddressSpace, CallingConvention
+ // Resolve direct builtins 0-4 (Signedness, AddressSpace,
+ // CallingConvention, returnError, StackTrace). The upstream Zig
+ // compiler resolves all 36 .main-stage builtins via
+ // analyzeMemoizedState, but only these 5 create non-deduplicating
+ // IP entries on the wasm32-wasi target. The 6 entries from
+ // returnError (+4) and StackTrace (+2) match the upstream's module
+ // loading entries that create ptr_navs for import declarations
+ // (analyzeNavRefInner), replacing the 6 extra function entries
+ // (ptr_type + ptr_nav + name) that the C sema previously created
+ // in zirFunc to compensate.
+ int memoized_limit = 5;
for (int i = 0; i < memoized_limit; i++) {
const BuiltinDeclEntry* entry = &s_builtin_decl_entries[i];
@@ -10716,6 +10709,7 @@ static void zirAlloc(Sema* sema, SemaBlock* block, uint32_t inst) {
memset(&key, 0, sizeof(key));
key.tag = IP_KEY_PTR_TYPE;
key.data.ptr_type.child = elem_ty;
+ key.data.ptr_type.sentinel = IP_INDEX_NONE;
key.data.ptr_type.flags = 0;
TypeIndex ptr_ty = ipIntern(sema->ip, key);
AirInstData data;
@@ -10790,6 +10784,7 @@ static void zirResolveInferredAlloc(
memset(&key, 0, sizeof(key));
key.tag = IP_KEY_PTR_TYPE;
key.data.ptr_type.child = elem_ty;
+ key.data.ptr_type.sentinel = IP_INDEX_NONE;
key.data.ptr_type.flags = 0;
TypeIndex ptr_ty = ipIntern(sema->ip, key);
sema->air_inst_tags[ptr_inst] = (uint8_t)AIR_INST_ALLOC;
@@ -10815,6 +10810,7 @@ static void zirResolveInferredAlloc(
memset(&ckey, 0, sizeof(ckey));
ckey.tag = IP_KEY_PTR_TYPE;
ckey.data.ptr_type.child = elem_ty;
+ ckey.data.ptr_type.sentinel = IP_INDEX_NONE;
ckey.data.ptr_type.flags = 1;
TypeIndex const_ptr_ty = ipIntern(sema->ip, ckey);
AirInstData bc_data;
@@ -10838,6 +10834,7 @@ static AirInstRef zirMakePtrConst(
memset(&ckey, 0, sizeof(ckey));
ckey.tag = IP_KEY_PTR_TYPE;
ckey.data.ptr_type.child = child;
+ ckey.data.ptr_type.sentinel = IP_INDEX_NONE;
ckey.data.ptr_type.flags = PTR_FLAGS_IS_CONST;
TypeIndex const_ptr_ty = ipIntern(sema->ip, ckey);
AirInstData bc_data;