zig

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

commit 056fbe1069c2cb8b811dccd89e6778d5fe2b2b65 (tree)
parent 216b706834ef0a27f43fbe1ea2057184a16e912a
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date:   Thu, 26 Feb 2026 21:19:54 +0000

sema: comptime DECL_REF/FIELD_PTR resolution and CG builtin nav lookup

Infrastructure for comptime evaluation of declaration references:

- DECL_REF/DECL_VAL: in comptime context, resolve import values
  (returns root struct type for imports, CG builtin type for
  @import("builtin")) and non-import declarations (returns
  already-resolved nav values). Non-comptime path loads import
  side effects only.

- zirFieldPtr: in comptime, when operand is a struct type (its
  type is `type`), look up field in type's namespace. Only returns
  already-resolved nav values to avoid creating IP entries out of
  order.

- findNavInNamespace: handle CG builtin module (no ZIR) by using
  IP string_bytes for nav name comparison instead of ZIR
  string_bytes.

- Use resolveImportPath helper in non-comptime DECL_REF path
  instead of duplicating path resolution logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Diffstat:
Mstage0/sema.c | 123++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 91 insertions(+), 32 deletions(-)

diff --git a/stage0/sema.c b/stage0/sema.c @@ -2435,19 +2435,26 @@ static InternPoolIndex internNavPtr( static uint32_t findNavInNamespace(uint32_t ns_idx, const char* name) { const SemaNamespace* ns = &s_namespaces[ns_idx]; - const Zir* zir = &s_loaded_modules[ns->file_idx].zir; - if (!s_loaded_modules[ns->file_idx].has_zir) + // Nav names come from ZIR string_bytes (normal modules) or IP + // string_bytes (compiler-generated builtin module without ZIR). + const uint8_t* string_bytes; + if (s_loaded_modules[ns->file_idx].has_zir) { + string_bytes = s_loaded_modules[ns->file_idx].zir.string_bytes; + } else if (s_module_ip) { + string_bytes = s_module_ip->string_bytes; + } else { return UINT32_MAX; + } for (uint32_t i = 0; i < ns->pub_nav_count; i++) { const Nav* nav = ipGetNav(ns->pub_navs[i]); - const char* nav_name = (const char*)&zir->string_bytes[nav->name]; + const char* nav_name = (const char*)&string_bytes[nav->name]; if (strcmp(nav_name, name) == 0) return ns->pub_navs[i]; } for (uint32_t i = 0; i < ns->priv_nav_count; i++) { const Nav* nav = ipGetNav(ns->priv_navs[i]); - const char* nav_name = (const char*)&zir->string_bytes[nav->name]; + const char* nav_name = (const char*)&string_bytes[nav->name]; if (strcmp(nav_name, name) == 0) return ns->priv_navs[i]; } @@ -8999,7 +9006,7 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) { uint32_t c_nav = findNavInNamespace(cc_ns, cc_name); if (c_nav != UINT32_MAX) { - ensureNavValUpToDate(c_nav); + (void)ensureNavValUpToDate(c_nav); } } } @@ -9858,6 +9865,28 @@ static AirInstRef zirFieldPtr(Sema* sema, SemaBlock* block, uint32_t inst) { AirInstRef obj = resolveInst(sema, obj_ref); + // Comptime: if the operand IS a struct type (its type is `type`), + // look up the field in the type's namespace. This handles patterns + // like @import("builtin").target where the import resolves to a + // struct type and we need namespace member access. + // Only returns already-resolved nav values — does NOT trigger + // analyzeNavValC which would create IP entries out of order. + if (AIR_REF_IS_IP(obj) && block->is_comptime) { + InternPoolIndex obj_ip = AIR_REF_TO_IP(obj); + InternPoolIndex obj_type = ipTypeOf(sema->ip, obj_ip); + if (obj_type == IP_INDEX_TYPE_TYPE) { + uint32_t ns = findNamespaceForType(obj_ip); + if (ns != UINT32_MAX) { + uint32_t nav = findNavInNamespace(ns, field_name); + if (nav != UINT32_MAX) { + const Nav* n = ipGetNav(nav); + if (n->resolved_type != IP_INDEX_NONE) + return AIR_REF_FROM_IP(n->resolved_type); + } + } + } + } + // Determine the struct type from the pointer operand's type. // The operand is a pointer-to-struct, so get the pointee type. TypeIndex ptr_ty = semaTypeOf(sema, obj); @@ -11532,43 +11561,73 @@ static bool analyzeBodyInner( continue; // decl_val / decl_ref: reference to a module-level declaration. - // Returns the Nav's resolved value when available, void otherwise. - // When the declaration is an @import, lazily create a struct - // type for the imported module and recursively process its - // imports (matching Zig's ensureFileAnalyzed / scanNamespace). + // In comptime context, resolves the declaration's value: + // for imports, returns the imported module's root struct type; + // for other declarations, calls ensureNavValUpToDate. + // In non-comptime context, loads imports (side effect) and + // returns void (the resolved value is not used). + // Ported from Sema.zig zirDeclRef / zirDeclVal. case ZIR_INST_DECL_VAL: case ZIR_INST_DECL_REF: { uint32_t decl_name_idx = sema->code.inst_datas[inst].str_tok.start; - if (sema->source_dir && decl_name_idx != 0) { + InternPoolIndex resolved = IP_INDEX_VOID_VALUE; + + if (decl_name_idx != 0 && sema->file_idx != UINT32_MAX + && block->is_comptime) { + // Comptime: resolve the declaration value. + const char* decl_name + = (const char*)&sema->code.string_bytes[decl_name_idx]; + const char* import_path + = findDeclImportPath(sema, decl_name_idx); + + if (import_path) { + if (strcmp(import_path, "builtin") == 0) { + // @import("builtin") — compiler-generated + // builtin module. Look up the nav in the + // current file's namespace. + uint32_t ns_idx = s_file_namespace[sema->file_idx]; + uint32_t nav = findNavInNamespace(ns_idx, decl_name); + if (nav != UINT32_MAX) { + const Nav* n = ipGetNav(nav); + if (n->resolved_type != IP_INDEX_NONE) + resolved = n->resolved_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) + resolved = s_file_root_type[fid]; + } + } + } else { + // Not an import — look up by name in the + // file's namespace and return already-resolved + // value. Don't call ensureNavValUpToDate to avoid + // creating IP entries out of order. + uint32_t ns_idx = s_file_namespace[sema->file_idx]; + uint32_t nav = findNavInNamespace(ns_idx, decl_name); + if (nav != UINT32_MAX) { + const Nav* n = ipGetNav(nav); + if (n->resolved_type != IP_INDEX_NONE) + resolved = n->resolved_type; + } + } + } else if (sema->source_dir && decl_name_idx != 0) { + // Non-comptime: just load import side effects. const char* import_path = findDeclImportPath(sema, decl_name_idx); if (import_path && strcmp(import_path, "builtin") != 0) { - // Resolve import path. char import_full[1024]; - const char* rel = import_path; - if (rel[0] == '.' && rel[1] == '/') - rel += 2; - snprintf(import_full, sizeof(import_full), "%s/%s", - sema->source_dir, rel); - { - FILE* check = fopen(import_full, "r"); - if (!check && sema->module_root - && import_path[0] != '.' - && import_path[0] != '/') { - snprintf(import_full, sizeof(import_full), - "%s/lib/%s/%s.zig", sema->module_root, - import_path, import_path); - } - if (check) - fclose(check); + if (resolveImportPath(sema->source_dir, import_path, + import_full, sizeof(import_full))) { + (void)ensureFileAnalyzedC(import_full); } - // Use ensureFileAnalyzedC for proper namespace - // scanning (creates type_struct + Navs). - (void)ensureFileAnalyzedC(import_full); } } - instMapPut( - &sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); + + instMapPut(&sema->inst_map, inst, AIR_REF_FROM_IP(resolved)); i++; continue; }