comptime VM: Phase 3 — field-level reflection readers
Three more read-only compiler-API readers on the TypeId-handle shape, each backed
by a new TypeTable query that both the legacy handler and the VM call (no drift):
type_nominal_name(t: TypeId) -> StringId (nominalName; loud-bail for unnamed types)
type_field_name(t: TypeId, idx: i64) -> StringId (memberName)
type_field_type(t: TypeId, idx: i64) -> TypeId (memberType)
All loud-bail on out-of-range idx / no-member — no silent default. First multi-arg
compiler fns (callCompilerFn now reads arg 1 = idx); added Vm.argHandle/argTypeId
range-checked arg readers and moved find_type/type_field_count onto them. Names use
the type_* family to avoid colliding with the std metatype builtins (field_name /
type_name in core.sx); the new TypeTable.nominalName is distinct from the existing
typeName(id) display-string renderer.
Example 0629 reflects Pair { lo: Point; hi: Point } — each field name + the nominal
name of a field's type, #run-folded, VM-HANDLED natively. VM unit test added.
Parity 690/690 (gate OFF and -Dcomptime-flat).
This commit is contained in:
@@ -988,6 +988,19 @@ pub const Vm = struct {
|
||||
/// legacy `compiler_lib` handlers, but reads/writes flat memory directly instead
|
||||
/// of marshaling `Value`s. The seed pair is the string-pool round-trip:
|
||||
/// `intern(s: string) -> StringId` and `text_of(id: StringId) -> string`.
|
||||
/// Read compiler-call arg `i` as a u32 handle (a `StringId` / `TypeId` word),
|
||||
/// range-checked — never a silent truncation.
|
||||
fn argHandle(self: *Vm, args: []const Ref, frame: *Frame, i: usize) Error!u32 {
|
||||
const raw = frame.get(args[i].index());
|
||||
if (raw > std.math.maxInt(u32)) return self.failMsg("comptime compiler call: handle arg out of u32 range");
|
||||
return @intCast(raw);
|
||||
}
|
||||
|
||||
/// Read compiler-call arg `i` as a `TypeId` handle.
|
||||
fn argTypeId(self: *Vm, args: []const Ref, frame: *Frame, i: usize) Error!TypeId {
|
||||
return @enumFromInt(try self.argHandle(args, frame, i));
|
||||
}
|
||||
|
||||
fn callCompilerFn(self: *Vm, name: []const u8, args: []const Ref, frame: *Frame) Error!?Reg {
|
||||
const table = try self.requireTable();
|
||||
if (std.mem.eql(u8, name, "intern")) {
|
||||
@@ -1016,9 +1029,7 @@ pub const Vm = struct {
|
||||
// these mirror intern/text_of's shape: word in, word out, no marshaling.
|
||||
if (std.mem.eql(u8, name, "find_type")) {
|
||||
if (args.len != 1) return self.failMsg("comptime find_type: expected one StringId arg");
|
||||
const raw = frame.get(args[0].index());
|
||||
if (raw > std.math.maxInt(u32)) return self.failMsg("comptime find_type: StringId out of range");
|
||||
const sid: types.StringId = @enumFromInt(@as(u32, @intCast(raw)));
|
||||
const sid: types.StringId = @enumFromInt(try self.argHandle(args, frame, 0));
|
||||
// Not found → the dedicated `unresolved` (0) sentinel, never a real
|
||||
// type id (mirrors `compiler_lib.handleFindType`).
|
||||
const tid = table.findByName(sid) orelse TypeId.unresolved;
|
||||
@@ -1026,15 +1037,36 @@ pub const Vm = struct {
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type_field_count")) {
|
||||
if (args.len != 1) return self.failMsg("comptime type_field_count: expected one TypeId arg");
|
||||
const raw = frame.get(args[0].index());
|
||||
if (raw > std.math.maxInt(u32)) return self.failMsg("comptime type_field_count: TypeId out of range");
|
||||
const tid: TypeId = @enumFromInt(@as(u32, @intCast(raw)));
|
||||
const tid = try self.argTypeId(args, frame, 0);
|
||||
// Same `TypeTable.memberCount` the legacy handler reads → no drift; a
|
||||
// type with no member count bails loudly (no silent 0).
|
||||
const count = table.memberCount(tid) orelse
|
||||
return self.failMsg("comptime type_field_count: type has no field/variant count");
|
||||
return @as(Reg, @bitCast(count));
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type_nominal_name")) {
|
||||
if (args.len != 1) return self.failMsg("comptime type_nominal_name: expected one TypeId arg");
|
||||
const tid = try self.argTypeId(args, frame, 0);
|
||||
const sid = table.nominalName(tid) orelse
|
||||
return self.failMsg("comptime type_nominal_name: type has no nominal name");
|
||||
return @as(Reg, @intFromEnum(sid));
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type_field_name")) {
|
||||
if (args.len != 2) return self.failMsg("comptime type_field_name: expected (TypeId, idx)");
|
||||
const tid = try self.argTypeId(args, frame, 0);
|
||||
const idx: i64 = @bitCast(frame.get(args[1].index()));
|
||||
const sid = table.memberName(tid, idx) orelse
|
||||
return self.failMsg("comptime type_field_name: out-of-range idx or unnamed member");
|
||||
return @as(Reg, @intFromEnum(sid));
|
||||
}
|
||||
if (std.mem.eql(u8, name, "type_field_type")) {
|
||||
if (args.len != 2) return self.failMsg("comptime type_field_type: expected (TypeId, idx)");
|
||||
const tid = try self.argTypeId(args, frame, 0);
|
||||
const idx: i64 = @bitCast(frame.get(args[1].index()));
|
||||
const mty = table.memberType(tid, idx) orelse
|
||||
return self.failMsg("comptime type_field_type: out-of-range idx or member has no type");
|
||||
return @as(Reg, mty.index());
|
||||
}
|
||||
return null; // not a known compiler function → caller bails to legacy
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user