io: graduate stream to tagged-union returns

read → (i32 | eof | closed), write → (i32 | closed),
close → (void | closed). `type eof = void` and `type closed = void`
are exported as named-void variants — distinct nominal tags despite
identical (zero-byte) payloads.

No existing callers exercised the eof/closed sentinels (the smoke
test mimics the stream pattern with its own local types), so the
graduation is purely API shape — no callsite churn.
This commit is contained in:
2026-05-12 02:14:09 +09:00
parent e9c3f75fd1
commit 5c20c40724

View File

@@ -2,27 +2,32 @@
// //
// No closures, no methods. A `stream` is a struct of function // No closures, no methods. A `stream` is a struct of function
// pointers plus a `ctx: *void`. The error channel is the return // pointers plus a `ctx: *void`. The error channel is the return
// value of read/write/close. Negative i32 = errno-style code, // value of read/write/close — Hare-shaped tagged unions instead of
// non-negative = bytes transferred. // errno-style integer sentinels.
// eof — read past the end of the stream. NAMED-void so it's a
// distinct variant tag from `void` (which would be "no result yet").
export type eof = void;
// closed — operation attempted on a stream that has already been
// closed. NAMED-void; same shape, different tag.
export type closed = void;
type stream = struct { type stream = struct {
ctx: *void, ctx: *void,
read: fn(s: *stream, buf: []u8) i32, read: fn(s: *stream, buf: []u8) (i32 | eof | closed),
write: fn(s: *stream, buf: []u8) i32, write: fn(s: *stream, buf: []u8) (i32 | closed),
close: fn(s: *stream) i32, close: fn(s: *stream) (void | closed),
}; };
def eof: i32 = -1; export fn read(s: *stream, buf: []u8) (i32 | eof | closed) = {
def closed: i32 = -2;
export fn read(s: *stream, buf: []u8) i32 = {
return s.read(s, buf); return s.read(s, buf);
}; };
export fn write(s: *stream, buf: []u8) i32 = { export fn write(s: *stream, buf: []u8) (i32 | closed) = {
return s.write(s, buf); return s.write(s, buf);
}; };
export fn close(s: *stream) i32 = { export fn close(s: *stream) (void | closed) = {
return s.close(s); return s.close(s);
}; };