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:
207
stage0/sema_test.zig
Normal file
207
stage0/sema_test.zig
Normal 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);
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user