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:
@@ -1677,15 +1677,20 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
if (self.reflectionTypeArgGuard(name, c)) |sentinel| return sentinel;
|
||||
|
||||
if (std.mem.eql(u8, name, "declare")) {
|
||||
// Comptime type-construction primitive: mint an empty nominal slot.
|
||||
// Comptime-only — emitted as a builtin_call the interp executes against
|
||||
// its `mint` table; never reaches codegen (its sx callers are only ever
|
||||
// comptime-evaluated).
|
||||
if (c.args.len != 0) {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, c.callee.span, "declare() takes no arguments", .{});
|
||||
// Comptime type-construction primitive: mint an empty nominal slot NAMED
|
||||
// by its (compile-time string) argument. Comptime-only — emitted as a
|
||||
// builtin_call the interp executes against its `mint` table; never
|
||||
// reaches codegen (its sx callers are only ever comptime-evaluated).
|
||||
if (c.args.len != 1) {
|
||||
if (self.diagnostics) |d| d.addFmt(.err, c.callee.span, "declare(name) takes one string argument", .{});
|
||||
return Ref.none;
|
||||
}
|
||||
return self.builder.callBuiltin(.declare, &.{}, .any);
|
||||
// The named forward type is pre-registered by `evalComptimeType`'s
|
||||
// `preregisterForwardTypes` before this body lowers (so a `*Name`
|
||||
// self-reference resolves); the interp's `declare` returns that slot.
|
||||
const name_ref = self.lowerExpr(c.args[0]);
|
||||
const args_owned = self.alloc.dupe(Ref, &.{name_ref}) catch return Ref.none;
|
||||
return self.builder.callBuiltin(.declare, args_owned, .any);
|
||||
}
|
||||
if (std.mem.eql(u8, name, "define")) {
|
||||
// Comptime type-construction primitive: complete a declare()'d slot
|
||||
|
||||
Reference in New Issue
Block a user