From 9f3f746c4be0fcb74a6f0668ced9fff65a5628de Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 17 Jun 2026 07:05:55 +0300 Subject: [PATCH] feat(metatype): widen type_info/define to tuple types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TypeInfo gains a `tuple(TupleInfo) variant (TupleInfo{elements: []Type}, positional/unnamed) — completing the reflect/construct triad with enum and struct. - meta.sx: TupleInfo + `tuple TypeInfo variant. - interp: reflectTypeInfo builds .tuple (tag 2) as bare type_tag elements (no name pairs); defineType dispatches tag 2 -> defineTuple, which decodes []Type and completes the declare slot as a structural .tuple via replaceKeyedInfo (kind change). Tuples are structural so the declared name is vestigial, but the slot is still completed in place so define returns the handle (consistent with enum/struct). - call.zig: the lower-time type_info guard now admits .tuple. define(declare("P"), .tuple(.{elements=.[i64,f64]})) builds a tuple, and define(declare("T"), type_info((i64,bool,f64))) round-trips one. Suite green (683). --- library/modules/std/meta.sx | 13 ++++++++--- src/ir/interp.zig | 44 +++++++++++++++++++++++++++++++++++-- src/ir/lower/call.zig | 4 ++-- 3 files changed, 54 insertions(+), 7 deletions(-) diff --git a/library/modules/std/meta.sx b/library/modules/std/meta.sx index d5d13b34..51572216 100644 --- a/library/modules/std/meta.sx +++ b/library/modules/std/meta.sx @@ -35,14 +35,21 @@ StructInfo :: struct { fields: []StructField; } +// The element types of a tuple being reflected or constructed. Tuples are +// POSITIONAL (no field names), so this is just an ordered list of types. +TupleInfo :: struct { + elements: []Type; +} + // The reflected/constructed type shape. A tagged union over the kinds of type -// that can be minted — `` .`enum `` and `` .`struct `` ship today (tuple later). +// that can be minted — `` .`enum ``, `` .`struct `` and `` .`tuple `` all ship. // The variants use the backtick raw-identifier escape so they read as the -// keywords `enum` / `struct` (`` .`enum(...) `` / `` .`struct(...) ``) rather than -// mangled `enum_` / `struct_`. +// keywords (`` .`enum(...) `` / `` .`struct(...) `` / `` .`tuple(...) ``) rather +// than mangled `enum_` / `struct_` / `tuple_`. TypeInfo :: enum { `enum: EnumInfo; `struct: StructInfo; + `tuple: TupleInfo; } // The compiler's ONLY type-construction primitives (comptime-only #builtins): diff --git a/src/ir/interp.zig b/src/ir/interp.zig index 9dcab03b..347a4285 100644 --- a/src/ir/interp.zig +++ b/src/ir/interp.zig @@ -2095,7 +2095,15 @@ pub const Interpreter = struct { } break :blk 1; }, - else => return bailDetail("comptime type_info: only enum / tagged-union / struct types reflect today"), + .tuple => |t| blk: { + // Tuple elements are POSITIONAL — bare `type_tag` values, not + // `{ name, type }` pairs (TupleInfo carries no field names). + for (t.fields) |elem_ty| { + elems.append(self.alloc, .{ .type_tag = elem_ty }) catch return error.CannotEvalComptime; + } + break :blk 2; + }, + else => return bailDetail("comptime type_info: only enum / tagged-union / struct / tuple types reflect today"), }; if (elems.items.len == 0) return bailDetail("comptime type_info: type has no members"); @@ -2124,7 +2132,8 @@ pub const Interpreter = struct { return switch (tag) { 0 => self.defineEnum(tbl, handle, info_val), 1 => self.defineStruct(tbl, handle, info_val), - else => bailDetail("comptime define(): unknown TypeInfo variant (only `.enum` / `.struct` are supported)"), + 2 => self.defineTuple(tbl, handle, info_val), + else => bailDetail("comptime define(): unknown TypeInfo variant (only `.enum` / `.struct` / `.tuple` are supported)"), }; } @@ -2249,6 +2258,37 @@ pub const Interpreter = struct { tbl.replaceKeyedInfo(handle, full); return .{ .value = .{ .type_tag = handle } }; } + + /// Complete a `declare()`d slot from a `.tuple(TupleInfo)` `TypeInfo` VALUE. + /// TupleInfo is `{ elements }`, each element a bare `Type` (positional, no + /// name). Tuples are structural, so the declared NAME is vestigial — we still + /// complete the slot in place (so `define` returns the handle, like the + /// enum/struct paths) via `replaceKeyedInfo` (kind change: tagged_union slot → + /// tuple, re-keyed structurally by element types). + fn defineTuple(self: *Interpreter, tbl: *types.TypeTable, handle: TypeId, info_val: Value) InterpError!ExecResult { + const ti_fields = info_val.aggregate; // defineType already checked the shape + const tinfo = ti_fields[1]; + const tinfo_fields = switch (tinfo) { + .aggregate => |f| f, + else => return bailDetail("comptime define(): `.tuple` payload is not a TupleInfo struct value"), + }; + if (tinfo_fields.len != 1) return bailDetail("comptime define(): TupleInfo must have an `elements` field"); + const elems = decodeVariantElements(tinfo_fields[0]) orelse + return bailDetail("comptime define(): `elements` is not a slice/array of Type"); + if (elems.len == 0) return bailDetail("comptime define(): tuple has no elements"); + + var field_tys = std.ArrayList(TypeId).empty; + for (elems) |elem| { + const ety = elem.asTypeId() orelse return bailDetail("comptime define(): tuple element is not a Type value"); + field_tys.append(self.alloc, ety) catch return error.CannotEvalComptime; + } + + const cur = tbl.get(handle); + if (cur != .tagged_union) return bailDetail("comptime define(): handle is not a declare()'d slot"); + const full: types.TypeInfo = .{ .tuple = .{ .fields = field_tys.items, .names = null } }; + tbl.replaceKeyedInfo(handle, full); + return .{ .value = .{ .type_tag = handle } }; + } }; /// Normalize an interpreter value into the list of EnumVariant element values. diff --git a/src/ir/lower/call.zig b/src/ir/lower/call.zig index 5f0c0e2b..5d926c98 100644 --- a/src/ir/lower/call.zig +++ b/src/ir/lower/call.zig @@ -1735,11 +1735,11 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C // inverse of `define`, which builds those kinds). A loud, well-spanned // reject here beats a deferred interp bail; tuple widening lands later. const reflectable = !t.isBuiltin() and switch (self.module.types.get(t)) { - .@"enum", .tagged_union, .@"struct" => true, + .@"enum", .tagged_union, .@"struct", .tuple => true, else => false, }; if (!reflectable) { - if (self.diagnostics) |d| d.addFmt(.err, c.args[0].span, "type_info: '{s}' is not reflectable — only enum / tagged-union / struct types reflect today", .{self.formatTypeName(t)}); + if (self.diagnostics) |d| d.addFmt(.err, c.args[0].span, "type_info: '{s}' is not reflectable — only enum / tagged-union / struct / tuple types reflect today", .{self.formatTypeName(t)}); return Ref.none; } const type_ref = self.builder.constType(t);