comptime VM: box_any/unbox_any + .any as a 16-byte flat-memory aggregate (Phase 4A.1)
Ported the Any-boxing conversion pair:
- box_any: alloc the 16-byte { type_tag@0, value@8 } box, tag = source TypeId
index (matches the legacy comptime interp; runtime anyTag also normalizes
arbitrary-width ints). Value slot holds a word source's scalar bytes (via
writeField(source_type) so f32 round-trips) or an aggregate source's
flat-memory ADDR (the runtime pointer-in-value-slot shape).
- unbox_any: read the value slot back (word -> readField; aggregate -> the
stored ADDR).
Required promoting .any to a first-class flat-memory aggregate (was
kindOf -> .unsupported): kindOf(.any) = .aggregate (16B, by-address) and
fieldOffset special-cases .any to the {@0, @8} layout (shared with
string/slice). Without the latter a struct_get on an Any panicked
(union field 'struct' while 'any' is active) -- caught + fixed, no crash.
Updated two unit tests that used unbox_any as the "unported op" example ->
compiler_call; added a box->unbox round-trip test. 697/0 both gates + all
unit tests. The 6 box_any examples no longer bail at box_any (output matches
legacy) but fall back further at switch_br/type_name/out (later 4A steps).
This commit is contained in:
@@ -689,6 +689,26 @@ test "comptime_vm exec: tagged-union enum_init with payload lays out {tag@0, pay
|
||||
try std.testing.expectEqual(@as(u64, 42), try v.machine.readWord(addr + 8, 8)); // payload
|
||||
}
|
||||
|
||||
test "comptime_vm exec: box_any/unbox_any round-trips a scalar through the {tag, value} box" {
|
||||
const alloc = std.testing.allocator;
|
||||
var table = types.TypeTable.init(alloc);
|
||||
defer table.deinit();
|
||||
// a := box_any(42, i64); return unbox_any(a) → 42 (exercises both the 16-byte
|
||||
// {tag@0, value@8} box write and the value-slot read-back).
|
||||
var fb = Fb.init(alloc, &.{}, .i64);
|
||||
defer fb.deinit();
|
||||
const b0 = fb.block(&.{});
|
||||
const c = fb.add(b0, inst(.{ .const_int = 42 }, .i64));
|
||||
const a = fb.add(b0, inst(.{ .box_any = .{ .operand = ref(c), .source_type = .i64 } }, .any));
|
||||
const u = fb.add(b0, inst(.{ .unbox_any = .{ .operand = ref(a) } }, .i64));
|
||||
_ = fb.add(b0, inst(.{ .ret = .{ .operand = ref(u) } }, .void));
|
||||
|
||||
var v = vm.Vm.init(alloc);
|
||||
v.table = &table;
|
||||
defer v.deinit();
|
||||
try std.testing.expectEqual(@as(i64, 42), toI64(try v.run(&fb.func, &.{})));
|
||||
}
|
||||
|
||||
test "comptime_vm exec: const_type yields a Type-value word; regToValue bridges it to .type_tag" {
|
||||
const alloc = std.testing.allocator;
|
||||
var table = types.TypeTable.init(alloc);
|
||||
@@ -1245,11 +1265,12 @@ test "comptime_vm tryEval: pure function → Value; unsupported → null" {
|
||||
const v = vm.tryEval(alloc, &module, ok_id) orelse return error.VmShouldHaveHandledIt;
|
||||
try std.testing.expectEqual(@as(i64, 42), v.int);
|
||||
|
||||
// fn bad() { unbox_any(1) } → tryEval yields null (caller falls back to legacy)
|
||||
// fn bad() { compiler_call() } → an unported op → tryEval yields null (caller
|
||||
// falls back to legacy). (box_any/unbox_any are now VM-native; compiler_call is
|
||||
// still unported until Phase 4D.)
|
||||
var fb2 = Fb.init(alloc, &.{}, .void);
|
||||
const c0 = fb2.block(&.{});
|
||||
const c = fb2.add(c0, inst(.{ .const_int = 1 }, .i64));
|
||||
_ = fb2.add(c0, inst(.{ .unbox_any = .{ .operand = ref(c) } }, .i64));
|
||||
_ = fb2.add(c0, inst(.{ .compiler_call = .{ .name = 0, .args = &.{} } }, .void));
|
||||
_ = fb2.add(c0, inst(.ret_void, .void));
|
||||
const bad_id = module.addFunction(fb2.func);
|
||||
|
||||
@@ -1271,19 +1292,18 @@ test "comptime_vm exec: division by zero and unsupported op bail loudly" {
|
||||
try std.testing.expectEqual(@as(i64, 4), toI64(try v.run(&fb.func, &.{ fromI64(12), fromI64(3) })));
|
||||
try std.testing.expectError(error.DivisionByZero, v.run(&fb.func, &.{ fromI64(12), fromI64(0) }));
|
||||
}
|
||||
// A not-yet-ported op (unbox_any) → Unsupported with the op name in `detail`.
|
||||
// A not-yet-ported op (compiler_call) → Unsupported with the op name in `detail`.
|
||||
{
|
||||
var fb = Fb.init(std.testing.allocator, &.{}, .void);
|
||||
defer fb.deinit();
|
||||
const b0 = fb.block(&.{});
|
||||
const c = fb.add(b0, inst(.{ .const_int = 1 }, .i64));
|
||||
_ = fb.add(b0, inst(.{ .unbox_any = .{ .operand = ref(c) } }, .i64));
|
||||
_ = fb.add(b0, inst(.{ .compiler_call = .{ .name = 0, .args = &.{} } }, .void));
|
||||
_ = fb.add(b0, inst(.ret_void, .void));
|
||||
|
||||
var v = vm.Vm.init(std.testing.allocator);
|
||||
defer v.deinit();
|
||||
try std.testing.expectError(error.Unsupported, v.run(&fb.func, &.{}));
|
||||
try std.testing.expectEqualStrings("unbox_any", v.detail.?);
|
||||
try std.testing.expectEqualStrings("compiler_call", v.detail.?);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user