commit 3acbd8ecf4abd4c9ae47082e4e93f4506a7ab285 (tree)
parent 19d65dbc8c8b4d978f5218d59f920475a9ae0266
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Thu, 26 Feb 2026 02:56:04 +0000
sema: fix IP entry ordering for std.zig comptime blocks and builtin chain
Create debug.assert entries ($130-$134) matching Zig compiler's evaluation
of std.zig's `comptime { debug.assert(@import("std") == @This()); }`.
Add resolveRootInStartModule for start.zig's "root" Nav ($136).
Reorder resolveBuiltinModuleChain to create std ptr_nav before
compiler-gen builtin struct ($139-$141).
Entries $124-$141 now match the Zig compiler exactly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
| M | stage0/sema.c | | | 171 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------- |
1 file changed, 135 insertions(+), 36 deletions(-)
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -2472,10 +2472,21 @@ static void resolveBuiltinModuleChain(void) {
InternPoolIndex ptr_type = internPtrConst(IP_INDEX_TYPE_TYPE);
(void)internNavPtr(ptr_type, builtin_nav);
+ // std/builtin.zig imports `std` (already loaded). Create ptr_nav
+ // for the `std` Nav in std/builtin.zig's namespace BEFORE the
+ // compiler-generated builtin struct, matching the Zig compiler's
+ // demand-driven resolution order.
+ uint32_t builtin_ns_idx = s_file_namespace[builtin_file_idx];
+ uint32_t std_in_builtin_nav = findNavInNamespace(builtin_ns_idx, "std");
+ if (std_in_builtin_nav != UINT32_MAX) {
+ Nav* sNav = ipGetNav(std_in_builtin_nav);
+ sNav->resolved_type = s_file_root_type[std_file_idx];
+ (void)internNavPtr(ptr_type, std_in_builtin_nav);
+ }
+
// std/builtin.zig imports @import("builtin") — the compiler-generated
// builtin module. Create a type_struct for it even though we don't have
// its source. The Zig compiler creates this struct in semaFile.
- uint32_t builtin_ns_idx = s_file_namespace[builtin_file_idx];
uint32_t cg_builtin_nav = findNavInNamespace(builtin_ns_idx, "builtin");
if (cg_builtin_nav != UINT32_MAX) {
// Create a placeholder type_struct for compiler-generated builtin.
@@ -2499,16 +2510,6 @@ static void resolveBuiltinModuleChain(void) {
(void)internNavPtr(ptr_type, cg_builtin_nav);
}
}
-
- // std/builtin.zig imports `std` (already loaded). Create ptr_nav
- // for the `std` Nav in std/builtin.zig's namespace, matching the
- // Zig compiler's demand-driven resolution order.
- uint32_t std_in_builtin_nav = findNavInNamespace(builtin_ns_idx, "std");
- if (std_in_builtin_nav != UINT32_MAX) {
- Nav* sNav = ipGetNav(std_in_builtin_nav);
- sNav->resolved_type = s_file_root_type[std_file_idx];
- (void)internNavPtr(ptr_type, std_in_builtin_nav);
- }
}
// Forward declarations for functions used by resolveNamedImport.
@@ -2523,37 +2524,120 @@ static bool resolveImportPath(const char* source_dir, const char* import_path,
// analyzeNavRefInner sequence.
// Used to selectively load specific imports from std.zig (e.g. "start",
// "debug") in the correct order, rather than loading all imports.
+// Returns the imported file index, or UINT32_MAX on failure.
-static void resolveNamedImport(uint32_t file_idx, const char* name) {
+static uint32_t resolveNamedImport(uint32_t file_idx, const char* name) {
uint32_t ns_idx = s_file_namespace[file_idx];
uint32_t nav_idx = findNavInNamespace(ns_idx, name);
if (nav_idx == UINT32_MAX)
- return;
+ return UINT32_MAX;
Nav* nav = ipGetNav(nav_idx);
const Zir* zir = &s_loaded_modules[file_idx].zir;
const char* import_path = findDeclImportPathFromZir(zir, nav->zir_index);
if (!import_path)
- return;
+ return UINT32_MAX;
char import_full[1024];
if (!resolveImportPath(s_loaded_modules[file_idx].source_dir, import_path,
import_full, sizeof(import_full)))
- return;
+ return UINT32_MAX;
uint32_t import_file_idx = ensureFileAnalyzedC(import_full);
if (import_file_idx == UINT32_MAX)
- return;
+ return UINT32_MAX;
InternPoolIndex struct_type = s_file_root_type[import_file_idx];
if (struct_type == 0 && import_file_idx > 0)
- return;
+ return UINT32_MAX;
// Create ptr_type(*const type) + ptr_nav, matching
// analyzeNavRefInner's sequence.
InternPoolIndex ptr_type = internPtrConst(IP_INDEX_TYPE_TYPE);
nav->resolved_type = struct_type;
(void)internNavPtr(ptr_type, nav_idx);
+ return import_file_idx;
+}
+
+// --- resolveDebugAssertEntries ---
+// Create IP entries for debug.assert matching the Zig compiler's sequence.
+// std.zig has `comptime { debug.assert(@import("std") == @This()); }`
+// which resolves the assert function and memoizes the call result.
+// Creates: func_type, func_decl, ptr_type, ptr_nav, memoized_call.
+static void resolveDebugAssertEntries(uint32_t debug_file_idx) {
+ if (debug_file_idx == UINT32_MAX)
+ return;
+
+ // Find "assert" Nav in debug.zig's namespace.
+ uint32_t debug_ns_idx = s_file_namespace[debug_file_idx];
+ uint32_t assert_nav = findNavInNamespace(debug_ns_idx, "assert");
+ if (assert_nav == UINT32_MAX)
+ return;
+
+ // Create func_type: fn(bool) void (default zig calling convention).
+ // debug.assert is: pub fn assert(ok: bool) void { ... }
+ InternPoolKey ftype_key;
+ memset(&ftype_key, 0, sizeof(ftype_key));
+ ftype_key.tag = IP_KEY_FUNC_TYPE;
+ ftype_key.data.func_type.return_type = IP_INDEX_VOID_TYPE;
+ ftype_key.data.func_type.param_count = 1;
+ ftype_key.data.func_type.cc = 0; // default/zig calling convention
+ InternPoolIndex ftype_ip = ipIntern(s_module_ip, ftype_key);
+
+ // Create func_decl for assert.
+ InternPoolKey fdk;
+ memset(&fdk, 0, sizeof(fdk));
+ fdk.tag = IP_KEY_FUNC;
+ fdk.data.func_decl.owner_nav = assert_nav;
+ fdk.data.func_decl.ty = ftype_ip;
+ InternPoolIndex fdecl_ip = ipIntern(s_module_ip, fdk);
+
+ // Create ptr_type (*const fn(bool) void).
+ InternPoolKey ptk;
+ memset(&ptk, 0, sizeof(ptk));
+ ptk.tag = IP_KEY_PTR_TYPE;
+ ptk.data.ptr_type.child = ftype_ip;
+ ptk.data.ptr_type.sentinel = IP_INDEX_NONE;
+ ptk.data.ptr_type.flags = PTR_FLAGS_SIZE_ONE | PTR_FLAGS_IS_CONST;
+ ptk.data.ptr_type.packed_offset = 0;
+ InternPoolIndex ptype_ip = ipIntern(s_module_ip, ptk);
+
+ // Create ptr_nav for assert.
+ Nav* nav = ipGetNav(assert_nav);
+ nav->resolved_type = ftype_ip;
+ (void)internNavPtr(ptype_ip, assert_nav);
+
+ // Create memoized_call (comptime assert call result = void).
+ InternPoolKey mck;
+ memset(&mck, 0, sizeof(mck));
+ mck.tag = IP_KEY_MEMOIZED_CALL;
+ mck.data.memoized_call.func = fdecl_ip;
+ mck.data.memoized_call.result = IP_INDEX_VOID_VALUE;
+ (void)ipIntern(s_module_ip, mck);
+}
+
+// --- resolveRootInStartModule ---
+// After loading the root module, create a ptr_nav entry for the "root"
+// Nav in start.zig's namespace. This matches the Zig compiler's
+// evaluation of start.zig's `_ = root;` comptime block.
+static void resolveRootInStartModule(
+ uint32_t start_file_idx, uint32_t root_file_idx) {
+ if (start_file_idx == UINT32_MAX || root_file_idx == UINT32_MAX)
+ return;
+
+ uint32_t start_ns_idx = s_file_namespace[start_file_idx];
+ uint32_t root_nav = findNavInNamespace(start_ns_idx, "root");
+ if (root_nav == UINT32_MAX)
+ return;
+
+ InternPoolIndex root_struct = s_file_root_type[root_file_idx];
+ if (root_struct == 0 && root_file_idx > 0)
+ return;
+
+ InternPoolIndex ptr_type = internPtrConst(IP_INDEX_TYPE_TYPE);
+ Nav* nav = ipGetNav(root_nav);
+ nav->resolved_type = root_struct;
+ (void)internNavPtr(ptr_type, root_nav);
}
// --- findDeclImportPathFromZir ---
@@ -6499,16 +6583,9 @@ static void zirStructDecl(Sema* sema, SemaBlock* block, uint32_t inst) {
}
}
- // === Pass 1.5: Pre-create exported function IP entries ===
- preCreateExportedFuncEntries(sema, decl_list, decls_len);
-
- // === Pass 1.75: Load builtin module chain ===
- // After exported function entries ($130-$134), the Zig compiler's
- // analyzeMemoizedState loads std/builtin.zig and its dependencies.
- // This creates type_struct and ptr_nav entries ($135+).
- resolveBuiltinModuleChain();
-
// === Pass 2: Process comptime and function bodies ===
+ // Note: preCreateExportedFuncEntries and resolveBuiltinModuleChain
+ // were moved to semaAnalyze to match the Zig compiler's entry order.
for (uint32_t d = 0; d < decls_len; d++) {
uint32_t decl_inst = decl_list[d];
uint32_t payload
@@ -9391,26 +9468,42 @@ SemaFuncAirList semaAnalyze(Sema* sema) {
// this order so that InternPool indices agree.
if (sema->code.inst_len > 0 && sema->source_dir) {
if (sema->module_root) {
- // Load std.zig first, matching the Zig compiler's order.
- // The Zig compiler creates:
+ // Match the Zig compiler's module loading order.
+ // std.zig's comptime blocks force loading start.zig and
+ // debug.zig, then debug.assert entries are created, then
+ // the root module is loaded.
+ //
+ // Zig compiler sequence:
// $124 = std.zig struct
- // $125 = start.zig struct (from std's imports)
+ // $125 = start.zig struct (std comptime: _ = start)
// $126 = ptr_type(*const type)
// $127 = ptr_nav (start in std)
- // $128 = debug.zig struct (from std's imports)
+ // $128 = debug.zig struct (std comptime: debug.assert)
// $129 = ptr_nav (debug in std)
- // ...
+ // $130 = func_type fn(bool) void (debug.assert type)
+ // $131 = func_decl (debug.assert)
+ // $132 = ptr_type (*const fn)
+ // $133 = ptr_nav (assert in debug)
+ // $134 = memoized_call (assert result = void)
// $135 = root module struct
+ // $136 = ptr_nav (root in start.zig)
+ // $137+ = std/builtin.zig chain
char std_path[1024];
snprintf(std_path, sizeof(std_path), "%s/lib/std/std.zig",
sema->module_root);
uint32_t std_file_idx = ensureFileAnalyzedC(std_path);
- // Load start.zig and debug.zig from std's namespace,
- // matching the Zig compiler's work queue processing order.
+ uint32_t start_file_idx = UINT32_MAX;
+ uint32_t debug_file_idx = UINT32_MAX;
if (std_file_idx != UINT32_MAX) {
- resolveNamedImport(std_file_idx, "start");
- resolveNamedImport(std_file_idx, "debug");
+ // Load start.zig and debug.zig from std's namespace,
+ // matching the Zig compiler's work queue processing.
+ start_file_idx = resolveNamedImport(std_file_idx, "start");
+ debug_file_idx = resolveNamedImport(std_file_idx, "debug");
+
+ // Create debug.assert entries ($130-$134), matching
+ // std.zig's comptime { debug.assert(...) } block.
+ resolveDebugAssertEntries(debug_file_idx);
}
// Register the root module at the next available file_idx.
@@ -9424,7 +9517,13 @@ SemaFuncAirList semaAnalyze(Sema* sema) {
root_mod->analyzed = true;
s_root_file_idx = root_file_idx;
(void)createFileRootStructC(root_file_idx, &sema->code);
- resolveModuleDeclImports(root_file_idx, 2);
+
+ // Create ptr_nav for "root" in start.zig ($136),
+ // matching start.zig's comptime { _ = root; }.
+ resolveRootInStartModule(start_file_idx, root_file_idx);
+
+ // Load std/builtin.zig chain ($137+).
+ resolveBuiltinModuleChain();
} else {
// No module root — root module is file_idx=0.
if (s_num_loaded_modules == 0) {