lib/io: port mode/whence/error + reader/writer/closer fn-aliases (#94 fold-d)
New lib/io/types.ww mirrors ref/hare/io/types.ha — the surrounding
port that lives alongside the existing lib/io/io.ww (pre-vtable
stream + eof + underread). Hare splits the same way (stream.ha +
types.ha share `module io`); ww does the equivalent via dir-enum.
Each type cites Hare per CLAUDE.md rule 9:
- mode (enum u8) — ref/hare/io/types.ha:29-34. RDWR=3 (not
Hare's `READ | WRITE`) because ww enum-value
positions don't fold expressions; bitfield
value SSoT preserved, divergence inline.
- whence (enum i32) — ref/hare/io/types.ha:37-41. Hare leaves the
underlying implicit; ww requires one. i32
matches the `off` type fold-e wires in.
- error — ref/hare/io/types.ha:11. Hare spreads
`errors::error`; lib/errors not ported, so
the union carries the two tags observable
in this fold: underread (from io.ww) and
the predeclared `nomem` (#29, type.c:72 /
check.ww:78). NOT redefined here.
- reader/writer/closer — ref/hare/io/types.ha:46/51/55. EOF=eof
(not Hare's `done` singleton) per #93
and the io.ww:8 rationale. `*stream`
forward-refs the existing pre-vtable
struct in io.ww; same cross-file pattern
Hare uses.
Drew signoff (this fold only): seeker, copier, strerror, and the
EOF=done singleton DEFERRED to fold-e — they need the `handle` sum
and #93's done landing. Hare's `_unsafe` carve-out unaffected.
eof / underread / stream re-used from io.ww (NOT redefined); io.ww
keeps the pre-vtable struct unchanged, ditto its WHY-comments.
Combined.ww regen (#110): selfhost/cmd/{w6c,wwdump}/main.combined.ww
auto-pulled the new types.ww via dir-enum (+52 lines each, same
package io). Makefile dep lines for wwdump_ww + w6c_ww add the new
source so editing it triggers rebuild.
Probe: test/wcc/768_io_types_run.c — 5 rows × 2 stages = 10
invocations. Pins enum value/underlying + the three fn-type aliases
at the param slot. Both siblings filed inline in the probe header:
- #189 wwstage `let r: io.reader = fn_name;` bails "let: not
assignable". cstage accepts. Param + struct-field paths work
in both stages, so io vtable port is unblocked. Probe uses
the alias only at the param slot.
- #190 wwstage match-arm on cross-module variant tag bails
"case: not a variant of scrutinee (io.eof | io.error)". Likely
same family as #178. cstage accepts. Probe uses `is` instead
of `match` for the variant gate.
make test: 201/201 (was 200; +1 from 768). 990-997 byte-id +
combined_ww_fresh + sizelint all green.
This commit is contained in:
262
test/wcc/768_io_types_run.c
Normal file
262
test/wcc/768_io_types_run.c
Normal file
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 768_io_types_run — project #94 fold-1 step (d) (drew Fold D) sentinel.
|
||||
* Pins the new lib/io/types.ww port (ref/hare/io/types.ha) — `mode` /
|
||||
* `whence` enums, `error = !(underread | nomem)` union, and the
|
||||
* `reader` / `writer` / `closer` fn-type aliases. The aliases forward-
|
||||
* ref `*stream` from lib/io/io.ww (Hare splits the same way:
|
||||
* stream.ha + types.ha share the `io` module).
|
||||
*
|
||||
* Each row is a self-contained `import io;` program: cstage `ww build`
|
||||
* compiles it, we run the binary and assert the exit. Rows that hit a
|
||||
* known wwstage checker gap (see SIBLINGS below) are cstage-only via
|
||||
* stage_mask — the bulk run both stages so the dir-enum concat through
|
||||
* types.ww + io.ww is exercised by ww_ww too.
|
||||
*
|
||||
* row | what it pins
|
||||
* --------------------------+--------------------------------------
|
||||
* enum_mode_value | io.mode (enum u8) member access +
|
||||
* | underlying numeric promotion. RDWR=3
|
||||
* | matches Hare's READ|WRITE bitfield
|
||||
* | (1 SSoT; see types.ww header note on
|
||||
* | the value spelling).
|
||||
* enum_whence_value | io.whence (enum i32) member access.
|
||||
* | END=2 matches ref/hare/io/types.ha:40.
|
||||
* reader_alias_param | `io.reader` usable as a fn-VALUE param
|
||||
* | type — the immediate use case the
|
||||
* | upcoming vtable port needs. dummy_read
|
||||
* | passed by-name, called through the
|
||||
* | alias, `is size` gate confirms the
|
||||
* | (size | io.eof | io.error) return
|
||||
* | shape parses + dispatches.
|
||||
* writer_alias_param | `io.writer` same shape; (size |
|
||||
* | io.error) return.
|
||||
* closer_alias_param | `io.closer` (void | io.error). Most
|
||||
* | minimal of the three — pins the alias
|
||||
* | resolves without depending on the
|
||||
* | tagged-return discriminant path.
|
||||
*
|
||||
* SIBLINGS (out of scope for #94 fold-1(d), filed inline):
|
||||
*
|
||||
* - #189: wwstage checker bails "let: not assignable" on
|
||||
* `let r: io.reader = some_fn_name;`
|
||||
* i.e. binding a fn-name to a fn-VALUE-typed let. cstage accepts.
|
||||
* Param + struct-field assignment work in both stages, so the io
|
||||
* vtable port is not blocked. PROBE ROUTES AROUND by using the
|
||||
* alias only at the param slot.
|
||||
*
|
||||
* - #190: wwstage match-arm on cross-module variant tag bails
|
||||
* "case: not a variant of scrutinee (io.eof)" / "(io.error)"
|
||||
* on `match (r(s,buf)) { case io.eof => ...; case let e: io.error
|
||||
* => ...; }`. Likely the same family as #178 (typeeqast layer
|
||||
* asymmetry). cstage accepts. PROBE ROUTES AROUND by using `is`
|
||||
* instead of `match` for the variant gate. The Hare-faithful
|
||||
* match form lands once that family is closed.
|
||||
*
|
||||
* GATE POLARITY: must stay GREEN. A red here means either types.ww
|
||||
* stopped parsing/checking, an enum-value underlying narrowed, or the
|
||||
* fn-type alias resolution regressed.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define STAGE_CS 1
|
||||
#define STAGE_WW 2
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_exit;
|
||||
int stage_mask;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "enum_mode_value",
|
||||
"package main;\n"
|
||||
"import io;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let m: io.mode = io.mode.RDWR;\n"
|
||||
" return m: i32;\n"
|
||||
"};\n",
|
||||
3,
|
||||
STAGE_CS | STAGE_WW },
|
||||
{ "enum_whence_value",
|
||||
"package main;\n"
|
||||
"import io;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let w: io.whence = io.whence.END;\n"
|
||||
" return w: i32;\n"
|
||||
"};\n",
|
||||
2,
|
||||
STAGE_CS | STAGE_WW },
|
||||
{ "reader_alias_param",
|
||||
"package main;\n"
|
||||
"import io;\n"
|
||||
"fn dummy_read(s: *io.stream, buf: []u8) (size | io.eof | io.error) = {\n"
|
||||
" return 0: size;\n"
|
||||
"};\n"
|
||||
"fn use_reader(r: io.reader, s: *io.stream, buf: []u8) i32 = {\n"
|
||||
" let v = r(s, buf);\n"
|
||||
" if (v is size) { return 11; };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let st: io.stream;\n"
|
||||
" let buf: [4]u8;\n"
|
||||
" let bs: []u8 = buf[0:4];\n"
|
||||
" return use_reader(dummy_read, &st, bs);\n"
|
||||
"};\n",
|
||||
11,
|
||||
STAGE_CS | STAGE_WW },
|
||||
{ "writer_alias_param",
|
||||
"package main;\n"
|
||||
"import io;\n"
|
||||
"fn dummy_write(s: *io.stream, buf: []u8) (size | io.error) = {\n"
|
||||
" return buf.len: size;\n"
|
||||
"};\n"
|
||||
"fn use_writer(w: io.writer, s: *io.stream, buf: []u8) i32 = {\n"
|
||||
" let v = w(s, buf);\n"
|
||||
" if (v is size) { return 22; };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let st: io.stream;\n"
|
||||
" let buf: [7]u8;\n"
|
||||
" let bs: []u8 = buf[0:7];\n"
|
||||
" return use_writer(dummy_write, &st, bs);\n"
|
||||
"};\n",
|
||||
22,
|
||||
STAGE_CS | STAGE_WW },
|
||||
{ "closer_alias_param",
|
||||
"package main;\n"
|
||||
"import io;\n"
|
||||
"fn dummy_close(s: *io.stream) (void | io.error) = {\n"
|
||||
" return void;\n"
|
||||
"};\n"
|
||||
"fn use_closer(c: io.closer, s: *io.stream) i32 = {\n"
|
||||
" let _v = c(s);\n"
|
||||
" return 33;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let st: io.stream;\n"
|
||||
" return use_closer(dummy_close, &st);\n"
|
||||
"};\n",
|
||||
33,
|
||||
STAGE_CS | STAGE_WW },
|
||||
};
|
||||
|
||||
static int
|
||||
build_with(const char *driver, const char *src_path, const char *tmpdir,
|
||||
const char *cwd)
|
||||
{
|
||||
char cmd[2048];
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s build -I %s/lib %s 2>/dev/null",
|
||||
tmpdir, driver, cwd, src_path);
|
||||
return runwait(cmd);
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[640], wdrv[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
const struct row *r = &rows[i];
|
||||
|
||||
char src[64], tmpdir[64];
|
||||
snprintf(src, sizeof src, "/tmp/iotyp_%d_%d.ww",
|
||||
getpid(), i);
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/iotyp_%d_d_%d",
|
||||
getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { fail++; total++; continue; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
const char *base = strrchr(src, '/');
|
||||
base = base ? base + 1 : src;
|
||||
char outbin[128];
|
||||
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
|
||||
char *dot = strrchr(outbin, '.');
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
|
||||
if (r->stage_mask & STAGE_CS) {
|
||||
total++;
|
||||
if (build_with(cdrv, src, tmpdir, cwd) != 0) {
|
||||
fprintf(stderr,
|
||||
"io_types_run[cs][%s]: build failed\n",
|
||||
r->label);
|
||||
fail++;
|
||||
} else {
|
||||
int got = runwait(outbin);
|
||||
if (got != r->want_exit) {
|
||||
fprintf(stderr,
|
||||
"io_types_run[cs][%s]: rc=%d want=%d\n",
|
||||
r->label, got, r->want_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
unlink(outbin);
|
||||
}
|
||||
|
||||
if (have_ww && (r->stage_mask & STAGE_WW)) {
|
||||
total++;
|
||||
if (build_with(wdrv, src, tmpdir, cwd) != 0) {
|
||||
fprintf(stderr,
|
||||
"io_types_run[ww][%s]: build failed\n",
|
||||
r->label);
|
||||
fail++;
|
||||
} else {
|
||||
int got = runwait(outbin);
|
||||
if (got != r->want_exit) {
|
||||
fprintf(stderr,
|
||||
"io_types_run[ww][%s]: rc=%d want=%d\n",
|
||||
r->label, got, r->want_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
unlink(outbin);
|
||||
}
|
||||
|
||||
unlink(src);
|
||||
rmdir(tmpdir);
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"io_types_run: %d/%d rows failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("io_types_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user