commit d29f7da7ab4d0bf812a52eaf29a33038c1785cf3 (tree)
parent 0e9bc3bf9b7c840a441b0d707ea06abefd0b5282
Author: Motiejus <motiejus@jakstys.lt>
Date: Tue, 3 Mar 2026 09:30:19 +0000
sema: fix duplicate func_type/func_decl and deferred body analysis (num_passing=87)
Two fixes for test 86 (inline_fn_with_plus_eq_call_inside_two_conditionals):
1. Skip duplicate func_type/func_decl in zirFunc when ensureNavValUpToDate
has already resolved the nav. Previously, ensureNavValUpToDate created
entries with ipForceIntern (no hash table) and zirFunc re-created them
with ipIntern (can't find existing), causing duplicates.
2. Defer non-generic body analysis from semaAnalyzeCall to match Zig's
ensureFuncBodyAnalysisQueued ordering. Callee body analysis is queued
and processed after the current function body completes, ensuring IP
entry ordering matches the Zig compiler's work queue semantics.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
3 files changed, 62 insertions(+), 21 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 = 86;
+pub const num_passing: usize = 87;
pub const files = [_][]const u8{
"stage0/sema_tests/empty.zig",
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -6126,18 +6126,29 @@ static AirInstRef semaAnalyzeCall(Sema* sema, SemaBlock* block, uint32_t inst,
.string_bytes[callee_name_idx];
body_name_idx = findStringInZirBytes(&sema->code, name);
}
- // Compute call-site arg types before entering body analysis
- // (which saves/resets AIR state, making semaTypeOf unavailable).
- TypeIndex arg_type_buf[16];
- const TypeIndex* arg_types_ptr = NULL;
- if (is_generic && args_len > 0) {
- for (uint32_t ai = 0; ai < args_len && ai < 16; ai++)
- arg_type_buf[ai] = semaTypeOf(sema, arg_refs[ai]);
- arg_types_ptr = arg_type_buf;
+ if (is_generic) {
+ // Generic functions need immediate analysis with call args
+ // (args are stack-allocated and won't survive deferral).
+ TypeIndex arg_type_buf[16];
+ const TypeIndex* arg_types_ptr = NULL;
+ if (args_len > 0) {
+ for (uint32_t ai = 0; ai < args_len && ai < 16; ai++)
+ arg_type_buf[ai] = semaTypeOf(sema, arg_refs[ai]);
+ arg_types_ptr = arg_type_buf;
+ }
+ analyzeFuncBodyAndRecord(sema, block, func_inst, body_name_idx,
+ arg_refs, args_len, arg_types_ptr, func_val_ip);
+ } else {
+ // Non-generic: defer body analysis to match Zig's
+ // ensureFuncBodyAnalysisQueued ordering. Body analysis
+ // runs after the current function body completes.
+ if (sema->num_deferred_body < 16) {
+ uint32_t di = sema->num_deferred_body++;
+ sema->deferred_body[di].func_inst = func_inst;
+ sema->deferred_body[di].name_idx = body_name_idx;
+ sema->deferred_body[di].func_val_ip = func_val_ip;
+ }
}
- analyzeFuncBodyAndRecord(sema, block, func_inst, body_name_idx,
- is_generic ? arg_refs : NULL, is_generic ? args_len : 0,
- arg_types_ptr, func_val_ip);
}
// Clean up cross-module state.
@@ -7358,6 +7369,7 @@ static void analyzeFuncBodyAndRecord(Sema* sema, SemaBlock* block,
TypeIndex saved_fn_ret_ty = sema->fn_ret_ty;
uint32_t saved_num_ia = sema->num_ia;
uint32_t saved_num_calls = sema->num_calls;
+ uint32_t saved_num_deferred = sema->num_deferred_body;
// --- Set up fresh AIR arrays for the function body ---
sema->air_inst_tags = ARR_INIT(uint8_t, SEMA_AIR_INITIAL_CAP);
sema->air_inst_datas = ARR_INIT(AirInstData, SEMA_AIR_INITIAL_CAP);
@@ -7665,6 +7677,21 @@ static void analyzeFuncBodyAndRecord(Sema* sema, SemaBlock* block,
sema->fn_ret_ty = saved_fn_ret_ty;
sema->num_ia = saved_num_ia;
sema->num_calls = saved_num_calls;
+
+ // Drain deferred body analysis queue: process entries added during
+ // this function's body analysis, matching Zig's work queue ordering.
+ while (sema->num_deferred_body > saved_num_deferred) {
+ uint32_t idx = saved_num_deferred;
+ uint32_t d_func_inst = sema->deferred_body[idx].func_inst;
+ uint32_t d_name_idx = sema->deferred_body[idx].name_idx;
+ InternPoolIndex d_fvip = sema->deferred_body[idx].func_val_ip;
+ // Remove first pending entry (shift remaining down).
+ for (uint32_t di = idx + 1; di < sema->num_deferred_body; di++)
+ sema->deferred_body[di - 1] = sema->deferred_body[di];
+ sema->num_deferred_body--;
+ analyzeFuncBodyAndRecord(
+ sema, block, d_func_inst, d_name_idx, NULL, 0, NULL, d_fvip);
+ }
}
// zirFunc: analyze a function declaration.
@@ -7906,13 +7933,6 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) {
cc = 1; // C calling convention (result of .c resolution)
}
- // Create function type IP entry. Ported from upstream
- // InternPool.getFuncType: func_types with the same full
- // signature (ret + params + cc) share the same IP entry.
- InternPoolIndex func_type_ip = internFuncType(
- sema, ret_ty, param_count, cc, false, param_type_buf);
-
- // Create func_decl entry (must follow func_type immediately).
// Find the nav for this function declaration.
const char* func_name = NULL;
if (sema->cur_decl_name != 0) {
@@ -7925,10 +7945,20 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) {
= sema->zcu->file_namespaces[sema->zcu->root_file_idx];
func_nav = findNavInNamespace(sema, rns, func_name);
}
+
+ // Only create func_type + func_decl if the nav hasn't been
+ // resolved yet. ensureNavValUpToDate may have already created
+ // these entries (e.g. when the function was referenced as a
+ // callee during inline expansion before zirFunc runs).
if (func_nav != UINT32_MAX) {
- (void)internFuncDecl(sema, func_nav, func_type_ip);
Nav* fn = ipGetNav(sema->ip, func_nav);
- fn->resolved_type = func_type_ip;
+ if (fn->resolved_type == IP_INDEX_NONE) {
+ InternPoolIndex func_type_ip = internFuncType(
+ sema, ret_ty, param_count, cc, false, param_type_buf);
+ (void)internFuncDecl(sema, func_nav, func_type_ip);
+ fn = ipGetNav(sema->ip, func_nav);
+ fn->resolved_type = func_type_ip;
+ }
}
// Note: upstream funcCommon does NOT create ptr_type/ptr_nav or
diff --git a/stage0/sema.h b/stage0/sema.h
@@ -267,6 +267,17 @@ typedef struct Sema {
} fields[4];
} ct_struct_vals[8];
uint32_t num_ct_struct_vals;
+ // Deferred body analysis queue: matches Zig's
+ // ensureFuncBodyAnalysisQueued. Non-inline callees encountered during body
+ // analysis are queued here and analyzed after the current function body
+ // completes, ensuring IP entry ordering matches the Zig compiler's work
+ // queue semantics.
+ struct {
+ uint32_t func_inst;
+ uint32_t name_idx;
+ InternPoolIndex func_val_ip;
+ } deferred_body[16];
+ uint32_t num_deferred_body;
} Sema;
// CallingConvention tag values (lib/std/builtin.zig CallingConvention enum).