comptime VM: VM-native metatype CONSTRUCTION — declare/define + tagged-union enum_init (P3.4 step 7)

The metatype type-construction builtins now run natively on the flat-memory
VM, so the construction examples run HANDLED end-to-end (no call_builtin
fallback to the legacy interp).

- Tagged-union enum_init WITH payload: allocate zeroed, write the tag at
  offset 0, copy the payload at tag_size ({ header, [N x i8] } layout).
- New .call_builtin exec arm -> callBuiltinVm (VM-native mirror of the legacy
  execBuiltinInner): declare(name) mints an empty forward nominal slot (shared
  declareNominal, also used by declare_type); define(handle, info) reads the
  TypeInfo tagged-union VALUE from flat memory and mints via defineFromInfo,
  a faithful port of legacy defineEnum/defineStruct/defineTuple (all-void enum
  -> real .enum per issue 0142, dup-name rejection, updatePreservingKey vs
  replaceKeyedInfo). Unmodeled builtins bail -> legacy fallback (dual-path).
- Refactored the []{name,ty} decode out of registerTypeVm into a shared
  decodeMemberSlice (+ decodeTypeSlice for bare-Type tuple elements).
- Correctness guard: enum_init/define assume a tag-headed layout, wrong for a
  backing_type tagged union (laid out as the backing struct) — both now bail
  loudly on backing_type != null rather than silent-clobber.

Examples 0614/0620/0621/0624/0632 run fully HANDLED on the VM; 0622/0623 run
define HANDLED then fall back at the still-unported type_info. VM output
byte-matches legacy for all 7. 697/0 both gates + all unit tests (added:
tagged-union enum_init payload layout).
This commit is contained in:
agra
2026-06-18 15:48:48 +03:00
parent eb68d9ed94
commit d0ebc55f99
3 changed files with 300 additions and 52 deletions

View File

@@ -659,6 +659,36 @@ test "comptime_vm exec: payloadless enum_init + enum_tag" {
try std.testing.expectEqual(@as(i64, 11), toI64(try v.run(&fb.func, &.{})));
}
test "comptime_vm exec: tagged-union enum_init with payload lays out {tag@0, payload@tag_size}" {
// The construction primitive `define` reuses: build `E.value(42)` where
// `E = { value: i64, closed: void }` and verify the flat-memory bytes — tag 0
// at offset 0, the i64 payload at offset tag_size (8). Mirrors the LLVM
// `{ header, [N x i8] }` layout the rest of the compiler reads.
const alloc = std.testing.allocator;
var table = types.TypeTable.init(alloc);
defer table.deinit();
const ufields = [_]types.TypeInfo.StructInfo.Field{
.{ .name = table.internString("value"), .ty = .i64 },
.{ .name = table.internString("closed"), .ty = .void },
};
const e = table.intern(.{ .tagged_union = .{ .name = table.internString("E"), .fields = &ufields, .tag_type = .i64 } });
// return E.value(42) → the tagged-union value's Addr
var fb = Fb.init(alloc, &.{}, e);
defer fb.deinit();
const b0 = fb.block(&.{});
const p = fb.add(b0, inst(.{ .const_int = 42 }, .i64));
const g = fb.add(b0, inst(.{ .enum_init = .{ .tag = 0, .payload = ref(p) } }, e));
_ = fb.add(b0, inst(.{ .ret = .{ .operand = ref(g) } }, .void));
var v = vm.Vm.init(alloc);
v.table = &table;
defer v.deinit();
const addr = try v.run(&fb.func, &.{});
try std.testing.expectEqual(@as(u64, 0), try v.machine.readWord(addr, 8)); // tag
try std.testing.expectEqual(@as(u64, 42), try v.machine.readWord(addr + 8, 8)); // payload
}
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);