commit 62eda0a570d2631f2dc9c8c30f86b1e8abaa46c2 (tree)
parent 5b369949bd54fb664b3f6ac2f18673b94f2a26c1
Author: Motiejus <motiejus@jakstys.lt>
Date: Sat, 28 Feb 2026 15:58:33 +0000
sema: demand-driven module loading via analyzeComptimeUnit
Replace hardcoded resolveNamedImport("start"), resolveNamedImport("debug"),
and resolveDebugAssertEntries calls with honest comptime block evaluation.
Port analyzeComptimeUnit from PerThread.zig:853-926 — evaluates comptime
block value bodies in a module's context, creating IP entries as side
effects of evaluation.
Add ZIR_INST_IMPORT handler in analyzeBodyInner — loads modules via
ensureFileAnalyzedC and returns root struct type. Ported from
Sema.zig:13764 zirImport.
Add ZIR_EXT_THIS handler in zirExtended — returns the enclosing struct
type. Ported from Sema.zig:16800 zirThis.
std.zig's comptime blocks now drive module loading:
Block 1: `_ = start;` → loads start.zig via DECL_VAL import resolution
Block 2: `debug.assert(@import("std") == @This());` → loads debug.zig,
resolves assert, creates func entries + memoized_call
Remove resolveNamedImport and resolveDebugAssertEntries (now dead code).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat:
| M | stage0/sema.c | | | 201 | +++++++++++++++++++++++++++++++++++++++++++------------------------------------ |
1 file changed, 110 insertions(+), 91 deletions(-)
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -2676,89 +2676,9 @@ static const char* findDeclImportPathFromZir(
static bool resolveImportPath(const char* source_dir, const char* import_path,
char* out_full, size_t out_size);
-// --- resolveNamedImport ---
-// Find a named declaration in a namespace, load its import file,
-// and create ptr_type + ptr_nav entries matching the Zig compiler's
-// 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 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 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 UINT32_MAX;
-
- char import_full[1024];
- if (!resolveImportPath(s_loaded_modules[file_idx].source_dir, import_path,
- import_full, sizeof(import_full)))
- return UINT32_MAX;
-
- uint32_t import_file_idx = ensureFileAnalyzedC(import_full);
- if (import_file_idx == UINT32_MAX)
- return UINT32_MAX;
-
- InternPoolIndex struct_type = s_file_root_type[import_file_idx];
- if (struct_type == 0 && import_file_idx > 0)
- 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.
-// Forward declaration (defined later, used by resolveDebugAssertEntries).
+// Forward declaration (defined later).
static InternPoolIndex ensureNavValUpToDate(uint32_t nav_idx);
-// resolveDebugAssertEntries: resolve debug.assert declaration from ZIR.
-// Creates func_type, func_decl, ptr_type, ptr_nav, and memoized_call
-// entries matching the Zig compiler's sequence.
-// Ported from PerThread.zig ensureNavResolved for function declarations.
-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;
-
- // Resolve the function declaration from ZIR (creates func_type,
- // func_decl, ptr_type, ptr_nav entries).
- InternPoolIndex fd = ensureNavValUpToDate(assert_nav);
- if (fd == IP_INDEX_NONE)
- return;
-
- // Create memoized_call (comptime assert(true) result = void).
- // In the Zig compiler, start.zig's comptime block calls
- // debug.assert(builtin.is_test) which evaluates to assert(false)
- // at comptime and produces a void result.
- if (s_module_ip->items[fd].tag == IP_KEY_FUNC) {
- InternPoolKey mck;
- memset(&mck, 0, sizeof(mck));
- mck.tag = IP_KEY_MEMOIZED_CALL;
- mck.data.memoized_call.func = fd;
- 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
@@ -5321,6 +5241,51 @@ static void resolveModuleDeclImports(uint32_t file_idx, uint32_t depth) {
}
}
+// Forward declaration (defined later, used by analyzeComptimeUnit).
+static void populateDeclTableFromZir(Sema* sema, const Zir* zir);
+
+// --- analyzeComptimeUnit ---
+// Evaluate a comptime block's value body in a module's context.
+// Ported from src/Zcu/PerThread.zig analyzeComptimeUnit (lines 853-926).
+// Side effects (IP entries from @import, field access, etc.) are the
+// primary purpose — the comptime block itself produces void.
+static void analyzeComptimeUnit(uint32_t file_idx, uint32_t comptime_decl) {
+ if (!s_loaded_modules[file_idx].has_zir)
+ return;
+ const Zir* zir = &s_loaded_modules[file_idx].zir;
+
+ // Get the value body from the comptime declaration.
+ const uint32_t* body = NULL;
+ uint32_t body_len = 0;
+ getValueBodyFromZir(zir, comptime_decl, &body, &body_len);
+ if (!body || body_len == 0)
+ return;
+
+ // Create a sema context with the module's ZIR.
+ // Ported from analyzeComptimeUnit: sema is created with the
+ // module's ZIR, fn_ret_ty = void (comptime blocks are always void).
+ Sema ct_sema;
+ semaInit(&ct_sema, s_module_ip, *zir);
+ ct_sema.file_idx = file_idx;
+ ct_sema.source_dir = s_loaded_modules[file_idx].source_dir;
+ ct_sema.module_root = s_global_module_root;
+
+ // Populate the decl table so nested imports resolve correctly.
+ populateDeclTableFromZir(&ct_sema, zir);
+
+ SemaBlock ct_block;
+ semaBlockInit(&ct_block, &ct_sema, NULL);
+ ct_block.is_comptime = true;
+
+ // Evaluate the comptime body.
+ // Ported from analyzeComptimeUnit line 921:
+ // resolveInlineBody(&block, value_body, inst);
+ (void)analyzeBodyInner(&ct_sema, &ct_block, body, body_len);
+
+ semaBlockDeinit(&ct_block);
+ semaDeinit(&ct_sema);
+}
+
// --- ensureFileAnalyzedC ---
// Ported from src/Zcu/PerThread.zig ensureFileAnalyzed.
// Loads, parses, and analyzes an imported file. Creates struct type,
@@ -10048,6 +10013,16 @@ static AirInstRef zirExtended(Sema* sema, SemaBlock* block, uint32_t inst) {
if (opcode == ZIR_EXT_REIFY) {
return zirReifyComptime(sema, inst);
}
+ if (opcode == ZIR_EXT_THIS) {
+ // @This() returns the enclosing struct type (owner of the
+ // namespace this code is declared in).
+ // Ported from src/Sema.zig:16800 zirThis.
+ if (sema->file_idx != UINT32_MAX) {
+ uint32_t ns_idx = s_file_namespace[sema->file_idx];
+ return AIR_REF_FROM_IP(s_namespaces[ns_idx].owner_type);
+ }
+ return AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE);
+ }
if (opcode == ZIR_EXT_BRANCH_HINT) {
uint32_t payload_index = sema->code.inst_datas[inst].extended.operand;
ZirInstRef hint_ref = sema->code.extra[payload_index + 1];
@@ -11540,6 +11515,33 @@ static bool analyzeBodyInner(
continue;
}
+ // @import("path"): load and analyze an imported module.
+ // Returns the root struct type of the imported module.
+ // Ported from src/Sema.zig:13764 zirImport.
+ case ZIR_INST_IMPORT: {
+ uint32_t pl = sema->code.inst_datas[inst].pl_tok.payload_index;
+ uint32_t path_idx = sema->code.extra[pl + 1];
+ const char* import_path
+ = (const char*)&sema->code.string_bytes[path_idx];
+ InternPoolIndex result = IP_INDEX_VOID_VALUE;
+
+ if (strcmp(import_path, "builtin") == 0
+ && s_cg_builtin_ns_idx != UINT32_MAX) {
+ result = s_namespaces[s_cg_builtin_ns_idx].owner_type;
+ } else if (sema->source_dir) {
+ char import_full[1024];
+ if (resolveImportPath(sema->source_dir, import_path,
+ import_full, sizeof(import_full))) {
+ uint32_t fid = ensureFileAnalyzedC(import_full);
+ if (fid != UINT32_MAX)
+ result = s_file_root_type[fid];
+ }
+ }
+ instMapPut(&sema->inst_map, inst, AIR_REF_FROM_IP(result));
+ i++;
+ continue;
+ }
+
// call / field_call: function call.
// Handles inline function calls from the same module.
case ZIR_INST_CALL:
@@ -12136,7 +12138,9 @@ SemaFuncAirList semaAnalyze(Sema* sema) {
(void)ipNavCount();
// Set up the func_air_list for collecting per-function AIR.
- SemaFuncAirList result;
+ // Use static storage to satisfy cppcheck autoVariables
+ // (address of local auto-variable assigned to parameter).
+ static SemaFuncAirList result;
memset(&result, 0, sizeof(result));
sema->func_air_list = &result;
@@ -12189,15 +12193,30 @@ SemaFuncAirList semaAnalyze(Sema* sema) {
uint32_t start_file_idx = UINT32_MAX;
if (std_file_idx != UINT32_MAX) {
- // 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");
- uint32_t 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);
+ // Evaluate std.zig's comptime blocks. These trigger:
+ // Block 1: `_ = start;` → loads start.zig
+ // Block 2: `debug.assert(@import("std") == @This());`
+ // → loads debug.zig, creates assert entries
+ // Ported from upstream: comptime blocks are evaluated via
+ // analyzeComptimeUnit after scanning the namespace.
+ uint32_t std_ns_idx = s_file_namespace[std_file_idx];
+ const SemaNamespace* std_ns = &s_namespaces[std_ns_idx];
+ for (uint32_t ci = 0; ci < std_ns->comptime_decl_count; ci++) {
+ analyzeComptimeUnit(
+ std_file_idx, std_ns->comptime_decls[ci]);
+ }
+ // Find start_file_idx for root module resolution below.
+ uint32_t start_nav = findNavInNamespace(std_ns_idx, "start");
+ if (start_nav != UINT32_MAX) {
+ const Nav* sn = ipGetNav(start_nav);
+ // Find which file the start nav resolved to.
+ for (uint32_t fi = 0; fi < s_num_loaded_modules; fi++) {
+ if (s_file_root_type[fi] == sn->resolved_type) {
+ start_file_idx = fi;
+ break;
+ }
+ }
+ }
}
// Register the root module at the next available file_idx.