feat(metatype): comptime-eval generic type-fn body locals

A generic ($T) -> Type type-fn comptime-evaluated only its return
EXPRESSION, so a local declared before the return ('vs := …; return
make_enum(…, vs)') was unresolved. Now a body with a prelude (statements
before the return) has its full body evaluated: createComptimeFunction-
WithPrelude lowers the pre-return statements into the comptime function's
scope before the return expr, so the locals resolve.

- comptime.zig: createComptimeFunctionWithPrelude (prelude stmts +
  expr); evalComptimeTypeBody (extract prelude + return expr, scan the
  whole body for declare() forward types); runComptimeTypeFunc factored
  out of evalComptimeType (shared bail/declare-never-defined handling).
- generic.zig: route a type-fn body WITH a prelude through
  evalComptimeTypeBody; no-prelude bodies stay on evalComptimeType (zero
  change for RecvResult/TryResult etc.).

Non-generic builders (whole body already evaluated) and the List-growth
path are unaffected. Suite green (684).
This commit is contained in:
agra
2026-06-17 07:40:09 +03:00
parent 60293bf5dd
commit d87d86df8a
3 changed files with 66 additions and 3 deletions

View File

@@ -1759,7 +1759,21 @@ pub fn instantiateTypeFunction(self: *Lowering, alias_name: []const u8, template
// `resolveTypeWithBindings` can't evaluate a Type-returning call.
if (findReturnTypeExpr(fd.body)) |ret_node| {
if (self.returnExprMintsType(ret_node)) {
const tid = self.evalComptimeType(ret_node) orelse return .unresolved;
// A body with LOCALS before its `return` (e.g. `vs := …; return
// make_enum(…, vs)`) needs its full body comptime-evaluated so those
// locals resolve; the bare return-expr path leaves them unresolved.
// A no-prelude body stays on the simpler `evalComptimeType` path.
const has_prelude = fd.body.data == .block and blk: {
for (fd.body.data.block.stmts) |stmt| {
if (stmt.data == .return_stmt) break :blk false;
break :blk true; // a non-return statement precedes the return
}
break :blk false;
};
const tid = (if (has_prelude)
self.evalComptimeTypeBody(fd.body, ret_node)
else
self.evalComptimeType(ret_node)) orelse return .unresolved;
// Re-key to the instantiation's mangled (or alias) name so the
// cache check at the top dedups a second instantiation — Contract 1.
self.renameNominalType(tid, if (has_alias) alias_name else mangled_name);