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).
35 lines
1.2 KiB
Plaintext
35 lines
1.2 KiB
Plaintext
// Comptime type construction — identity: a type-fn that builds a type with
|
|
// `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
|
|
// were not registered under the mangled instantiation name, the two sites would
|
|
// mint distinct types and `consume(build())` would be a type error.
|
|
#import "modules/std.sx";
|
|
#import "modules/std/meta.sx";
|
|
|
|
Box :: ($T: Type) -> Type {
|
|
return define(declare("Box"), .enum(.{ variants = .[
|
|
EnumVariant.{ name = "some", payload = T },
|
|
EnumVariant.{ name = "none", payload = void },
|
|
] }));
|
|
}
|
|
|
|
build :: () -> Box(i64) { // site A resolves Box(i64)
|
|
return Box(i64).some(7);
|
|
}
|
|
|
|
consume :: (b: Box(i64)) { // site B resolves Box(i64) independently
|
|
if b == {
|
|
case .some: (n) { print("some {}\n", n); }
|
|
case .none: { print("none\n"); }
|
|
}
|
|
}
|
|
|
|
main :: () -> i32 {
|
|
consume(build()); // typechecks ONLY if site A == site B
|
|
b : Box(i64) = .none;
|
|
consume(b);
|
|
return 0;
|
|
}
|