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:
@@ -47,6 +47,8 @@ pub const BoundFn = struct {
|
||||
pub const bound_fns = [_]BoundFn{
|
||||
.{ .sx_name = "intern", .handler = handleIntern },
|
||||
.{ .sx_name = "text_of", .handler = handleTextOf },
|
||||
.{ .sx_name = "find_type", .handler = handleFindType },
|
||||
.{ .sx_name = "type_field_count", .handler = handleTypeFieldCount },
|
||||
};
|
||||
|
||||
/// Look up a compiler function by its sx name. Returns null when the name is not
|
||||
@@ -82,3 +84,30 @@ fn handleTextOf(interp: *Interpreter, args: []const Value) InterpError!Value {
|
||||
const id: StringId = @enumFromInt(@as(u32, @intCast(args[0].int)));
|
||||
return Value{ .string = interp.module.types.getString(id) };
|
||||
}
|
||||
|
||||
/// `find_type(name: StringId) -> TypeId` — look up a named type (struct / enum /
|
||||
/// union / tagged-union / error-set) by its interned name and return its handle.
|
||||
/// A name with no matching type yields the dedicated `unresolved` sentinel (a
|
||||
/// `TypeId` of 0), the codebase-blessed "no type" marker — NOT an `?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). The caller checks the
|
||||
/// handle against 0 / `unresolved`. The VM mirrors this in `comptime_vm.callCompilerFn`.
|
||||
fn handleFindType(interp: *Interpreter, args: []const Value) InterpError!Value {
|
||||
if (args.len != 1 or args[0] != .int) return error.TypeError;
|
||||
if (args[0].int < 0 or args[0].int > std.math.maxInt(u32)) return error.TypeError;
|
||||
const name: StringId = @enumFromInt(@as(u32, @intCast(args[0].int)));
|
||||
const tid = interp.module.types.findByName(name) orelse types.TypeId.unresolved;
|
||||
return Value{ .int = tid.index() };
|
||||
}
|
||||
|
||||
/// `type_field_count(t: TypeId) -> i64` — the member count of an aggregate type
|
||||
/// (struct/union/tagged-union fields, enum variants, array/vector length), read
|
||||
/// through `TypeTable.memberCount`. A type with no member count (scalar, pointer,
|
||||
/// the `unresolved` sentinel, …) is a loud error — never a silent 0.
|
||||
fn handleTypeFieldCount(interp: *Interpreter, args: []const Value) InterpError!Value {
|
||||
if (args.len != 1 or args[0] != .int) return error.TypeError;
|
||||
if (args[0].int < 0 or args[0].int > std.math.maxInt(u32)) return error.TypeError;
|
||||
const tid: types.TypeId = @enumFromInt(@as(u32, @intCast(args[0].int)));
|
||||
const count = interp.module.types.memberCount(tid) orelse return error.TypeError;
|
||||
return Value{ .int = count };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user