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:
| M | stage0/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) {