comptime VM: Phase 3 — type_kind + type_field_value readers (read side complete)

The last two read-only readers the metatype's type_info(T) needs, each backed by
a TypeTable query both the legacy handler and the VM call (no drift):

  type_kind(t: TypeId) -> i64            (kindCode; stable discriminant, total — never bails)
  type_field_value(t: TypeId, idx) -> i64 (memberValue; enum explicit value or ordinal)

kindCode codes (compiler-owned, stable): 0 other / 1 struct / 2 enum /
3 tagged_union / 4 tuple / 5 union / 6 array / 7 vector / 8 error_set.

With these, the READ side is complete: find_type + type_kind + type_field_count +
type_field_{name,type} + type_nominal_name + type_field_value cover everything
reflectTypeInfo reads — a comptime sx fn can fully reflect a struct/enum/tuple
into data with no #builtin.

Example 0630 reflects Color / WindowFlags(flags) / Point. VM unit test added.

Revised forward direction: the write side will be ONE register_type(info) fn that
branches on the kind in the compiler (subsuming define's per-kind dispatch), not a
per-kind register_struct.

Parity 691/691 (gate OFF and -Dcomptime-flat).
This commit is contained in:
agra
2026-06-18 09:47:23 +03:00
parent d23e208430
commit 27bc301651
10 changed files with 252 additions and 29 deletions

View File

@@ -907,6 +907,70 @@ test "comptime_vm exec: compiler-fn type_field_name/type/nominal_name (native re
try std.testing.expectEqual(@as(i64, @intFromEnum(point_name)), toI64(try v.run(module.getFunction(main2), &.{})));
}
test "comptime_vm exec: compiler-fn type_kind + type_field_value (native reflection)" {
const alloc = std.testing.allocator;
var module = Module.init(alloc);
defer module.deinit();
// A struct and an enum with explicit values.
const pfields = [_]types.TypeInfo.StructInfo.Field{
.{ .name = module.types.internString("x"), .ty = .i64 },
.{ .name = module.types.internString("y"), .ty = .i64 },
};
const point = module.types.intern(.{ .@"struct" = .{ .name = module.types.internString("Point"), .fields = &pfields } });
const variants = [_]types.StringId{ module.types.internString("ok"), module.types.internString("missing") };
const evals = [_]i64{ 200, 404 };
const status = module.types.intern(.{ .@"enum" = .{
.name = module.types.internString("Status"),
.variants = &variants,
.explicit_values = &evals,
} });
// extern type_kind(t: u32) -> i64 [compiler] (FuncId 0)
const kp = [_]Function.Param{param(.u32)};
var kb = Fb.init(alloc, &kp, .i64);
kb.func.is_extern = true;
kb.func.compiler_welded = true;
kb.func.name = module.types.internString("type_kind");
const kind_id = module.addFunction(kb.func);
// extern type_field_value(t: u32, idx: i64) -> i64 [compiler] (FuncId 1)
const vp = [_]Function.Param{ param(.u32), param(.i64) };
var vb = Fb.init(alloc, &vp, .i64);
vb.func.is_extern = true;
vb.func.compiler_welded = true;
vb.func.name = module.types.internString("type_field_value");
const val_id = module.addFunction(vb.func);
var v = vm.Vm.init(alloc);
v.table = &module.types;
v.module = &module;
defer v.deinit();
// type_kind(Point) → 1 (struct); type_kind(Status) → 2 (enum).
inline for (.{ .{ point, 1 }, .{ status, 2 } }) |case| {
var fb = Fb.init(alloc, &.{}, .i64);
const b0 = fb.block(&.{});
const t = fb.add(b0, inst(.{ .const_int = @intFromEnum(case[0]) }, .u32));
const kargs = [_]Ref{ref(t)};
const k = fb.add(b0, inst(.{ .call = .{ .callee = kind_id, .args = &kargs } }, .i64));
_ = fb.add(b0, inst(.{ .ret = .{ .operand = ref(k) } }, .void));
const mid = module.addFunction(fb.func);
try std.testing.expectEqual(@as(i64, case[1]), toI64(try v.run(module.getFunction(mid), &.{})));
}
// type_field_value(Status, 1) → 404 (explicit value).
var fb = Fb.init(alloc, &.{}, .i64);
const b0 = fb.block(&.{});
const t = fb.add(b0, inst(.{ .const_int = @intFromEnum(status) }, .u32));
const one = fb.add(b0, inst(.{ .const_int = 1 }, .i64));
const vargs = [_]Ref{ ref(t), ref(one) };
const val = fb.add(b0, inst(.{ .call = .{ .callee = val_id, .args = &vargs } }, .i64));
_ = fb.add(b0, inst(.{ .ret = .{ .operand = ref(val) } }, .void));
const mid = module.addFunction(fb.func);
try std.testing.expectEqual(@as(i64, 404), toI64(try v.run(module.getFunction(mid), &.{})));
}
test "comptime_vm exec: func_ref + call_indirect dispatch" {
const alloc = std.testing.allocator;
var module = Module.init(alloc);