Files
zig/stage0/stages_test.zig
Motiejus 9cd2e0ff77 stage0: refactor globals into structs matching Zig architecture
Eliminates all 39 mutable static globals across sema.c (37) and
intern_pool.c (2). State is now held in properly-typed structs passed
as parameters, mirroring the Zig reference implementation layout.

New files matching Zig src/ layout:
- compilation.h: CompilationConfig + Compilation (matches Compilation.zig)
- zcu.h/zcu.c: ZcuFile, ZcuNamespace, Zcu, zcuInit/zcuDeinit (matches Zcu.zig)
- zcu_per_thread.h: forward declarations for PerThread-style functions

Key changes:
- InternPool gains navs[] (dynamically allocated) + nav_count/nav_cap;
  Nav functions now take InternPool* (was implicit via globals)
- Sema gains Zcu* zcu; semaInit now takes Zcu* instead of InternPool*
- All module-level state (files, namespaces, memoized state, config)
  moved from static globals into Zcu struct
- zig0.c creates Compilation + Zcu before semaInit
- Test files updated to use zcuInit/zcuDeinit API

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-02 16:44:27 +00:00

110 lines
4.4 KiB
Zig

const std = @import("std");
const Allocator = std.mem.Allocator;
const Ast = std.zig.Ast;
const AstGen = std.zig.AstGen;
const parser_test = @import("parser_test.zig");
const astgen_test = @import("astgen_test.zig");
const sema_test = @import("sema_test.zig");
const c = parser_test.c;
const sc = sema_test.c;
const corpus = @import("corpus.zig");
test "stages: corpus" {
@setEvalBranchQuota(corpus.files.len * 100);
const gpa = std.testing.allocator;
inline for (corpus.files[0..corpus.num_passing]) |path| {
stagesCheck(gpa, path, @embedFile("../" ++ path)) catch {
std.debug.print("FAIL: {s}\n", .{path});
return error.TestFailed;
};
}
}
fn stagesCheck(gpa: Allocator, comptime path: []const u8, source: [:0]const u8) !void {
// Parse once with C parser
var c_ast = c.astParse(source.ptr, @intCast(source.len));
defer c.astDeinit(&c_ast);
// Convert C AST to Zig AST once
var tree = try parser_test.zigAst(gpa, c_ast);
defer tree.deinit(gpa);
// Stage 1: Parser — compare C and Zig ASTs, check canonical rendering
if (!std.debug.inValgrind()) {
var zig_tree = try Ast.parse(gpa, source, .zig);
defer zig_tree.deinit(gpa);
try parser_test.expectAstConsistent(tree, zig_tree, source);
}
if (tree.errors.len == 0) {
const formatted = try tree.renderAlloc(gpa);
defer gpa.free(formatted);
try std.testing.expectEqualStrings(source, formatted);
}
// Stage 2: AstGen — compare C and Zig ZIR
var ref_zir = try AstGen.generate(gpa, tree);
defer ref_zir.deinit(gpa);
var c_zir = c.astGen(&c_ast);
defer c.zirDeinit(&c_zir);
if (c_zir.has_compile_errors) {
std.debug.print("C port returned compile errors (inst_len={d})\n", .{c_zir.inst_len});
return error.TestUnexpectedResult;
}
try astgen_test.expectEqualZir(gpa, ref_zir, c_zir);
// Stage 3: Sema — compare C sema vs pre-computed AIR
{
const this_dir = comptime std.fs.path.dirname(@src().file) orelse ".";
const abs_repo_root = std.fs.cwd().realpathAlloc(gpa, comptime this_dir ++ "/..") catch return error.ResolvePath;
defer gpa.free(abs_repo_root);
const repo_dir = comptime std.fs.path.dirname(path) orelse ".";
var source_dir_buf: [std.fs.max_path_bytes:0]u8 = undefined;
const source_dir_path = std.fmt.bufPrintZ(&source_dir_buf, "{s}/{s}", .{ abs_repo_root, repo_dir }) catch unreachable;
var module_root_buf: [std.fs.max_path_bytes:0]u8 = undefined;
const module_root_path = std.fmt.bufPrintZ(&module_root_buf, "{s}", .{abs_repo_root}) catch unreachable;
var comp: sc.Compilation = .{ .config = .{
.module_root = module_root_path.ptr,
.target_cpu_arch = "wasm32",
.target_cpu_model = "lime1",
.object_format = "wasm",
.link_mode = "static",
.is_test = false,
} };
const c_zcu = sc.zcuInit(&comp);
defer sc.zcuDeinit(c_zcu);
var c_sema: sc.Sema = undefined;
sc.semaInit(&c_sema, c_zcu, @bitCast(c_zir));
defer sc.semaDeinit(&c_sema);
// Pre-generated AIR uses ReleaseSmall (strip=true), so match it.
c_sema.strip = true;
// Set source_dir, root_fqn based on path prefix.
if (comptime std.mem.startsWith(u8, path, "stage0/")) {
// Sema unit tests: set module_root to enable std/start/builtin
// loading, matching the Zig compiler which always creates std
// even with std_mod=null (Compilation.zig line 2196).
c_sema.source_dir = source_dir_path.ptr;
c_sema.root_fqn = comptime sema_test.pathStem(path);
} else if (comptime std.mem.startsWith(u8, path, "lib/std/")) {
c_sema.source_dir = source_dir_path.ptr;
c_sema.root_fqn = "std";
c_sema.module_prefix = sema_test.pathToModulePrefix(path);
} else {
c_sema.source_dir = source_dir_path.ptr;
c_sema.root_fqn = comptime sema_test.pathStem(path);
}
var c_func_air_list = sc.semaAnalyze(&c_sema);
defer sc.semaFuncAirListDeinit(&c_func_air_list);
const air_data = @import("air_data").getData(path);
const precomputed = try sema_test.parsePrecomputedAir(air_data);
defer sema_test.freePrecomputedAir(precomputed);
try sema_test.airComparePrecomputed(precomputed, c_func_air_list);
}
}