Stream B1 B1.2 (Io capability + context.io + Future + cancel) is blocked on two newly-discovered, independent compiler bugs, both with standalone repros: - 0150: a `void` struct field crashes the compiler with an unsized-type SIGTRAP in LLVM getTypeSizeInBits. Blocks `Future(void)` -> `timeout`. - 0151: a type-var inferred from a fn-pointer parameter's RETURN type is not bound as a usable type in the function body (`unknown type 'R'`). Blocks the central `async(io, worker: ($A)->$R, arg)` free-fn's `Future(R)`. The B1.2 design itself is validated end-to-end (the Io protocol threaded on Context like Allocator, the stateless blocking CBlockingIo default, both __sx_default_context materializers, and `context.io.now_ms()` all work live). Only the async/await/timeout ergonomic layer hits the two bugs. Per the IMPASSABLE STOP rule, all B1.2 working changes were reverted (master green, 726/0) and the work paused pending fixes; WIP is saved at .sx-tmp/b12-wip/. Checkpoint + plan updated to mark B1.2 BLOCKED with full resume notes.
3.4 KiB
0150 — a void struct field crashes the compiler (unsized-type SIGTRAP in LLVM)
Status
OPEN — surfaced by Stream B1 (fibers) B1.2: Future(void) (needed by
timeout(io, ms) -> Future(void)) instantiates a struct with a result: void
field, which hits this bug. Independent of the fibers work (a plain
struct { v: void; } reproduces it standalone).
Symptom
Declaring or instantiating any struct that has a field of type void aborts the
compiler with SIGTRAP (exit 133/134) — no sx diagnostic. The trap is LLVM's
llvm_unreachable("Cannot getTypeInfo() on a type that is unsized!"):
libLLVM`llvm::DataLayout::getTypeSizeInBits + 912 brk #0x1 (EXC_BREAKPOINT)
Reached via declareFunction → toLLVMType(func.ret) when a function returns
such a struct, or directly when laying out the struct.
Observed: SIGTRAP, no output, no diagnostic.
Expected: either zero-size the void field (a void/zero-sized field is a
legitimate construct — cf. Zig) OR emit a clean type diagnostic
("a struct field may not have type void") — never a raw backend crash.
Reproduction
#import "modules/std.sx";
Holder :: struct { v: void; ok: bool; }
main :: () -> i32 {
h : Holder = .{ ok = true };
if h.ok { print("ok\n"); }
return 0;
}
./zig-out/bin/sx run repro.sx → SIGTRAP (exit 133), no output.
Also reproduces through a generic: Box :: struct($T: Type) { v: T; } then
Box(void) — i.e. any monomorphization that binds a struct field to void.
Suspected area
src/backend/llvm/types.zigtoLLVMTypeInfo(struct field loop ~line 111): avoidfield's LLVM type is the unsizedvoidtype, thengetTypeSizeInBitson the enclosing struct traps.- The type layout / size code (
src/ir/types.zigtypeSizeBytesand the LLVM struct builder) should treat avoidfield as zero-sized (skip it in the LLVM struct, size 0, align 1) — the same way a zero-field struct is handled.
Investigation prompt (paste into a fresh session)
A
voidstruct field crashes the sx compiler with an unsized-type SIGTRAP in LLVMgetTypeSizeInBits(no diagnostic). Repro:issues/0150-...(run it → exit 133). Decide the semantics: avoidfield should be ZERO-SIZED (preferred — it is a legitimate construct, e.g.Future(void).result), laid out as nothing (size 0, align 1) and OMITTED from the LLVM struct body; OR, if zero-sized fields are out of scope, a clean front-end diagnostic ("a struct field may not have typevoid, found in field<name>of<Struct>") before emission — NEVER a backend trap. Likely sites:src/backend/llvm/types.zigtoLLVMTypeInfo(skipvoidfields when building the LLVM struct element list) +src/ir/types.zigsize/align (typeSizeBytes/align: avoidfield contributes 0). If choosing the diagnostic route, add it where struct fields are validated at type-resolution time. Verify: the repro printsok(zero-size route) or emits the diagnostic + clean exit 1 (diagnostic route); then move the repro intoexamples/as a regression test.
Why this matters for B1 (fibers)
Future($R) with $R = void is the natural shape for timeout(io, ms) -> Future(void) (B1.2 spec) and for any future-of-no-value. B1.2 deferred
timeout pending this fix rather than route around it with a substitute
non-void shape (which would hide the bug). Once 0150 lands, re-add timeout
with Future(void) (see the saved WIP at .sx-tmp/b12-wip/io.sx).