fix: comptime VM reg→value bridge for optional results (issue 0162)
Add an .optional arm to regToValue in comptime_vm.zig: read the
has_value flag at offset sizeof(child), bridge the payload recursively
into a { payload, i1=true } aggregate when set, yield .null_val (zero
{T,i1}) when clear or the bare null sentinel. Matching serialize arm in
serializeAggregateValue (emit_llvm.zig). Pointer/?Closure/?Protocol-child
optionals and array-payload aggregates bail loudly, not silently.
Regression: examples/comptime/0643-comptime-run-optional-aggregate.sx
(present ?T, present ?i64, null ?i64). Verified by 3 adversarial reviews.
This commit is contained in:
@@ -2236,6 +2236,37 @@ pub const Vm = struct {
|
||||
}
|
||||
return .{ .aggregate = out };
|
||||
}
|
||||
if (info == .optional) {
|
||||
// Only the `{ payload@0, has_value@sizeof(child) }` aggregate
|
||||
// shape lands here — a pointer-child optional is a word and
|
||||
// bridges through the `.word` arm. A closure / protocol child
|
||||
// has a different layout (sentinel func-ref / ctx-ptr-as-flag),
|
||||
// so guard against it and bail loudly rather than mis-read.
|
||||
const child = info.optional.child;
|
||||
if (optChildIsPtr(table, child))
|
||||
return self.failMsg("reg→value: pointer optional reached the aggregate arm");
|
||||
if (!child.isBuiltin()) switch (table.get(child)) {
|
||||
.closure => return self.failMsg("reg→value: ?Closure optional not bridged (sentinel layout)"),
|
||||
.@"struct" => |s| if (s.is_protocol)
|
||||
return self.failMsg("reg→value: ?Protocol optional not bridged (ctx-ptr layout)"),
|
||||
else => {},
|
||||
};
|
||||
// has_value flag lives at offset sizeof(child); clear → null.
|
||||
// The `const_null` form is a bare `null_addr` (no allocation);
|
||||
// treat that as none too (mirrors `optHas`).
|
||||
if (reg == null_addr) return .null_val;
|
||||
const has = (try self.machine.readWord(reg + table.typeSizeBytes(child), 1)) != 0;
|
||||
if (!has) return .null_val;
|
||||
// Present: bridge the payload (read from offset 0) as the child
|
||||
// type, and present it as the `{ payload, i1=true }` LLVM-struct
|
||||
// shape the host's optional serializer expects.
|
||||
const payload_reg = try self.readField(table, reg, child);
|
||||
const payload = try self.regToValue(alloc, table, payload_reg, child);
|
||||
const out = alloc.alloc(Value, 2) catch return self.failMsg("reg→value: out of memory (optional)");
|
||||
out[0] = payload;
|
||||
out[1] = .{ .boolean = true };
|
||||
return .{ .aggregate = out };
|
||||
}
|
||||
return self.failMsg("reg→value: aggregate shape not bridged yet");
|
||||
},
|
||||
.unsupported => return self.failMsg("reg→value: unsupported type"),
|
||||
|
||||
Reference in New Issue
Block a user