stage0: fix import resolution and increase module limits

- Fix resolveImportPath to handle .zig-extension imports (e.g.
  "BitStack.zig") as relative paths instead of module names
- Increase SEMA_NS_MAX_NAVS 256→1024, MAX_LOADED_MODULES 256→512,
  MAX_NAMESPACES 128→512 to handle std library modules
- Reduce resolveModuleDeclImports recursion depth 3→2 to avoid
  exceeding Nav limits while still covering neghf2→common→std chain
- Add source_dir/module_root support to zig0.c for standalone IP dumps
- Document anti-pattern: analysis paralysis when facing large IP gaps

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-25 23:03:47 +00:00
parent 659c586249
commit 9ae9aff807
4 changed files with 70 additions and 7 deletions

View File

@@ -121,6 +121,25 @@ entries so that IP indices in the function body AIR match.
verify progress after each batch, not to plan the entire
implementation upfront.
### Anti-pattern: analysis paralysis / proving work unnecessary
When the gap is large (hundreds of entries), the temptation is to spend rounds
analyzing whether the entries are "really needed" or whether a shortcut exists.
**This is always wrong.** The entries are needed — the test comparison is
byte-for-byte with no IP base adjustment.
Signs you are bailing out:
- Asking "does the AIR actually reference non-pre-interned IP indices?"
- Exploring "what if we DON'T create module-level entries?"
- Running verbose-air dumps to prove the function body is simple
- Suggesting the IP count "might not matter"
The correct response to a large gap:
1. Fix the immediate crash/assertion (e.g. increase buffer sizes)
2. Port the next upstream function that creates entries
3. Test, measure gap reduction, commit
4. Repeat
## AIR comparison exceptions
C and Zig AIR must match byte-by-byte except:

View File

@@ -39,7 +39,7 @@ static uint32_t s_exported_decl_names[MAX_EXPORTED_DECL_NAMES];
// Zig compiler's module-level analysis. Each imported file module gets
// a type_struct IP entry, created lazily when the module is first
// accessed during semantic analysis.
#define MAX_LOADED_MODULES 256
#define MAX_LOADED_MODULES 512
typedef struct {
char path[1024]; // canonical file path
char source_dir[1024]; // directory containing this file
@@ -59,7 +59,7 @@ static InternPool* s_module_ip; // IP for struct type creation
// --- Namespace storage ---
// Ported from Zcu.Namespace.
#define MAX_NAMESPACES 128
#define MAX_NAMESPACES 512
static SemaNamespace s_namespaces[MAX_NAMESPACES];
static uint32_t s_num_namespaces;
@@ -2453,10 +2453,21 @@ static bool resolveImportPath(const char* source_dir, const char* import_path,
if (rel[0] == '.' && rel[1] == '/')
rel += 2;
// Relative or absolute paths: resolve from source_dir.
if (import_path[0] == '.' || import_path[0] == '/') {
snprintf(out_full, out_size, "%s/%s", source_dir, rel);
return true;
}
// Imports with a file extension (e.g. "BitStack.zig") are resolved
// relative to the current module's source directory.
const char* dot = strrchr(import_path, '.');
if (dot && strcmp(dot, ".zig") == 0) {
snprintf(out_full, out_size, "%s/%s", source_dir, import_path);
return true;
}
// Bare module names (e.g. "std") resolve via module_root.
if (s_global_module_root) {
snprintf(out_full, out_size, "%s/lib/%s/%s.zig", s_global_module_root,
import_path, import_path);
@@ -9025,7 +9036,7 @@ SemaFuncAirList semaAnalyze(Sema* sema) {
s_loaded_modules[0].has_zir = true;
s_loaded_modules[0].analyzed = true;
(void)createFileRootStructC(0, &sema->code);
resolveModuleDeclImports(0, 3);
resolveModuleDeclImports(0, 2);
}
// If we have ZIR instructions, attempt to analyze the main struct

View File

@@ -113,7 +113,7 @@ typedef struct {
// Groups declarations by owning type (file root struct, etc.).
// Ported from Zcu.Namespace.
#define SEMA_NS_MAX_NAVS 256
#define SEMA_NS_MAX_NAVS 1024
#define SEMA_NS_MAX_COMPTIME 64
typedef struct {

View File

@@ -15,8 +15,9 @@
// - code = 0: program successfully terminated.
// - code = 1: panicked, panic message in msg. Caller should free msg.
// - code = 2: interpreter error, error in msg. Caller should free msg.
static int zig0Run(const char* program, bool verbose_air,
bool verbose_intern_pool, char** msg) {
static int zig0Run(const char* program, const char* source_dir,
const char* module_root, bool verbose_air, bool verbose_intern_pool,
char** msg) {
uint32_t len = (uint32_t)strlen(program);
Ast ast = astParse(program, len);
if (ast.has_error) {
@@ -46,6 +47,8 @@ static int zig0Run(const char* program, bool verbose_air,
InternPool ip = ipInit();
Sema sema;
semaInit(&sema, &ip, zir);
sema.source_dir = source_dir;
sema.module_root = module_root;
SemaFuncAirList func_airs = semaAnalyze(&sema);
if (verbose_intern_pool)
verboseIpPrint(stderr, &ip);
@@ -98,7 +101,37 @@ int zig0RunFile(const char* fname, bool verbose_air, bool verbose_intern_pool,
fclose(f);
program[fsize] = 0;
int code = zig0Run(program, verbose_air, verbose_intern_pool, msg);
// Compute source_dir (dirname of fname) for import resolution.
char source_dir[1024] = { 0 };
{
const char* last_slash = strrchr(fname, '/');
if (last_slash) {
size_t len = (size_t)(last_slash - fname);
if (len >= sizeof(source_dir))
len = sizeof(source_dir) - 1;
memcpy(source_dir, fname, len);
source_dir[len] = '\0';
}
}
// Derive module_root: walk up from source_dir to find the repo root.
// For paths like .../lib/compiler_rt/neghf2.zig, module_root is the
// directory containing "lib/". Heuristic: strip /lib/... suffix.
char module_root[1024] = { 0 };
{
const char* lib_pos = strstr(source_dir, "/lib/");
if (lib_pos) {
size_t len = (size_t)(lib_pos - source_dir);
if (len >= sizeof(module_root))
len = sizeof(module_root) - 1;
memcpy(module_root, source_dir, len);
module_root[len] = '\0';
}
}
int code
= zig0Run(program, source_dir, module_root[0] ? module_root : NULL,
verbose_air, verbose_intern_pool, msg);
free(program);
return code;
}