commit 4ebbc9047f60d940e31d53bb8800d60c9a92f364 (tree)
parent dd762f7bcff6e02254e6291317a16575a282b0e0
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Thu, 19 Feb 2026 06:19:39 +0000
stages_test: add Zig InternPool cross-check for pre-interned entries
Wire src/test_exports.zig through build.zig so zig0 tests can import
the real Zig InternPool. Add a test that initializes both the C and Zig
InternPools and compares all 124 pre-interned entries index by index.
Also add rule to skill file: never run `zig build test` or bare
`zig build` (they test upstream Zig and take ages).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
4 files changed, 141 insertions(+), 2 deletions(-)
diff --git a/build.zig b/build.zig
@@ -686,7 +686,7 @@ pub fn build(b: *std.Build) !void {
};
const test_zig0_step = b.step("test-zig0", "Run zig0 C implementation tests");
- addZig0TestStep(b, test_zig0_step, zig0_target, optimize, zig0_cc, zig0_no_exec, zig0_valgrind, zig0_test_timeout);
+ addZig0TestStep(b, test_zig0_step, zig0_target, optimize, zig0_cc, zig0_no_exec, zig0_valgrind, zig0_test_timeout, exe_options);
const fmt_zig0 = b.step("fmt-zig0", "Format zig0 C code");
const clang_format = b.addSystemCommand(&.{ "clang-format", "-i" });
@@ -749,7 +749,7 @@ pub fn build(b: *std.Build) !void {
all_zig0.dependOn(&zig0_fmt_check.step);
all_zig0.dependOn(lint_zig0);
for (zig0_compilers) |compiler| {
- addZig0TestStep(b, all_zig0, zig0_target, optimize, compiler, false, zig0_valgrind, zig0_test_timeout);
+ addZig0TestStep(b, all_zig0, zig0_target, optimize, compiler, false, zig0_valgrind, zig0_test_timeout, exe_options);
}
}
@@ -1615,6 +1615,7 @@ fn addZig0TestStep(
no_exec: bool,
valgrind: bool,
test_timeout: ?[]const u8,
+ exe_options: *std.Build.Step.Options,
) void {
const test_mod = b.createModule(.{
.root_source_file = b.path("stage0_test_root.zig"),
@@ -1625,6 +1626,22 @@ fn addZig0TestStep(
addZig0CSources(b, test_mod, cc, optimize);
test_mod.linkSystemLibrary("c", .{});
+ // Re-export module rooted in src/ (can resolve compiler-internal imports)
+ const zig_internals_mod = b.createModule(.{
+ .root_source_file = b.path("src/test_exports.zig"),
+ });
+ const aro_mod = b.createModule(.{
+ .root_source_file = b.path("lib/compiler/aro/aro.zig"),
+ });
+ const aro_translate_c_mod = b.createModule(.{
+ .root_source_file = b.path("lib/compiler/aro_translate_c.zig"),
+ });
+ aro_translate_c_mod.addImport("aro", aro_mod);
+ zig_internals_mod.addImport("aro", aro_mod);
+ zig_internals_mod.addImport("aro_translate_c", aro_translate_c_mod);
+ zig_internals_mod.addOptions("build_options", exe_options);
+ test_mod.addImport("zig_internals", zig_internals_mod);
+
const timeout: ?[]const u8 = test_timeout orelse if (valgrind) null else "300";
const test_exe = b.addTest(.{
diff --git a/src/test_exports.zig b/src/test_exports.zig
@@ -0,0 +1,3 @@
+pub const InternPool = @import("InternPool.zig");
+// Later: pub const Sema = @import("Sema.zig");
+// Later: pub const Air = @import("Air.zig");
diff --git a/stage0/.claude/skills/enable-tests/SKILL.md b/stage0/.claude/skills/enable-tests/SKILL.md
@@ -25,6 +25,9 @@ edit C source files. The worker handles all of that.
the worker's claim that tests pass — verify it.
3. **Batch passing tests.** When multiple consecutive tests pass without code
changes, commit them together in one commit.
+4. **NEVER run `zig build test` or `zig build` without arguments.** These
+ build and test upstream Zig, which takes ages and is irrelevant to zig0.
+ Always use `zig build test-zig0 -Dzig0-cc=tcc` for testing.
## File Configuration
diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig
@@ -8,6 +8,8 @@ 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 zig_internals = @import("zig_internals");
+const ZigIP = zig_internals.InternPool;
const Stage = enum { parser, sema };
@@ -1201,3 +1203,117 @@ const corpus_files = .{
//"../lib/compiler_rt/udivmodti4_test.zig", // 10249535
//"../src/arch/x86_64/CodeGen.zig", // 11086406
};
+
+// ---------------------------------------------------------------------------
+// InternPool cross-check: C vs Zig reference
+// ---------------------------------------------------------------------------
+
+test "sema: intern pool pre-interned comparison" {
+ const gpa = std.testing.allocator;
+
+ // Zig InternPool
+ var zig_ip: ZigIP = ZigIP.empty;
+ try zig_ip.init(gpa, 1);
+ defer zig_ip.deinit(gpa);
+
+ // C InternPool
+ var c_ip = sc.ipInit();
+ defer sc.ipDeinit(&c_ip);
+
+ // Both should have 124 pre-interned entries
+ try std.testing.expectEqual(@as(u32, 124), c_ip.items_len);
+
+ // Compare each entry's key
+ for (0..c_ip.items_len) |i| {
+ const c_key = sc.ipIndexToKey(&c_ip, @intCast(i));
+ const zig_key = zig_ip.indexToKey(@enumFromInt(i));
+ try expectKeysEqual(c_key, zig_key, @intCast(i));
+ }
+}
+
+fn expectKeysEqual(c_key: sc.InternPoolKey, zig_key: ZigIP.Key, index: u32) !void {
+ switch (c_key.tag) {
+ sc.IP_KEY_INT_TYPE => {
+ const zig_int = zig_key.int_type;
+ try std.testing.expectEqual(c_key.data.int_type.bits, zig_int.bits);
+ // C: 0=unsigned, 1=signed; Zig: Signedness enum { signed=0, unsigned=1 }
+ const c_is_signed = c_key.data.int_type.signedness != 0;
+ try std.testing.expectEqual(c_is_signed, zig_int.signedness == .signed);
+ },
+ sc.IP_KEY_SIMPLE_TYPE => {
+ // SimpleType enum values encode the Index directly in the Zig IP
+ const zig_st = zig_key.simple_type;
+ try std.testing.expectEqual(index, @intFromEnum(zig_st));
+ },
+ sc.IP_KEY_PTR_TYPE => {
+ const zig_ptr = zig_key.ptr_type;
+ const c_ptr = c_key.data.ptr_type;
+ try std.testing.expectEqual(c_ptr.child, @intFromEnum(zig_ptr.child));
+ try std.testing.expectEqual(c_ptr.sentinel, @intFromEnum(zig_ptr.sentinel));
+ // Compare size
+ const c_size: u2 = @intCast(c_ptr.flags & sc.PTR_FLAGS_SIZE_MASK);
+ try std.testing.expectEqual(c_size, @intFromEnum(zig_ptr.flags.size));
+ // Compare is_const
+ const c_is_const = (c_ptr.flags & sc.PTR_FLAGS_IS_CONST) != 0;
+ try std.testing.expectEqual(c_is_const, zig_ptr.flags.is_const);
+ },
+ sc.IP_KEY_VECTOR_TYPE => {
+ const zig_vec = zig_key.vector_type;
+ try std.testing.expectEqual(c_key.data.vector_type.len, zig_vec.len);
+ try std.testing.expectEqual(c_key.data.vector_type.child, @intFromEnum(zig_vec.child));
+ },
+ sc.IP_KEY_OPT_TYPE => {
+ try std.testing.expectEqual(c_key.data.opt_type, @intFromEnum(zig_key.opt_type));
+ },
+ sc.IP_KEY_ANYFRAME_TYPE => {
+ try std.testing.expectEqual(c_key.data.anyframe_type, @intFromEnum(zig_key.anyframe_type));
+ },
+ sc.IP_KEY_ERROR_UNION_TYPE => {
+ const zig_eu = zig_key.error_union_type;
+ try std.testing.expectEqual(c_key.data.error_union_type.error_set, @intFromEnum(zig_eu.error_set_type));
+ try std.testing.expectEqual(c_key.data.error_union_type.payload, @intFromEnum(zig_eu.payload_type));
+ },
+ sc.IP_KEY_TUPLE_TYPE => {
+ // For the pre-interned set this is only empty_tuple_type
+ const zig_tuple = zig_key.tuple_type;
+ try std.testing.expectEqual(@as(u32, 0), zig_tuple.types.len);
+ try std.testing.expectEqual(@as(u32, 0), zig_tuple.values.len);
+ },
+ sc.IP_KEY_UNDEF => {
+ try std.testing.expectEqual(c_key.data.undef, @intFromEnum(zig_key.undef));
+ },
+ sc.IP_KEY_SIMPLE_VALUE => {
+ const zig_sv = zig_key.simple_value;
+ // Map C SimpleValue enum to Zig SimpleValue enum by comparing the
+ // Index they encode (Zig SimpleValue values are the IP index itself).
+ try std.testing.expectEqual(index, @intFromEnum(zig_sv));
+ },
+ sc.IP_KEY_INT => {
+ const zig_int = zig_key.int;
+ // Compare type
+ try std.testing.expectEqual(c_key.data.int_val.ty, @intFromEnum(zig_int.ty));
+ // Compare value
+ switch (zig_int.storage) {
+ .u64 => |v| {
+ try std.testing.expect(!c_key.data.int_val.is_negative);
+ try std.testing.expectEqual(c_key.data.int_val.value, v);
+ },
+ .i64 => |v| {
+ if (v < 0) {
+ try std.testing.expect(c_key.data.int_val.is_negative);
+ const abs: u64 = @intCast(-v);
+ try std.testing.expectEqual(c_key.data.int_val.value, abs);
+ } else {
+ try std.testing.expect(!c_key.data.int_val.is_negative);
+ try std.testing.expectEqual(c_key.data.int_val.value, @as(u64, @intCast(v)));
+ }
+ },
+ else => return error.TestUnexpectedResult,
+ }
+ },
+ else => {
+ std.debug.print("unhandled key tag {d} at index {d}\n", .{ c_key.tag, index });
+ return error.TestUnexpectedResult;
+ },
+ }
+}