green(reify): declare/define floor — reify is sx; E :: reify(...) comptime-evaluated

First slice of the re-architecture. The compiler gains two comptime
type-construction builtins — declare() (mint an empty/undefined nominal
slot) and define(handle, info) (decode a TypeInfo VALUE + complete the
slot) — executed by the interpreter against a new `mint` TypeTable handle
(setMintTable). reify becomes PLAIN sx in meta.sx:
  reify :: (info) -> Type { h := declare(); define(h, info); return h; }

`E :: f(...)` where f is a non-generic Type-returning fn (reify, and later
make_enum) is now comptime-evaluated via evalComptimeTypeNamed: wrap the
call in a throwaway comptime fn, run it through the interp with the mint
table enabled so declare/define mint the type, read back the type_tag, and
rename the anonymous slot to the binding name. The compiler has ZERO reify
knowledge at the decl site — the old `E :: reify` hook is deleted.

examples/0614 (inline reify) now runs on this floor. Full suite green (673).

INTERMEDIATE: reifyType + findReturnReifyCall still serve the type-fn path
(0615/0617) and will be deleted in the next slice (type-fn body
comptime-eval), after which the compiler has no reify code at all.
This commit is contained in:
agra
2026-06-16 20:39:02 +03:00
parent ae27cffe9d
commit 442a70b8c9
7 changed files with 241 additions and 14 deletions

View File

@@ -43,6 +43,14 @@ const isPackFn = Lowering.isPackFn;
/// either DCE away or stay hidden from the dynamic symbol table.
/// Anything starting with `Java_` is a JNI native method that Android's
/// runtime resolves by name mangling — same rule.
/// True when `fd` declares a `-> Type` return — the signal that a non-generic
/// call to it (`E :: f(...)`) should be comptime-evaluated to mint a type (the
/// REIFY floor). Matches a bare `Type` type-expr return only.
fn fnReturnsTypeValue(fd: *const ast.FnDecl) bool {
const rt = fd.return_type orelse return false;
return rt.data == .type_expr and std.mem.eql(u8, rt.data.type_expr.name, "Type");
}
fn isExportedEntryName(name: []const u8) bool {
return std.mem.eql(u8, name, "main") or
std.mem.eql(u8, name, "JNI_OnLoad") or
@@ -651,16 +659,20 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
.field_access => |fa| fa.field,
else => "",
};
// `E :: reify(...)` — mint a NEW nominal type from a
// `TypeInfo` literal and register `E` as an alias to it.
// `reifyType` builds the type (or diagnoses + returns null);
// either way `E` is bound (to the minted type, or poisoned
// to `.unresolved` so downstream `E.value` gets a clean
// follow-on rather than a silent default type).
if (std.mem.eql(u8, callee_name, "reify")) {
const tid = self.reifyType(cd.name, call_data) orelse TypeId.unresolved;
self.putTypeAlias(self.current_source_file, cd.name, tid);
continue;
// `E :: f(...)` where `f` is a NON-generic fn returning
// `Type` (e.g. the sx `reify` / `make_enum`): comptime-
// evaluate the call — `declare`/`define` reached inside it
// mint the type — and bind `E` as an alias to the result.
// The compiler has ZERO `reify` knowledge: any Type-returning
// value-fn flows here. Generic type-fns (`$T`) are minted by
// `instantiateTypeFunction` below. Poison on failure so
// `E.x` gets a clean follow-on, never a silent default.
if (self.program_index.fn_ast_map.get(callee_name)) |fd| {
if (fd.type_params.len == 0 and fnReturnsTypeValue(fd)) {
const tid = self.evalComptimeTypeNamed(cd.value, cd.name) orelse TypeId.unresolved;
self.putTypeAlias(self.current_source_file, cd.name, tid);
continue;
}
}
// A namespaced callee (`ns.Box(..)`) is an explicit qualified
// reach, exempt from the bare-head visibility gate (E4).