zig

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

commit 7af3e9e6b79a95a95c282344be02e540fb571d07 (tree)
parent 6a1f0158b3f8df73c41978a0aa6f9d1e27d4e619
Author: Motiejus <motiejus@jakstys.lt>
Date:   Fri, 27 Feb 2026 14:25:24 +0000

sema: fix path normalization, directory-as-file, DECL_VAL ptr_nav

Three fixes to close the IP gap for neghf2.zig:

1. Path normalization: strip leading "./" in ensureFileAnalyzedC to
   avoid duplicate file entries (e.g. "./lib/std/std.zig" vs
   "lib/std/std.zig").

2. Directory-as-file: check .zig extension in loadImportZir to prevent
   fopen on directories (which succeeds on Linux, parsing garbage).

3. DECL_VAL ptr_nav: create ptr_nav as side effect in the DECL_VAL
   handler of resolveZirTypeInst, matching Zig's zirDeclVal →
   analyzeNavRef → analyzeNavRefInner chain. This ensures type + ptr_nav
   entries are created in the correct order during struct field type
   resolution.

Also simplify resolveTargetModuleChain: remove explicit SemanticVersion
loading since the cascade from resolveStructFullyC(os_nav) now correctly
creates entries via the DECL_VAL and field_val handlers.

First IP mismatch for neghf2.zig moved from [593] to [691].

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

Diffstat:
Mstage0/sema.c | 90+++++++++++++++++++++++++++++++++++++++----------------------------------------
1 file changed, 44 insertions(+), 46 deletions(-)

diff --git a/stage0/sema.c b/stage0/sema.c @@ -1971,6 +1971,13 @@ static Zir loadImportZir( if (rel[0] == '.' && rel[1] == '/') rel += 2; + // Bare module names (e.g. "builtin", "std") without a .zig extension + // are not direct file paths. On Linux, fopen on a directory succeeds, + // which would cause us to parse garbage. Skip non-.zig paths. + size_t rlen = strlen(rel); + if (rlen < 4 || strcmp(rel + rlen - 4, ".zig") != 0) + return empty_zir; + char full_path[1024]; int n = snprintf(full_path, sizeof(full_path), "%s/%s", source_dir, rel); if (n < 0 || (size_t)n >= sizeof(full_path)) @@ -3328,8 +3335,20 @@ static InternPoolIndex resolveZirTypeInst( } if (nav != UINT32_MAX) { InternPoolIndex result = ensureNavValUpToDate(nav); - if (result != IP_INDEX_NONE && result != IP_INDEX_VOID_TYPE) + if (result != IP_INDEX_NONE && result != IP_INDEX_VOID_TYPE) { + // Create ptr_nav as side effect, matching Zig's + // zirDeclVal → analyzeNavRef → analyzeNavRefInner. + if (result < s_module_ip->items_len) { + InternPoolKeyTag kt = s_module_ip->items[result].tag; + if (kt == IP_KEY_STRUCT_TYPE || kt == IP_KEY_ENUM_TYPE + || kt == IP_KEY_UNION_TYPE) { + InternPoolIndex dv_ptr_ty + = internPtrConst(IP_INDEX_TYPE_TYPE); + (void)internNavPtr(dv_ptr_ty, nav); + } + } return result; + } // Type alias (e.g. `const ErrorSet = ?[]const Error`): // resolve the nav's value body as a type expression. // Also reached when ensureNavValUpToDate returns VOID_TYPE, @@ -5002,7 +5021,6 @@ static InternPoolIndex ensureNavValUpToDate(uint32_t nav_idx) { if (!resolveImportPath(s_loaded_modules[file_idx].source_dir, import_path, import_full, sizeof(import_full))) break; - uint32_t import_file_idx = ensureFileAnalyzedC(import_full); if (import_file_idx == UINT32_MAX) break; @@ -5509,58 +5527,33 @@ static void resolveTargetModuleChain(void) { } // 2b. Target.Os — resolve field types, then inner types. + // + // resolveStructFullyC(os_nav) cascades into resolving VersionRange + // field types, which reference std.SemanticVersion.Range. Pre-set + // Target.zig's `std` import so the decl_val resolver can find it. + // The DECL_VAL and field_val handlers in resolveZirTypeInst create + // ptr_nav entries as side effects, matching Zig's analyzeNavRef. if (os_nav != UINT32_MAX) { + // Target.zig has `const std = @import("std")`. + // Pre-set resolved_type so field type resolution inside + // Target.zig can resolve std.* references without re-loading + // the already-loaded std.zig. + { + uint32_t std_import_nav = findNavInNamespace(target_ns, "std"); + if (std_import_nav != UINT32_MAX) { + Nav* si = ipGetNav(std_import_nav); + if (si->resolved_type == IP_INDEX_NONE) + si->resolved_type = s_file_root_type[s_std_file_idx]; + } + } + resolveStructFullyC(os_nav); const Nav* on = ipGetNav(os_nav); uint32_t os_ns = findNamespaceForType(on->resolved_type); if (os_ns != UINT32_MAX) { (void)resolveNestedTypeDecl(os_ns, "Tag"); - - // Create VersionRange union type (shallow — no field type - // resolution). Field types are resolved after loading - // SemanticVersion, since VersionRange.semver references - // std.SemanticVersion.Range. uint32_t vr_nav = resolveNavType(os_ns, "VersionRange"); - // Target.zig has `const std = @import("std")`. - // Set resolved_type so downstream code (e.g. SemanticVersion - // field type resolution) can resolve std.* references. - // The ptr_nav for this import is created later by the - // cascade (not here — Zig compiler defers it). - { - uint32_t std_import_nav = findNavInNamespace(target_ns, "std"); - if (std_import_nav != UINT32_MAX) { - Nav* si = ipGetNav(std_import_nav); - if (si->resolved_type == IP_INDEX_NONE) - si->resolved_type = s_file_root_type[s_std_file_idx]; - } - } - - // Load std.SemanticVersion (needed by Os.VersionRange) - // Only set resolved_type — the ptr_nav is created later - // by demand-driven resolution (not here). - uint32_t sem_nav - = findNavInNamespace(std_ns_idx, "SemanticVersion"); - if (sem_nav != UINT32_MAX) { - const Nav* sn = ipGetNav(sem_nav); - const char* sem_import - = findDeclImportPathFromZir(std_zir, sn->zir_index); - if (sem_import) { - char sem_full[1024]; - if (resolveImportPath( - s_loaded_modules[s_std_file_idx].source_dir, - sem_import, sem_full, sizeof(sem_full))) { - uint32_t sem_file = ensureFileAnalyzedC(sem_full); - if (sem_file != UINT32_MAX) { - Nav* snav = ipGetNav(sem_nav); - snav->resolved_type = s_file_root_type[sem_file]; - } - } - } - } - - // Now fully resolve VersionRange field types (SemanticVersion - // is loaded, so std.SemanticVersion.Range can be resolved). if (vr_nav != UINT32_MAX) resolveUnionFullyC(vr_nav); @@ -6283,6 +6276,11 @@ static uint32_t ensureFileAnalyzedC(const char* full_path) { if (!s_module_ip) return UINT32_MAX; + // Normalize: strip leading "./" to avoid duplicate entries for the + // same file (e.g. "./lib/std/std.zig" vs "lib/std/std.zig"). + while (full_path[0] == '.' && full_path[1] == '/') + full_path += 2; + // Check if already tracked. for (uint32_t i = 0; i < s_num_loaded_modules; i++) { if (strcmp(s_loaded_modules[i].path, full_path) == 0) {