comptime VM: Phase 3 — find_type + type_field_count reflection readers
First read-only compiler-API reflection readers, bound the same way as the intern/text_of seed (compiler_lib.bound_fns + Vm.callCompilerFn, native on flat memory, no marshaling). A type handle is a plain u32 TypeId (like StringId), so both stay clean scalar host-calls: find_type(name: StringId) -> TypeId (TypeTable.findByName; unresolved/0 if absent) type_field_count(t: TypeId) -> i64 (new TypeTable.memberCount; loud-bail, no silent 0) memberCount is the single source both the legacy handler and the VM read, so the two paths can't drift. find_type returns a non-optional TypeId using the unresolved(0) sentinel for not-found rather than ?Type — a Type value is .any-typed (which the flat-memory VM does not represent) and an optional can't cross the legacy<->VM eval boundary; unresolved is the project-blessed "no type" marker. Example 0628 chains intern -> find_type -> type_field_count (+ a not-found lookup), folded at #run, VM-HANDLED natively. VM unit test added. Parity 689/689 (gate OFF and -Dcomptime-flat).
This commit is contained in:
@@ -769,6 +769,70 @@ test "comptime_vm exec: compiler-fn intern/text_of round-trip (native, no legacy
|
||||
try std.testing.expectEqual(@as(i64, 5), toI64(try v.run(module.getFunction(main_id), &.{})));
|
||||
}
|
||||
|
||||
test "comptime_vm exec: compiler-fn find_type + type_field_count (native reflection)" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = Module.init(alloc);
|
||||
defer module.deinit();
|
||||
|
||||
// A struct `Point { x, y, z }` registered in the type table (the thing the
|
||||
// reflection readers look up by name and count the fields of).
|
||||
const point_name = module.types.internString("Point");
|
||||
const pfields = [_]types.TypeInfo.StructInfo.Field{
|
||||
.{ .name = module.types.internString("x"), .ty = .i64 },
|
||||
.{ .name = module.types.internString("y"), .ty = .i64 },
|
||||
.{ .name = module.types.internString("z"), .ty = .i64 },
|
||||
};
|
||||
_ = module.types.intern(.{ .@"struct" = .{ .name = point_name, .fields = &pfields } });
|
||||
|
||||
// extern find_type(name: u32) -> u32 [compiler] (FuncId 0, no body)
|
||||
const fp = [_]Function.Param{.{ .name = module.types.internString("name"), .ty = .u32 }};
|
||||
var ffb = Fb.init(alloc, &fp, .u32);
|
||||
ffb.func.is_extern = true;
|
||||
ffb.func.compiler_welded = true;
|
||||
ffb.func.name = module.types.internString("find_type");
|
||||
const find_id = module.addFunction(ffb.func);
|
||||
|
||||
// extern type_field_count(t: u32) -> i64 [compiler] (FuncId 1, no body)
|
||||
const cp = [_]Function.Param{.{ .name = module.types.internString("t"), .ty = .u32 }};
|
||||
var cfb = Fb.init(alloc, &cp, .i64);
|
||||
cfb.func.is_extern = true;
|
||||
cfb.func.compiler_welded = true;
|
||||
cfb.func.name = module.types.internString("type_field_count");
|
||||
const count_id = module.addFunction(cfb.func);
|
||||
|
||||
// main(): return type_field_count(find_type(intern_id_of("Point"))) → 3
|
||||
// ("Point" is already interned above; pass its StringId directly.)
|
||||
var fb = Fb.init(alloc, &.{}, .i64);
|
||||
const b0 = fb.block(&.{});
|
||||
const nm = fb.add(b0, inst(.{ .const_int = @intFromEnum(point_name) }, .u32));
|
||||
const nargs = [_]Ref{ref(nm)};
|
||||
const tid = fb.add(b0, inst(.{ .call = .{ .callee = find_id, .args = &nargs } }, .u32));
|
||||
const targs = [_]Ref{ref(tid)};
|
||||
const cnt = fb.add(b0, inst(.{ .call = .{ .callee = count_id, .args = &targs } }, .i64));
|
||||
_ = fb.add(b0, inst(.{ .ret = .{ .operand = ref(cnt) } }, .void));
|
||||
const main_id = module.addFunction(fb.func);
|
||||
|
||||
var v = vm.Vm.init(alloc);
|
||||
v.table = &module.types;
|
||||
v.module = &module;
|
||||
defer v.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 3), toI64(try v.run(module.getFunction(main_id), &.{})));
|
||||
|
||||
// A name with no matching type → the `unresolved` (0) sentinel.
|
||||
const missing = module.types.internString("Nope");
|
||||
var mfb = Fb.init(alloc, &.{}, .u32);
|
||||
const mb = mfb.block(&.{});
|
||||
const mnm = mfb.add(mb, inst(.{ .const_int = @intFromEnum(missing) }, .u32));
|
||||
const margs = [_]Ref{ref(mnm)};
|
||||
const mres = mfb.add(mb, inst(.{ .call = .{ .callee = find_id, .args = &margs } }, .u32));
|
||||
_ = mfb.add(mb, inst(.{ .ret = .{ .operand = ref(mres) } }, .void));
|
||||
const missing_main = module.addFunction(mfb.func);
|
||||
try std.testing.expectEqual(
|
||||
@as(i64, @intFromEnum(TypeId.unresolved)),
|
||||
toI64(try v.run(module.getFunction(missing_main), &.{})),
|
||||
);
|
||||
}
|
||||
|
||||
test "comptime_vm exec: func_ref + call_indirect dispatch" {
|
||||
const alloc = std.testing.allocator;
|
||||
var module = Module.init(alloc);
|
||||
|
||||
Reference in New Issue
Block a user