green(metatype): declare(name) + self-reference (recursive enums via *Name)
declare now takes the type's NAME — `declare(name) -> Type` — because the
compiler needs it at compile time to register the forward type, which is
what makes self-reference resolve. EnumInfo drops `name` (it lives on
declare now); define completes the handle's body in place (the slot is
already named).
Self-reference mechanism (evalComptimeType): before lowering a comptime
type expression, preregisterForwardTypes scans it (and a called ctor fn's
body) for `declare("Name")` calls and registers each as an empty forward
nominal type AND binds it as a type alias. The alias is essential: a
`Name :: ctor()` decl makes `Name` a const_decl author, so a `*Name`
self-reference resolves through the forward-ALIAS path
(type_aliases_by_source), which a bare findByName registration doesn't
satisfy. With both in place `*Name` resolves to the forward slot at lower
time; the interp's declare returns that same slot; define fills it.
List :: make_list();
make_list :: () -> Type {
h := declare("List");
return define(h, .enum(.{ variants = .[
EnumVariant.{ name = "cons", payload = *List },
EnumVariant.{ name = "nil", payload = void } ] }));
}
Verified: cons/nil construct + match (direct and through the pointer),
multi-node list traversal via a recursive `count(*List)`. meta.sx
RecvResult/TryResult + examples 0614/0615/0617 updated to declare(name);
full suite green (673).
This commit is contained in:
@@ -189,10 +189,6 @@ pub const Interpreter = struct {
|
||||
/// that may run `declare`/`define`. Null elsewhere (unit tests, emit-time
|
||||
/// `#run`) → those builtins bail loudly.
|
||||
mint: ?*types.TypeTable = null,
|
||||
/// Monotonic suffix for `declare()`'s anonymous slot names, so two
|
||||
/// undefined slots alive at once don't collide in `findByName` before
|
||||
/// `define` names them (or a type-fn renames them to the mangled name).
|
||||
declare_counter: u32 = 0,
|
||||
|
||||
// Heap: dynamically allocated memory blocks
|
||||
heap: std.ArrayList([]u8),
|
||||
@@ -1980,14 +1976,14 @@ pub const Interpreter = struct {
|
||||
.declare => {
|
||||
const tbl = self.mint orelse
|
||||
return bailDetail("comptime declare(): no type-mint target (declare/define are comptime-only — reached at runtime/emit?)");
|
||||
// Mint an EMPTY (undefined) tagged_union slot under a fresh
|
||||
// anonymous name. The binding site (`E :: …` / type-fn) renames
|
||||
// it to the real name afterwards. An empty `fields` is the
|
||||
// "declared, not yet defined" state — `define` fills it.
|
||||
var buf: [40]u8 = undefined;
|
||||
const nm = std.fmt.bufPrint(&buf, "__reified_{d}", .{self.declare_counter}) catch "__reified";
|
||||
self.declare_counter += 1;
|
||||
if (bi.args.len != 1) return bailDetail("comptime declare(name): needs the name argument");
|
||||
const nm = frame.getRef(bi.args[0]).asString(self) orelse
|
||||
return bailDetail("comptime declare(): name is not a string");
|
||||
const name_id = tbl.internString(nm);
|
||||
// Lowering already registered this named forward slot (so a
|
||||
// `*Name` self-reference in the body resolved); return THAT slot
|
||||
// so `define` completes the same one. Mint it if somehow absent.
|
||||
if (tbl.findByName(name_id)) |existing| return .{ .value = .{ .type_tag = existing } };
|
||||
const info: types.TypeInfo = .{ .tagged_union = .{
|
||||
.name = name_id,
|
||||
.fields = &.{},
|
||||
@@ -2027,11 +2023,10 @@ pub const Interpreter = struct {
|
||||
.aggregate => |f| f,
|
||||
else => return bailDetail("comptime define(): `.enum` payload is not an EnumInfo struct value"),
|
||||
};
|
||||
// EnumInfo = `{ name: string, variants: []EnumVariant }`. The name
|
||||
// travels with the shape — `define` names the slot from it.
|
||||
if (einfo_fields.len != 2) return bailDetail("comptime define(): EnumInfo must have `name` and `variants`");
|
||||
const name = einfo_fields[0].asString(self) orelse return bailDetail("comptime define(): EnumInfo `name` is not a string");
|
||||
const elems = decodeVariantElements(einfo_fields[1]) orelse
|
||||
// EnumInfo = `{ variants: []EnumVariant }`. The name was given to
|
||||
// `declare` (it's already the slot's name) — `define` only fills the body.
|
||||
if (einfo_fields.len != 1) return bailDetail("comptime define(): EnumInfo must have a `variants` field");
|
||||
const elems = decodeVariantElements(einfo_fields[0]) orelse
|
||||
return bailDetail("comptime define(): `variants` is not a slice/array of EnumVariant");
|
||||
if (elems.len == 0) return bailDetail("comptime define(): enum has no variants");
|
||||
|
||||
@@ -2047,22 +2042,21 @@ pub const Interpreter = struct {
|
||||
fields.append(self.alloc, .{ .name = tbl.internString(vname), .ty = payload_tid }) catch return error.CannotEvalComptime;
|
||||
}
|
||||
|
||||
// Complete the declared slot: NAME it from the EnumInfo (the name travels
|
||||
// with the shape) and fill the body. The name changes the intern key
|
||||
// (declare minted an anonymous `__reified_N`), so re-key via
|
||||
// `replaceKeyedInfo`. The nominal id is preserved.
|
||||
// Complete the declared slot IN PLACE: it already has its name + nominal
|
||||
// id (from `declare`); fill the body. Name/id unchanged → the intern key
|
||||
// is stable, so `updatePreservingKey`.
|
||||
const cur = tbl.get(handle);
|
||||
if (cur != .tagged_union) return bailDetail("comptime define(): handle is not a declare()'d enum slot");
|
||||
const full: types.TypeInfo = .{ .tagged_union = .{
|
||||
.name = tbl.internString(name),
|
||||
.name = cur.tagged_union.name,
|
||||
.fields = fields.items,
|
||||
.tag_type = .i64,
|
||||
.backing_type = null,
|
||||
.explicit_tag_values = null,
|
||||
.nominal_id = cur.tagged_union.nominal_id,
|
||||
} };
|
||||
tbl.replaceKeyedInfo(handle, full);
|
||||
// Return the handle so the one-shot form chains: `T :: define(declare(), info)`.
|
||||
tbl.updatePreservingKey(handle, full);
|
||||
// Return the handle so the one-shot form chains: `T :: define(declare("T"), info)`.
|
||||
return .{ .value = .{ .type_tag = handle } };
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user