zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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:
Mstage0/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.