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:
agra
2026-06-16 22:02:48 +03:00
parent 12e2ff7ef4
commit 7a9db03bcc
6 changed files with 104 additions and 54 deletions

View File

@@ -1,5 +1,5 @@
// Comptime type construction: mint a NEW nominal enum from a `TypeInfo` value
// via the `define(declare(), info)` primitives, then construct one of its
// via the `define(declare("E"), info)` primitives, then construct one of its
// variants and match on it — exercising that a programmatically-built enum
// (with NO backing AST decl) flows through enum codegen unmodified (layout /
// construct / match), byte-identical to a hand-written enum.
@@ -9,7 +9,7 @@
#import "modules/std.sx";
#import "modules/std/meta.sx";
E :: define(declare(), .enum(.{ name = "E", variants = .[
E :: define(declare("E"), .enum(.{ variants = .[
EnumVariant.{ name = "value", payload = i64 },
EnumVariant.{ name = "closed", payload = void },
] }));

View File

@@ -1,5 +1,5 @@
// Comptime type construction — identity: a type-fn that builds a type with
// `define(declare(), ...)` must memoize by the instantiation's mangled name, so
// `define(declare("Box"), ...)` must memoize by the instantiation's mangled name, so
// `Box(i64)` resolved at two INDEPENDENT sites (here: a return type and a
// parameter type) is ONE `TypeId`. A value built at one site is therefore
// assignable / matchable at the other — nominal identity. If the minted result
@@ -9,7 +9,7 @@
#import "modules/std/meta.sx";
Box :: ($T: Type) -> Type {
return define(declare(), .enum(.{ name = "Box", variants = .[
return define(declare("Box"), .enum(.{ variants = .[
EnumVariant.{ name = "some", payload = T },
EnumVariant.{ name = "none", payload = void },
] }));