P5.7 Step C: delete interp.zig — the comptime VM is the sole evaluator

The legacy tagged-Value Interpreter is gone. Relocate the Value result-DTO
+ decodeVariantElements into a new comptime_value.zig (the VM<->host
materialization boundary); repoint comptime_vm/emit_llvm/ir-barrel Value to
it and BuildConfig to compiler_hooks; delete the dead valueToReg bridge;
slim compiler_lib.zig to just the name registry (BoundFn{sx_name} + bound_fns
+ findFn — weldedCompilerFn only validates names); simplify printInterpBailDiag
to comptime_vm.last_bail_reason; drop the unused interp_mod import in lower.zig.
rm src/ir/interp.zig + interp.test.zig.

Value is relocated (not eliminated): it survives only as the slim result DTO
at the VM->valueToLLVMConst boundary; the execution-time marshaling the VM
pivot targeted is gone. Drop dead Value.asString/reflectTypeId.

706/0 corpus + 476/476 unit.
This commit is contained in:
agra
2026-06-19 20:05:57 +03:00
parent 103a156b26
commit 7b8be86834
11 changed files with 217 additions and 3721 deletions

View File

@@ -30,10 +30,11 @@ const std = @import("std");
const inst_mod = @import("inst.zig");
const types = @import("types.zig");
const mod_mod = @import("module.zig");
const interp_mod = @import("interp.zig");
const comptime_value = @import("comptime_value.zig");
const compiler_hooks = @import("compiler_hooks.zig");
const host_ffi = @import("host_ffi.zig");
const errors_mod = @import("../errors.zig");
const Value = interp_mod.Value;
const Value = comptime_value.Value;
const Inst = inst_mod.Inst;
const Ref = inst_mod.Ref;
const BlockId = inst_mod.BlockId;
@@ -191,7 +192,7 @@ pub var last_bail_reason: ?[]const u8 = null;
/// hardened to return `error.OutOfBounds` (not a debug panic) on a null/out-of-
/// range/oversized access, so a malformed run bails to `null` (→ legacy fallback)
/// rather than crashing the compiler. On a bail, `last_bail_reason` names the cause.
pub fn tryEval(gpa: std.mem.Allocator, module: *const Module, func_id: inst_mod.FuncId, build_config: ?*interp_mod.BuildConfig, source_map: ?*const std.StringHashMap([:0]const u8)) ?Value {
pub fn tryEval(gpa: std.mem.Allocator, module: *const Module, func_id: inst_mod.FuncId, build_config: ?*compiler_hooks.BuildConfig, source_map: ?*const std.StringHashMap([:0]const u8)) ?Value {
last_bail_reason = null;
const func = module.getFunction(func_id);
if (func.is_extern or func.blocks.items.len == 0) {
@@ -229,7 +230,7 @@ pub fn tryEval(gpa: std.mem.Allocator, module: *const Module, func_id: inst_mod.
/// `cb: (opt: BuildOptions) -> bool`): when `pass_options` is set, the handle (a
/// null sentinel — the real state is the threaded `BuildConfig`) is passed after
/// the implicit ctx. Returns null on a bail (`last_bail_reason` names the cause).
pub fn runBuildCallback(gpa: std.mem.Allocator, module: *const Module, func_id: inst_mod.FuncId, build_config: ?*interp_mod.BuildConfig, source_map: ?*const std.StringHashMap([:0]const u8), pass_options: bool) ?Value {
pub fn runBuildCallback(gpa: std.mem.Allocator, module: *const Module, func_id: inst_mod.FuncId, build_config: ?*compiler_hooks.BuildConfig, source_map: ?*const std.StringHashMap([:0]const u8), pass_options: bool) ?Value {
last_bail_reason = null;
const func = module.getFunction(func_id);
if (func.is_extern or func.blocks.items.len == 0) {
@@ -354,7 +355,7 @@ pub const Vm = struct {
/// the `#run`/const-init eval sites so an `abi(.compiler)` `BuildOptions` function
/// (e.g. `set_post_link_callback`) records into it directly. Null at lowering-time
/// type-fn evals (no build config exists yet); such a function bails loudly.
build_config: ?*interp_mod.BuildConfig = null,
build_config: ?*compiler_hooks.BuildConfig = null,
/// File → source text (the diagnostics' `import_sources`), threaded from the host
/// so `trace_resolve` can turn a packed `(func_id, span.start)` comptime frame into
/// `file:line:col` + the source line. Null → line/col degrade to 1 / "".
@@ -2146,48 +2147,6 @@ pub const Vm = struct {
// owns comptime end-to-end. Covers scalars + strings + structs; other aggregate
// shapes bail loudly (added as wiring surfaces them).
/// Convert a legacy `Value` of type `ty` into a VM `Reg`, materializing
/// aggregates into comptime memory (returning their `Addr`).
pub fn valueToReg(self: *Vm, table: *const types.TypeTable, value: Value, ty: TypeId) Error!Reg {
switch (kindOf(table, ty)) {
.word => return switch (value) {
.int => |i| @bitCast(i),
.boolean => |b| @intFromBool(b),
.float => |f| @bitCast(f),
.null_val => null_addr,
.type_tag => |t| t.index(),
else => self.failMsg("value→reg: scalar value kind mismatch"),
},
.aggregate => {
if (ty == .string) {
const text = switch (value) {
.string => |s| s,
else => return self.failMsg("value→reg: expected a string literal value"),
};
const data = self.machine.allocBytes(text.len + 1, 1);
if (text.len > 0) @memcpy(try self.machine.bytes(data, text.len), text);
return self.makeSlice(table, data, text.len);
}
const info = table.get(ty);
if (info == .@"struct") {
const fvals = switch (value) {
.aggregate => |a| a,
else => return self.failMsg("value→reg: expected a struct aggregate"),
};
const addr = self.machine.allocBytes(table.typeSizeBytes(ty), table.typeAlignBytes(ty));
for (info.@"struct".fields, 0..) |f, i| {
if (i >= fvals.len) break;
const fr = try self.valueToReg(table, fvals[i], f.ty);
try self.writeField(table, addr + fieldOffset(table, ty, @intCast(i)), f.ty, fr);
}
return addr;
}
return self.failMsg("value→reg: aggregate shape not bridged yet (slice/array/optional/tuple/enum)");
},
.unsupported => return self.failMsg("value→reg: unsupported type"),
}
}
/// Convert a VM `Reg` (+ comptime memory) of type `ty` back into a legacy `Value`.
/// Strings/aggregates are deep-copied into `alloc` (they must outlive comptime memory).
pub fn regToValue(self: *Vm, alloc: std.mem.Allocator, table: *const types.TypeTable, reg: Reg, ty: TypeId) Error!Value {