Additive: keeps lib/io/io.ww's pre-vtable `stream` struct + `read`/`write`/`close` wrappers (fold-e2-eN migrates the legacy surface to vtable-backed implementations and retires it). lib/io/stream.ww — vtable struct (reader/writer/closer slots, spelled `(*T | void)` per #192 — ww parser rejects `nullable *T`), vstream = *vtable, and the st_read/st_write/st_close dispatchers per ref/hare/io/stream.ha:33-68. Void-arm `return e;` chains two direct widens: concrete `errors.unsupported` → `error` (#199 α) then `error` → (size|eof|error) (#205 NAMED-variant nominal at tagged→tagged subset). Hare's `?`-propagating st_close collapses to a direct `return (*c)(s);` because #173 is still open; the surface stays Hare-shaped. lib/io/types.ww — retarget reader/writer/closer fn-aliases from *stream to vstream. Extend `error` union to include `errors.unsupported` explicitly (no spread — per ken's #204-block the wrapper-vs-flatten layout asymmetry would mis-widen; the deferred fix is filed as #199b layout-extension). test/wcc/775_io_vtable_run.c — 7-row sentinel: reader/writer/ closer × {set, void} happy paths + branched-callee runtime. Rows verify the call runs + the constant exit; the void-arm rows do NOT inspect the resulting variant tag (deferred #199b wrapped-slot tag-remap mis-routes to dst tag 0). Cstage and wwstage emit byte-identical asm on every row. test/wcc/768_io_types_run.c — track the alias retarget; rows now build a vtable, pass `&vt` (= vstream), and call through the fn-VALUE param shape. Combined.ww regen for w6c + wwdump per #110: lib/errors lands transitively via the new `import errors;` in types.ww. Sibling filed inline (NOT fixed): the checker rejects bare `&fn_name` / `let p: *alias = &fn` assignment to a `(*alias | void)` field — the structural `*fn(...)` value isn't accepted as the `*alias` NAMED variant. Both stages reject. Probes route around via explicit `(&fn): *io.reader` cast at each vtable-field assignment.
96 lines
3.9 KiB
Plaintext
96 lines
3.9 KiB
Plaintext
// stream — Hare-shaped vtable port. Project #94 fold-e1.
|
||
//
|
||
// Adds the additive vtable surface alongside lib/io/io.ww's pre-vtable
|
||
// stream struct + read/write/close wrappers (which fold-e2-eN migrate
|
||
// to vtable-backed implementations and then retire). Three exports:
|
||
//
|
||
// vtable a struct of optional fn-pointer slots — reader/writer/
|
||
// closer per ref/hare/io/stream.ha:36-42. Hare spells the
|
||
// nullable as `nullable *T`; ww parser rejects that
|
||
// spelling (#192), so each slot is a `(*T | void)` tagged
|
||
// union (Plan-9-lean per the Nullable kept ruling; opt-in
|
||
// at optionality boundaries only).
|
||
// vstream `*vtable` alias matching Hare's `stream = *vtable`
|
||
// (ref/hare/io/stream.ha:33). Named `vstream` rather than
|
||
// `stream` because lib/io/io.ww still owns the legacy
|
||
// `stream` struct name until fold-eN collapses both.
|
||
// st_read /
|
||
// st_write /
|
||
// st_close the three dispatchers per ref/hare/io/stream.ha:44-68.
|
||
// Each `match`es the matching vtable slot, returns
|
||
// `errors.unsupported` (widened twice) when the slot is
|
||
// void, otherwise calls through the fn pointer.
|
||
//
|
||
// Fold-e1 cgen/checker dependencies (all closed at HEAD): #191
|
||
// (N_DOT on aliased-ptr receiver — `s.reader` resolves through the
|
||
// `vstream = *vtable` alias), #198 (`is` on cross-module variant
|
||
// tag), #199 α (concrete→tagged widen via NAMED-variant nominal
|
||
// match, used by `let e: error = u;`), #200 (`as` payload-extract),
|
||
// #201 (return-of-tagged-call forwards instead of rewinds), #205
|
||
// (tagged→tagged subset accepts NAMED-variant nominal match, used
|
||
// by `return e;` where ret type contains `error` as a direct
|
||
// variant). Two direct widens compose for the void-arm `return`:
|
||
// concrete `errors.unsupported` → tagged `error` via #199 α, then
|
||
// tagged `error` → tagged `(size | eof | error)` via #205.
|
||
//
|
||
// Deferrals (drew-signed): `seeker` lands with fold-e2 once `off`
|
||
// + `whence` plug into the signature; Hare's `?`-propagating
|
||
// `st_close` body (`c(s)?;`) collapses to a direct `return (*c)(s);`
|
||
// here because #173 (TRY-on-tagged-return both-stages broken) is
|
||
// still open — the dispatcher's surface stays Hare-shaped, only the
|
||
// internal try-prop is omitted.
|
||
|
||
package io;
|
||
|
||
// ref/hare/io/stream.ha:36-42. Nullable spelling per #192 (ww parser
|
||
// rejects `nullable *T`); each slot is `(*T | void)`.
|
||
export type vtable = struct {
|
||
reader: (*reader | void),
|
||
writer: (*writer | void),
|
||
closer: (*closer | void),
|
||
};
|
||
|
||
// ref/hare/io/stream.ha:33. `vstream` not `stream` while io.ww still
|
||
// owns the legacy `stream` struct name.
|
||
export type vstream = *vtable;
|
||
|
||
// ref/hare/io/stream.ha:44-50. The void-arm `return e;` chains two
|
||
// direct widens: `errors.unsupported → error` via #199 α, then
|
||
// `error → (size | eof | error)` via #205. The call-arm spells the
|
||
// fn-ptr call explicitly per #193 (ww requires `(*r)(args)` rather
|
||
// than implicit `r(args)`).
|
||
export fn st_read(s: vstream, buf: []u8) (size | eof | error) = {
|
||
match (s.reader) {
|
||
case void => {
|
||
let u: errors.unsupported;
|
||
let e: error = u;
|
||
return e;
|
||
};
|
||
case let r: *reader => return (*r)(s, buf);
|
||
};
|
||
};
|
||
|
||
// ref/hare/io/stream.ha:53-59.
|
||
export fn st_write(s: vstream, buf: []u8) (size | error) = {
|
||
match (s.writer) {
|
||
case void => {
|
||
let u: errors.unsupported;
|
||
let e: error = u;
|
||
return e;
|
||
};
|
||
case let w: *writer => return (*w)(s, buf);
|
||
};
|
||
};
|
||
|
||
// ref/hare/io/stream.ha:62-68. Hare propagates the close error via
|
||
// `c(s)?;` then falls through to the void arm; ww collapses to a
|
||
// direct return because #173 (TRY-on-tagged-return) is still open.
|
||
// The surface (void | error) matches; only the internal try-prop
|
||
// is omitted.
|
||
export fn st_close(s: vstream) (void | error) = {
|
||
match (s.closer) {
|
||
case void => return void;
|
||
case let c: *closer => return (*c)(s);
|
||
};
|
||
};
|