comptime VM arc: abi(.compiler) ABI, out as sx fn, VM-native diagnostics, BuildConfig threaded

Lands the full VM/compiler-API arc on branch reify (701/0 both gates):
- abi(.compiler) ABI replaces abi(.zig) extern compiler + the fake
  #library "compiler"; bodiless decl = compiler-API surface, bodied =
  user compiler-domain fn (lowered for VM eval, emit-skipped).
- out is a plain sx fn (libc write) — the out builtin deleted; the VM
  handles it via host-FFI. trace_resolve + interp_print_frames ported.
- 4B VM-native diagnostics: 1179/1180 render proper comptime type
  construction failed: under strict.
- S5a: build_options/set_post_link_callback on abi(.compiler) with
  BuildConfig threaded into the VM (green intermediate).
- 0522 fixed (describe(args: []Type)); regression 0638.

Strict deletion-gate down to 4 compiler_call bails (1609/1614/1615/1616)
+ 1654 (legitimate unresolvable-symbol diagnostic).
This commit is contained in:
agra
2026-06-19 07:04:10 +03:00
parent fdc4ee2331
commit 2060373c16
80 changed files with 12684 additions and 11922 deletions

View File

@@ -376,6 +376,55 @@ test "comptime_vm exec: const_string length + str_eq/str_ne" {
try std.testing.expectEqual(@as(i64, 3), toI64(try v.run(&fb.func, &.{})));
}
test "comptime_vm exec: error_tag_name_get maps a tag id to its name string" {
const alloc = std.testing.allocator;
var table = types.TypeTable.init(alloc);
defer table.deinit();
_ = table.internTag("Foo");
const bad = table.internTag("Bad"); // the tag we'll resolve
// return error_tag_name(<bad tag id>) → the string "Bad"
var fb = Fb.init(alloc, &.{}, .string);
defer fb.deinit();
const b0 = fb.block(&.{});
const id = fb.add(b0, inst(.{ .const_int = @intCast(bad) }, .i64));
const name = fb.add(b0, inst(.{ .error_tag_name_get = .{ .operand = ref(id) } }, .string));
_ = fb.add(b0, inst(.{ .ret = .{ .operand = ref(name) } }, .void));
var v = vm.Vm.init(alloc);
v.table = &table;
defer v.deinit();
const word = try v.run(&fb.func, &.{});
const val = try v.regToValue(alloc, &table, word, .string);
defer alloc.free(val.string); // regToValue dupes the bytes
try std.testing.expectEqualStrings("Bad", val.string);
}
test "comptime_vm exec: type_is_unsigned(u32) - type_is_unsigned(i64) == 1" {
const alloc = std.testing.allocator;
var table = types.TypeTable.init(alloc);
defer table.deinit();
// r_u := type_is_unsigned(u32) → 1 ; r_s := type_is_unsigned(i64) → 0
// return r_u - r_s → 1 (only the correct unsigned/signed verdicts give 1)
var fb = Fb.init(alloc, &.{}, .i64);
defer fb.deinit();
const b0 = fb.block(&.{});
const ct_u = fb.add(b0, inst(.{ .const_type = .u32 }, .type_value));
const au = [_]Ref{ref(ct_u)};
const r_u = fb.add(b0, inst(.{ .call_builtin = .{ .builtin = .type_is_unsigned, .args = &au } }, .bool));
const ct_s = fb.add(b0, inst(.{ .const_type = .i64 }, .type_value));
const as = [_]Ref{ref(ct_s)};
const r_s = fb.add(b0, inst(.{ .call_builtin = .{ .builtin = .type_is_unsigned, .args = &as } }, .bool));
const diff = fb.add(b0, inst(.{ .sub = .{ .lhs = ref(r_u), .rhs = ref(r_s) } }, .i64));
_ = fb.add(b0, inst(.{ .ret = .{ .operand = ref(diff) } }, .void));
var v = vm.Vm.init(alloc);
v.table = &table;
defer v.deinit();
try std.testing.expectEqual(@as(i64, 1), toI64(try v.run(&fb.func, &.{})));
}
test "comptime_vm exec: array_to_slice + index through slice + slice length" {
const alloc = std.testing.allocator;
var table = types.TypeTable.init(alloc);
@@ -1262,7 +1311,7 @@ test "comptime_vm tryEval: pure function → Value; unsupported → null" {
_ = fb.add(b0, inst(.{ .ret = .{ .operand = ref(m) } }, .void));
const ok_id = module.addFunction(fb.func);
const v = vm.tryEval(alloc, &module, ok_id) orelse return error.VmShouldHaveHandledIt;
const v = vm.tryEval(alloc, &module, ok_id, null, null) orelse return error.VmShouldHaveHandledIt;
try std.testing.expectEqual(@as(i64, 42), v.int);
// fn bad() { compiler_call() } → an unported op → tryEval yields null (caller
@@ -1274,7 +1323,7 @@ test "comptime_vm tryEval: pure function → Value; unsupported → null" {
_ = fb2.add(c0, inst(.ret_void, .void));
const bad_id = module.addFunction(fb2.func);
try std.testing.expect(vm.tryEval(alloc, &module, bad_id) == null);
try std.testing.expect(vm.tryEval(alloc, &module, bad_id, null, null) == null);
}
test "comptime_vm exec: division by zero and unsupported op bail loudly" {
@@ -1432,7 +1481,7 @@ test "comptime_vm tryEval: deref of a null pointer bails (null, not a crash)" {
// The hardened accessors turn the null deref into error.OutOfBounds → run
// bails → tryEval returns null (legacy fallback), NOT a debug panic.
try std.testing.expect(vm.tryEval(alloc, &module, bad_id) == null);
try std.testing.expect(vm.tryEval(alloc, &module, bad_id, null, null) == null);
}
test "comptime_vm: arena allocations are aligned, non-null, and stable across grows" {