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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user