Skip to content

v2026.5.15: struct-payload channels and mailboxes

v2026.5.15: struct-payload channels and mailboxes

Section titled “v2026.5.15: struct-payload channels and mailboxes”

Chan[T] and Mailbox[Msg] now carry arbitrary struct payloads through the generic layer with every field preserved per-call. The earlier chan_new_with_cap[T] / mailbox_new_with_cap[T] workaround constructors are retired — use plain chan_new / mailbox_new.

Before this release, sending a struct through a generic channel’s slot-write read the payload back with its first field corrupted (it always came back as 1). The cause was three coupled compiler gaps in the cross-module generic struct-by-value path:

  • Issue B — qualified type-arg chan_try_send[mod.Cmd] was not monomorphized (encodeTypeArgName had no .field_expr case).
  • Issues A + C — bare type-arg chan_try_send[Cmd] with the struct defined in the caller module: the substituted type name aliased monomorph-scoped storage that was freed before LLVM emission, so the spill alloca defaulted to i64 and the coercion path reinterpreted the struct through an error-union-shaped wrap. The constant 1 you saw in field 0 was the error-union discriminant leaking into your data.

All three are closed. A ts.resolve() audit pass also hardened two return-type interning sites against the same root cause.

use std.sync.mailbox as mb
use std.sync.chan as chan
pub struct Cmd { kind: u32, payload: u32 }
var slots: [4]Cmd = .undefined
var box = mb.mailbox_new[Cmd](slots[..])
_ = mb.mailbox_try_send[Cmd](&box, Cmd{ kind: 11, payload: 111 })
_ = mb.mailbox_try_send[Cmd](&box, Cmd{ kind: 22, payload: 222 })
var got: Cmd = Cmd{ kind: 0, payload: 0 }
_ = mb.mailbox_try_recv_into[Cmd](&box, &got)
// got == Cmd{ kind: 11, payload: 111 } — both fields, per-call.

The same shape works for the blocking mailbox_recv (?Cmd), mailbox_close, and terminal-drain semantics, and identically for the raw Chan[Struct] surface.

Terminal window
./scripts/zb test-chan-smoke # scenario 9: Mailbox[Cmd] round-trip
./scripts/zb test-mailbox-struct-payload # focused struct-payload regression
./scripts/zb test-struct-byval-xmod # bare-type-arg cross-module generic
./scripts/zb test-xmod-qualified-type-arg # qualified-type-arg cross-module generic
  • Chan[T] and Mailbox[Msg] remain SPSC.
  • Multi-thread proof coverage is still in progress.
  • Struct payloads are pass-by-value; the backing slice belongs to the caller and is never reallocated.