Files
zig/stage0/stages_test.zig
Motiejus Jakštys 1ae7c7687f sema_test: zero-copy PrecomputedFunc — point into binary data directly
PrecomputedFunc now stores raw [*]const u8 byte pointers instead of c.Air,
eliminating per-function heap allocations and memcpy in parsePrecomputedAir.
airCompareOne takes two PrecomputedFunc values; C-sema output is wrapped via
precomputedFromCAir.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 20:11:21 +00:00

99 lines
3.8 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 * 2);
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
{
// Symlink to the repo root inside a tmpDir so relative imports
// resolve within the module root, and paths stay under .zig-cache/tmp/
// to avoid 'std' module conflicts with lib/std/.
const this_dir = comptime std.fs.path.dirname(@src().file) orelse ".";
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const abs_repo_root = std.fs.cwd().realpathAlloc(gpa, comptime this_dir ++ "/..") catch return error.ResolvePath;
defer gpa.free(abs_repo_root);
tmp.dir.symLink(abs_repo_root, "root", .{ .is_directory = true }) catch return error.SymlinkCreate;
var tmp_abs_buf: [std.fs.max_path_bytes]u8 = undefined;
const tmp_abs = tmp.dir.realpathZ(".", &tmp_abs_buf) catch return error.ResolvePath;
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}/root/{s}", .{ tmp_abs, 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}/root", .{tmp_abs}) catch unreachable;
var c_ip = sc.ipInit();
defer sc.ipDeinit(&c_ip);
var c_sema: sc.Sema = undefined;
sc.semaInit(&c_sema, &c_ip, @bitCast(c_zir));
defer sc.semaDeinit(&c_sema);
c_sema.source_dir = source_dir_path.ptr;
c_sema.module_root = module_root_path.ptr;
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);
}
}