stage0: add sema test framework skeleton (Phase C)

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) <noreply@anthropic.com>
This commit is contained in:
2026-02-17 20:04:31 +00:00
parent 25db8d160e
commit ed2d9b4d25
2 changed files with 208 additions and 0 deletions

207
stage0/sema_test.zig Normal file
View File

@@ -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);
}

View File

@@ -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");
}