fix: comptime reg->value bridge for array-in-aggregate + clean abort on comptime-init failure (issue 0167)
(C) regToValue (comptime_vm.zig) gained no array arm, so a #run returning an aggregate containing an array bailed 'reg->value: aggregate shape not bridged yet'. Add an .array arm: read N elements at stride typeSizeBytes(elem) from the array address, bridge each recursively via regToValue -> an .aggregate Value (serializeAggregateValue already emits arrays). Composes with struct fields, nested arrays, array-of-structs, and the ?Arr optional payload; unbridgeable elements bail loudly. (E) A global failing #run proceeded into LLVM emission and panicked 'unresolved type reached LLVM emission' when the unresolved const was used. Add 'if (self.comptime_failed) return;' in emit() after Pass 0 so it aborts cleanly (exit 1, the comptime diagnostic) across run/ir/build. Regression: examples/comptime/0644-comptime-run-array-aggregate.sx. Verified by 3 adversarial reviews, suite 793/0. Filed separate bugs found during review: 0181 (optional-chain ?. to array field + index panics), 0182 (body-local #run unbridged silently miscompiles).
This commit is contained in:
@@ -2236,6 +2236,24 @@ pub const Vm = struct {
|
||||
}
|
||||
return .{ .aggregate = out };
|
||||
}
|
||||
if (info == .array) {
|
||||
// `[N]E` is held by-address as N contiguous `E` slots at
|
||||
// stride `sizeof(E)`. Bridge each element via `regToValue`
|
||||
// recursively (so a nested array / array-of-struct / array
|
||||
// inside a struct all compose), producing an `.aggregate`
|
||||
// Value whose serializer arm (`serializeAggregateValue`'s
|
||||
// `.array` case) emits an `LLVMConstArray2`.
|
||||
const elem_ty = info.array.element;
|
||||
const len: usize = @intCast(info.array.length);
|
||||
const stride: Addr = @intCast(table.typeSizeBytes(elem_ty));
|
||||
const out = alloc.alloc(Value, len) catch return self.failMsg("reg→value: out of memory (array)");
|
||||
for (0..len) |i| {
|
||||
const elem_addr = reg + @as(Addr, @intCast(i)) * stride;
|
||||
const er = try self.readField(table, elem_addr, elem_ty);
|
||||
out[i] = try self.regToValue(alloc, table, er, elem_ty);
|
||||
}
|
||||
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
|
||||
|
||||
@@ -391,6 +391,16 @@ pub const LLVMEmitter = struct {
|
||||
// Pass 0.5: Run comptime side-effect functions (#run expr; at top level)
|
||||
self.runComptimeSideEffects();
|
||||
|
||||
// A comptime/global init failure (e.g. an unbridgeable `#run` result)
|
||||
// sets `comptime_failed` AND leaves the failed const's type as the
|
||||
// `.unresolved` sentinel. The driver converts `comptime_failed` into a
|
||||
// clean exit-1 *after* emit() returns — but the remaining passes
|
||||
// (declare/emit function bodies that reference the now-unresolved const)
|
||||
// would `@panic("unresolved type reached LLVM emission")` first. Abort
|
||||
// emission here so the failure surfaces as the printed diagnostic +
|
||||
// clean exit, never the panic.
|
||||
if (self.comptime_failed) return;
|
||||
|
||||
// Pass 1: Declare all functions (so calls can reference them)
|
||||
for (self.ir_mod.functions.items, 0..) |func, i| {
|
||||
self.declareFunction(&func, @intCast(i));
|
||||
|
||||
Reference in New Issue
Block a user