P5.7 Step C1: evaluate #insert on the comptime VM (sole evaluator)

evalComptimeString (the #insert lowering-time site) was the last user of
the legacy Interpreter.call. Route it through comptime_vm.tryEval instead:
the VM is hardened to bail (never panic) on malformed lowering-time IR
(0737's ret Ref.none), and regToValue dupes the result string into the
lowering allocator so it outlives the VM arena. Drop the now-unused
interp_mod / build_opts imports from comptime.zig.

500/500 unit + 706/0 corpus.
This commit is contained in:
agra
2026-06-19 17:25:12 +03:00
parent 64eb01918a
commit 4d9f73f506

View File

@@ -6,9 +6,7 @@ const types = @import("../types.zig");
const inst_mod = @import("../inst.zig");
const unescape = @import("../../unescape.zig");
const parser_mod = @import("../../parser.zig");
const interp_mod = @import("../interp.zig");
const comptime_vm = @import("../comptime_vm.zig");
const build_opts = @import("build_opts");
const program_index_mod = @import("../program_index.zig");
const resolver_mod = @import("../resolver.zig");
const ModuleConstInfo = program_index_mod.ModuleConstInfo;
@@ -588,34 +586,24 @@ pub fn evalComptimeString(self: *Lowering, expr: *const Node) ?[:0]const u8 {
return self.alloc.dupeZ(u8, str) catch null;
}
// Case 2: Evaluate via IR interpreter, reusing the parent module.
// The parent's `scanDecls` pass has already registered every
// type / protocol / impl / thunk the comptime call may need
// (Allocator, CAllocator, Context, the per-impl thunks). A
// fresh empty module would only lazy-lower function ASTs and
// would miss the type/protocol registrations, which would break
// `context.allocator.X` — the protocol dispatch chain needs
// those types to resolve struct field layout and the alloc/
// dealloc thunks at the bottom of the dispatch.
// Case 2: evaluate on the comptime VM (the SOLE evaluator — P5.7), reusing
// the parent module. The parent's `scanDecls` pass has already registered
// every type / protocol / impl / thunk the comptime call may need
// (Allocator, CAllocator, Context, the per-impl thunks); a fresh empty
// module would miss those and break `context.allocator.X`.
//
// Lowering-time IR can be malformed (e.g. a `ret Ref.none` left by an
// unresolved name — see `0737`); the VM is hardened to BAIL (never panic) on
// it, so `tryEval` yields null and we return null. The real user diagnostic
// (the visibility error, …) was already emitted while lowering the inserted
// expression. `regToValue` dupes the result string into `self.alloc`, so it
// outlives the VM's arena.
const ct_func_id = self.createComptimeFunction("__insert", expr, .string);
// NOTE: the comptime VM is intentionally NOT wired at this LOWERING-time
// site. Unlike the emit-time const-init / `#run` folds (which run on fully
// lowered IR), lowering-time IR can be malformed (e.g. a `ret Ref.none` left by
// an unresolved name — see `0737`), and routing that through the VM is out of
// scope until the VM is fully hardened against arbitrary malformed IR. The
// emit-time sites already give the VM full corpus coverage.
var interp = interp_mod.Interpreter.init(self.module, self.alloc);
defer interp.deinit();
if (self.diagnostics) |d| if (d.import_sources) |sm| interp.setSourceMap(sm);
const result = interp.call(ct_func_id, &.{}) catch return null;
const str = result.asString(&interp) orelse switch (result) {
const result = comptime_vm.tryEval(self.alloc, self.module, ct_func_id, null, null) orelse return null;
const str = switch (result) {
.string => |s| s,
else => return null,
};
return self.alloc.dupeZ(u8, str) catch null;
}