From ed2d9b4d2575f4715c2e7a9d4e43f7658e8153e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Tue, 17 Feb 2026 20:04:31 +0000 Subject: [PATCH] stage0: add sema test framework skeleton (Phase C) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create sema_test.zig with: - InternPool unit tests: pre-interned types/values, ipTypeOf, ipIntern deduplication, new key interning, vector/pointer types - Sema smoke tests: empty source and "const x = 0;" through full C pipeline (parse → astgen → sema) without crashing Wire sema_test.zig into test_all.zig. Co-Authored-By: Claude Opus 4.6 (1M context) --- stage0/sema_test.zig | 207 +++++++++++++++++++++++++++++++++++++++++++ stage0/test_all.zig | 1 + 2 files changed, 208 insertions(+) create mode 100644 stage0/sema_test.zig diff --git a/stage0/sema_test.zig b/stage0/sema_test.zig new file mode 100644 index 0000000000..eac0d20bef --- /dev/null +++ b/stage0/sema_test.zig @@ -0,0 +1,207 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +const parser_test = @import("parser_test.zig"); + +// Import C types including sema.h (which transitively includes air.h, intern_pool.h, etc.) +// Also include astgen.h so we have astParse/astGen/astDeinit/zirDeinit in the same namespace. +pub const c = @cImport({ + @cInclude("astgen.h"); + @cInclude("sema.h"); +}); + +// Helper to convert C #define integer constants (c_int) to u32 for comparison +// with uint32_t fields (InternPoolIndex, etc.). +fn idx(val: c_int) u32 { + return @bitCast(val); +} + +// Helper to convert C enum values (c_uint) to the expected tag type for comparison. +fn tag(val: c_uint) c_uint { + return val; +} + +// --------------------------------------------------------------------------- +// InternPool unit tests +// --------------------------------------------------------------------------- + +test "intern_pool: init and pre-interned types" { + var ip = c.ipInit(); + defer c.ipDeinit(&ip); + + // Verify pre-interned count + try std.testing.expectEqual(@as(u32, 124), ip.items_len); + + // Verify some key type indices + const void_key = c.ipIndexToKey(&ip, idx(c.IP_INDEX_VOID_TYPE)); + try std.testing.expectEqual(tag(c.IP_KEY_SIMPLE_TYPE), void_key.tag); + try std.testing.expectEqual(tag(c.SIMPLE_TYPE_VOID), void_key.data.simple_type); + + const u32_key = c.ipIndexToKey(&ip, idx(c.IP_INDEX_U32_TYPE)); + try std.testing.expectEqual(tag(c.IP_KEY_INT_TYPE), u32_key.tag); + try std.testing.expectEqual(@as(u16, 32), u32_key.data.int_type.bits); + try std.testing.expectEqual(@as(u8, 0), u32_key.data.int_type.signedness); // unsigned + + const i32_key = c.ipIndexToKey(&ip, idx(c.IP_INDEX_I32_TYPE)); + try std.testing.expectEqual(tag(c.IP_KEY_INT_TYPE), i32_key.tag); + try std.testing.expectEqual(@as(u16, 32), i32_key.data.int_type.bits); + try std.testing.expectEqual(@as(u8, 1), i32_key.data.int_type.signedness); // signed + + const bool_key = c.ipIndexToKey(&ip, idx(c.IP_INDEX_BOOL_TYPE)); + try std.testing.expectEqual(tag(c.IP_KEY_SIMPLE_TYPE), bool_key.tag); + try std.testing.expectEqual(tag(c.SIMPLE_TYPE_BOOL), bool_key.data.simple_type); +} + +test "intern_pool: pre-interned values" { + var ip = c.ipInit(); + defer c.ipDeinit(&ip); + + // Check void value + const void_val = c.ipIndexToKey(&ip, idx(c.IP_INDEX_VOID_VALUE)); + try std.testing.expectEqual(tag(c.IP_KEY_SIMPLE_VALUE), void_val.tag); + try std.testing.expectEqual(tag(c.SIMPLE_VALUE_VOID), void_val.data.simple_value); + + // Check bool true/false + const true_val = c.ipIndexToKey(&ip, idx(c.IP_INDEX_BOOL_TRUE)); + try std.testing.expectEqual(tag(c.IP_KEY_SIMPLE_VALUE), true_val.tag); + try std.testing.expectEqual(tag(c.SIMPLE_VALUE_TRUE), true_val.data.simple_value); + + const false_val = c.ipIndexToKey(&ip, idx(c.IP_INDEX_BOOL_FALSE)); + try std.testing.expectEqual(tag(c.IP_KEY_SIMPLE_VALUE), false_val.tag); + try std.testing.expectEqual(tag(c.SIMPLE_VALUE_FALSE), false_val.data.simple_value); + + // Check zero + const zero_key = c.ipIndexToKey(&ip, idx(c.IP_INDEX_ZERO)); + try std.testing.expectEqual(tag(c.IP_KEY_INT), zero_key.tag); +} + +test "intern_pool: ipTypeOf" { + var ip = c.ipInit(); + defer c.ipDeinit(&ip); + + // Types have type 'type' + try std.testing.expectEqual(idx(c.IP_INDEX_TYPE_TYPE), c.ipTypeOf(&ip, idx(c.IP_INDEX_VOID_TYPE))); + try std.testing.expectEqual(idx(c.IP_INDEX_TYPE_TYPE), c.ipTypeOf(&ip, idx(c.IP_INDEX_U32_TYPE))); + try std.testing.expectEqual(idx(c.IP_INDEX_TYPE_TYPE), c.ipTypeOf(&ip, idx(c.IP_INDEX_BOOL_TYPE))); + + // Values have their respective types + try std.testing.expectEqual(idx(c.IP_INDEX_VOID_TYPE), c.ipTypeOf(&ip, idx(c.IP_INDEX_VOID_VALUE))); + try std.testing.expectEqual(idx(c.IP_INDEX_BOOL_TYPE), c.ipTypeOf(&ip, idx(c.IP_INDEX_BOOL_TRUE))); + try std.testing.expectEqual(idx(c.IP_INDEX_BOOL_TYPE), c.ipTypeOf(&ip, idx(c.IP_INDEX_BOOL_FALSE))); +} + +test "intern_pool: ipIntern deduplication" { + var ip = c.ipInit(); + defer c.ipDeinit(&ip); + + // Interning an existing key should return the same index + var void_key: c.InternPoolKey = undefined; + @memset(std.mem.asBytes(&void_key), 0); + void_key.tag = c.IP_KEY_SIMPLE_TYPE; + void_key.data.simple_type = c.SIMPLE_TYPE_VOID; + + const result = c.ipIntern(&ip, void_key); + try std.testing.expectEqual(idx(c.IP_INDEX_VOID_TYPE), result); + + // Items count shouldn't increase for duplicate + try std.testing.expectEqual(@as(u32, 124), ip.items_len); +} + +test "intern_pool: ipIntern new key" { + var ip = c.ipInit(); + defer c.ipDeinit(&ip); + + // Intern a new array type + var arr_key: c.InternPoolKey = undefined; + @memset(std.mem.asBytes(&arr_key), 0); + arr_key.tag = c.IP_KEY_ARRAY_TYPE; + arr_key.data.array_type = .{ + .len = 10, + .child = idx(c.IP_INDEX_U8_TYPE), + .sentinel = c.IP_INDEX_NONE, + }; + + const idx1 = c.ipIntern(&ip, arr_key); + try std.testing.expect(idx1 >= idx(c.IP_INDEX_PREINTERN_COUNT)); + try std.testing.expectEqual(@as(u32, 125), ip.items_len); + + // Re-interning should return same index + const idx2 = c.ipIntern(&ip, arr_key); + try std.testing.expectEqual(idx1, idx2); + try std.testing.expectEqual(@as(u32, 125), ip.items_len); +} + +test "intern_pool: vector types" { + var ip = c.ipInit(); + defer c.ipDeinit(&ip); + + // Verify vector_8_i8 at index 52 + const v8i8 = c.ipIndexToKey(&ip, idx(c.IP_INDEX_VECTOR_8_I8_TYPE)); + try std.testing.expectEqual(tag(c.IP_KEY_VECTOR_TYPE), v8i8.tag); + try std.testing.expectEqual(@as(u32, 8), v8i8.data.vector_type.len); + try std.testing.expectEqual(idx(c.IP_INDEX_I8_TYPE), v8i8.data.vector_type.child); + + // Verify vector_4_f32 at index 93 + const v4f32 = c.ipIndexToKey(&ip, idx(c.IP_INDEX_VECTOR_4_F32_TYPE)); + try std.testing.expectEqual(tag(c.IP_KEY_VECTOR_TYPE), v4f32.tag); + try std.testing.expectEqual(@as(u32, 4), v4f32.data.vector_type.len); + try std.testing.expectEqual(idx(c.IP_INDEX_F32_TYPE), v4f32.data.vector_type.child); +} + +test "intern_pool: pointer types" { + var ip = c.ipInit(); + defer c.ipDeinit(&ip); + + // ptr_usize (index 45): *usize + const ptr_usize = c.ipIndexToKey(&ip, idx(c.IP_INDEX_PTR_USIZE_TYPE)); + try std.testing.expectEqual(tag(c.IP_KEY_PTR_TYPE), ptr_usize.tag); + try std.testing.expectEqual(idx(c.IP_INDEX_USIZE_TYPE), ptr_usize.data.ptr_type.child); + + // manyptr_const_u8 (index 48): [*]const u8 + const manyptr = c.ipIndexToKey(&ip, idx(c.IP_INDEX_MANYPTR_CONST_U8_TYPE)); + try std.testing.expectEqual(tag(c.IP_KEY_PTR_TYPE), manyptr.tag); + try std.testing.expectEqual(idx(c.IP_INDEX_U8_TYPE), manyptr.data.ptr_type.child); + try std.testing.expect((manyptr.data.ptr_type.flags & idx(c.PTR_FLAGS_SIZE_MASK)) == idx(c.PTR_FLAGS_SIZE_MANY)); + try std.testing.expect((manyptr.data.ptr_type.flags & idx(c.PTR_FLAGS_IS_CONST)) != 0); +} + +// --------------------------------------------------------------------------- +// Sema smoke tests +// --------------------------------------------------------------------------- + +test "sema: empty source smoke test" { + const source: [:0]const u8 = ""; + + // Parse and generate ZIR via C + var c_ast = c.astParse(source.ptr, @intCast(source.len)); + defer c.astDeinit(&c_ast); + var c_zir = c.astGen(&c_ast); + defer c.zirDeinit(&c_zir); + + // Run C Sema + var ip = c.ipInit(); + defer c.ipDeinit(&ip); + var sema = c.semaInit(&ip, c_zir); + defer c.semaDeinit(&sema); + var air = c.semaAnalyze(&sema); + defer c.airDeinit(&air); + + // For now, just verify it doesn't crash. + // Once Sema handlers are implemented, we'll compare with Zig reference. +} + +test "sema: const x = 0 smoke test" { + const source: [:0]const u8 = "const x = 0;"; + + var c_ast = c.astParse(source.ptr, @intCast(source.len)); + defer c.astDeinit(&c_ast); + var c_zir = c.astGen(&c_ast); + defer c.zirDeinit(&c_zir); + + var ip = c.ipInit(); + defer c.ipDeinit(&ip); + var sema = c.semaInit(&ip, c_zir); + defer c.semaDeinit(&sema); + var air = c.semaAnalyze(&sema); + defer c.airDeinit(&air); +} diff --git a/stage0/test_all.zig b/stage0/test_all.zig index 08359c4b82..3efe46d971 100644 --- a/stage0/test_all.zig +++ b/stage0/test_all.zig @@ -2,5 +2,6 @@ test "zig0 test suite" { _ = @import("tokenizer_test.zig"); _ = @import("parser_test.zig"); _ = @import("astgen_test.zig"); + _ = @import("sema_test.zig"); _ = @import("stages_test.zig"); }