feat(metatype): implement type_info($T) reflection (enum round-trip)
type_info reflects an enum / tagged-union INTO a TypeInfo value — the
inverse of define's decode — so define(declare(n), type_info(T)) mints
a byte-identical copy with NO literal variant list.
- inst.zig: new BuiltinId.type_info (comptime-only, like declare/define).
- lower/call.zig: replace the 'not yet implemented' bail. Resolve $T at
lower time, reject non-enum/non-tagged-union loudly with a good span,
emit callBuiltin(.type_info, [const_type], TypeInfo).
- interp.zig: reflectTypeInfo builds the exact nested-aggregate Value
defineEnum decodes — variant {name,payload}, slice {data,len}, EnumInfo
{variants}, TypeInfo {tag0, EnumInfo}. tagged_union reflects field.ty
(tagless already void); payloadless `enum` reflects void per variant.
- emit: unchanged — type_info is always comptime-evaluated, the existing
comptime-only else arm (shared with declare/define) never fires.
0619 turns green: a source enum (circle:f64 / rect:i64 / empty) reflected
and reconstructed, constructs and matches like the original.
This commit is contained in:
@@ -2001,9 +2001,59 @@ pub const Interpreter = struct {
|
||||
const info_val = frame.getRef(bi.args[1]);
|
||||
return self.defineEnum(tbl, handle, info_val);
|
||||
},
|
||||
.type_info => {
|
||||
// Reflect a type INTO a `TypeInfo` value — the inverse of
|
||||
// `define`'s decode. Lowering already validated the arg is an
|
||||
// enum/tagged-union and passed it as a `const_type`.
|
||||
if (bi.args.len != 1) return bailDetail("comptime type_info: missing type argument");
|
||||
const tid = frame.getRef(bi.args[0]).asTypeId() orelse
|
||||
return bailDetail("comptime type_info: argument is not a Type value");
|
||||
return self.reflectTypeInfo(tid);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the `.enum(EnumInfo{ variants })` `TypeInfo` value for `tid` — the
|
||||
/// exact shape `defineEnum` decodes, so `define(declare(n), type_info(T))`
|
||||
/// round-trips. A `tagged_union` reflects each field as
|
||||
/// `EnumVariant{ name, payload = field.ty }` (tagless variants already carry
|
||||
/// `void`); a payloadless `@"enum"` reflects every variant with `void`.
|
||||
/// Value layout mirrors how the interp evaluates the hand-written literal:
|
||||
/// variant = { string(name), type_tag(payload) }
|
||||
/// variants = { aggregate(variant…), int(len) } (slice fat pointer)
|
||||
/// EnumInfo = { variants }
|
||||
/// TypeInfo = { int(0), EnumInfo } (`.enum` tag = 0)
|
||||
fn reflectTypeInfo(self: *Interpreter, tid: TypeId) InterpError!ExecResult {
|
||||
var elems = std.ArrayList(Value).empty;
|
||||
const info = self.module.types.get(tid);
|
||||
switch (info) {
|
||||
.tagged_union => |u| {
|
||||
for (u.fields) |f| {
|
||||
const nm = self.alloc.dupe(u8, self.module.types.getString(f.name)) catch return error.CannotEvalComptime;
|
||||
const pair = self.alloc.dupe(Value, &.{ .{ .string = nm }, .{ .type_tag = f.ty } }) catch return error.CannotEvalComptime;
|
||||
elems.append(self.alloc, .{ .aggregate = pair }) catch return error.CannotEvalComptime;
|
||||
}
|
||||
},
|
||||
.@"enum" => |e| {
|
||||
for (e.variants) |vname| {
|
||||
const nm = self.alloc.dupe(u8, self.module.types.getString(vname)) catch return error.CannotEvalComptime;
|
||||
const pair = self.alloc.dupe(Value, &.{ .{ .string = nm }, .{ .type_tag = .void } }) catch return error.CannotEvalComptime;
|
||||
elems.append(self.alloc, .{ .aggregate = pair }) catch return error.CannotEvalComptime;
|
||||
}
|
||||
},
|
||||
else => return bailDetail("comptime type_info: only enum/tagged-union types reflect today"),
|
||||
}
|
||||
if (elems.items.len == 0) return bailDetail("comptime type_info: type has no variants");
|
||||
|
||||
const variants_slice = self.alloc.dupe(Value, &.{
|
||||
.{ .aggregate = elems.items },
|
||||
.{ .int = @intCast(elems.items.len) },
|
||||
}) catch return error.CannotEvalComptime;
|
||||
const einfo = self.alloc.dupe(Value, &.{.{ .aggregate = variants_slice }}) catch return error.CannotEvalComptime;
|
||||
const typeinfo = self.alloc.dupe(Value, &.{ .{ .int = 0 }, .{ .aggregate = einfo } }) catch return error.CannotEvalComptime;
|
||||
return .{ .value = .{ .aggregate = typeinfo } };
|
||||
}
|
||||
|
||||
/// Complete a `declare()`d slot from a `TypeInfo` VALUE. The value is the
|
||||
/// `.enum(EnumInfo)` tagged-union (`{ tag, EnumInfo }`), EnumInfo is
|
||||
/// `{ variants }`, and each variant is `{ name: string, payload: Type }`.
|
||||
|
||||
Reference in New Issue
Block a user