From fd45aedf6ce5f01c3d646d77d19bf632cc520352 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Tue, 12 May 2026 02:25:40 +0900 Subject: [PATCH] os: graduate filesize/readall/writeall to (i64 | oserror) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `type oserror = i64` carries -errno (Hare's errors::errno-shaped named-i64). The three convenience wrappers move off the i64 = -1 sentinel and onto the tagged-union surface. Callers updated across the selfhost (wwdump, w6c, w6a, w6l, ww driver). The slurp paths in w6c/w6a/w6l/wwdump now match on the filesize and readall results; the ELF-emitting writeall sites in w6a/obj.ww are wrapped through two small local helpers (`wrn` for "wrote N bytes ok?", `wrdrop` for fire-and-forget) so the existing 11-callsite write loop stays readable. selfhost/test/smoke.ww kept using raw os.read instead of os.readall: the 990 cgen-match probe compiles smoke.ww standalone (no `use` expansion), and cross-module type references like `os.oserror` can't be resolved in that mode. Two selfhost-side gaps surfaced and got plugged: - lib/ww/parse/parse.ww parsetype now collapses dotted type names (`pkg.Type` → single N_TNAME with the joined string), mirroring C parsetype's dotted-path loop. Local `joindotted` helper because there's no arena-based string-concat in the selfhost lib yet. - selfhost/cmd/wcc/check.ww name-resolver applies the dotted-prefix rule from cmd/wcc/check.c's resolve_typename: split at the last dot, look up the head as a `use` import, then the leaf as a type. --- lib/os/os.ww | 31 ++++---- lib/ww/parse/parse.ww | 33 ++++++++- selfhost/cmd/w6a/main.combined.ww | 92 ++++++++++++++++-------- selfhost/cmd/w6a/main.ww | 15 +++- selfhost/cmd/w6a/obj.ww | 46 ++++++++---- selfhost/cmd/w6c/main.combined.ww | 101 ++++++++++++++++++++------ selfhost/cmd/w6c/main.ww | 15 +++- selfhost/cmd/w6l/dyn.ww | 15 +++- selfhost/cmd/w6l/dynout.ww | 7 +- selfhost/cmd/w6l/main.combined.ww | 82 ++++++++++++++------- selfhost/cmd/w6l/obj.ww | 15 +++- selfhost/cmd/w6l/out.ww | 14 +++- selfhost/cmd/wcc/check.ww | 22 ++++++ selfhost/cmd/ww/main.combined.ww | 46 +++++++----- selfhost/cmd/ww/main.ww | 15 +++- selfhost/cmd/wwdump/main.combined.ww | 104 +++++++++++++++++++++------ selfhost/cmd/wwdump/main.ww | 18 ++++- selfhost/test/smoke.combined.ww | 37 ++++++---- selfhost/test/smoke.ww | 6 +- 19 files changed, 533 insertions(+), 181 deletions(-) diff --git a/lib/os/os.ww b/lib/os/os.ww index 400c78c1..70cacfe5 100644 --- a/lib/os/os.ww +++ b/lib/os/os.ww @@ -109,25 +109,29 @@ export fn lseek(fd: i32, off: i64, whence: i32) i64 = { return syscall3(SYS_LSEEK, fd: i64, off, whence: i64); }; -// filesize — convenience: returns the byte length of an open fd by -// seeking to the end and back. -1 on error. -export fn filesize(fd: i32) i64 = { +// oserror — the underlying errno from a failed syscall, as a +// negative i64 (Linux's int convention; e.g. -2 = ENOENT). The +// NAMED-i64 alias makes it a distinct variant tag from a "good" +// i64 byte count. Hare's analogue is errors::errno. +export type oserror = i64; + +// filesize — byte length of an open fd via lseek-to-end-and-back. +export fn filesize(fd: i32) (i64 | oserror) = { let end: i64 = lseek(fd, 0i64, SEEK_END); - if (end < 0) { return -1i64; }; + if (end < 0) { return end: oserror; }; let r: i64 = lseek(fd, 0i64, SEEK_SET); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; return end; }; // readall — keep reading until `n` bytes have arrived or the fd -// closes early. Returns bytes read (0..=n) or -1 on read error. -// Hare name (io::readall); the buffer is caller-supplied, matching -// the Plan 9 subset convention. -export fn readall(fd: i32, buf: *u8, n: u64) i64 = { +// closes early. Hare name (io::readall); the buffer is caller- +// supplied, matching the Plan 9 subset convention. +export fn readall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let got: u64 = 0u64; for (got < n) { let r: i64 = read(fd, buf + got, n - got); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return got: i64; }; // short read: caller decides got += r: u64; }; @@ -135,13 +139,12 @@ export fn readall(fd: i32, buf: *u8, n: u64) i64 = { }; // writeall — keep writing until `n` bytes have been accepted or the -// fd refuses progress. Returns bytes written or -1. Hare name -// (io::writeall). -export fn writeall(fd: i32, buf: *u8, n: u64) i64 = { +// fd refuses progress. Hare name (io::writeall). +export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let sent: u64 = 0u64; for (sent < n) { let r: i64 = write(fd, buf + sent, n - sent); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return sent: i64; }; sent += r: u64; }; diff --git a/lib/ww/parse/parse.ww b/lib/ww/parse/parse.ww index a399bc0d..60c1fe7f 100644 --- a/lib/ww/parse/parse.ww +++ b/lib/ww/parse/parse.ww @@ -104,6 +104,26 @@ fn expectbindname(p: *parser, into: *str) bool = { // Other forms (slice, array, struct, fn, chan, tuple, tagged) will // land in subsequent commits. +// joindotted — arena-build "head.tail" for dotted type-name path +// collapse. Mirrors aprintf in C parser; pulled local to avoid a +// cross-module dependency. +fn joindotted(a: *arena, head: str, tail: str) str = { + let n: u64 = head.len: u64 + 1u64 + tail.len: u64; + let p: *u8 = amalloc(a, n + 1u64): *u8; + let i: u64 = 0u64; + let j: i32 = 0; + for (j < head.len) { p[i] = head[j]; i += 1u64; j += 1; }; + p[i] = 46u8; // '.' + i += 1u64; + j = 0; + for (j < tail.len) { p[i] = tail[j]; i += 1u64; j += 1; }; + p[i] = 0u8; + let r: str; + r.ptr = p; + r.len = n: i32; + return r; +}; + fn parsetype(p: *parser) *node = { let pf: str = p.curfile; let pl: i32 = p.curline; @@ -175,10 +195,17 @@ fn parsetype(p: *parser) *node = { if (p.curkind == TK_IDENT) { let n: *node = newnode(p.a, N_TNAME, pf, pl, pc); - n.str = p.curtext; + let acc: str = p.curtext; advance(p); - // Dotted path collapse (pkg.Type) deferred — fixtures don't - // need it yet. + // Dotted path collapse: pkg.Type → single TNAME with the + // joined string. Mirrors C parsetype's loop. + for (p.curkind == TK_DOT) { + advance(p); + if (p.curkind != TK_IDENT) { break; }; + acc = joindotted(p.a, acc, p.curtext); + advance(p); + }; + n.str = acc; return n; }; diff --git a/selfhost/cmd/w6a/main.combined.ww b/selfhost/cmd/w6a/main.combined.ww index 69d2dbeb..87354108 100644 --- a/selfhost/cmd/w6a/main.combined.ww +++ b/selfhost/cmd/w6a/main.combined.ww @@ -110,25 +110,29 @@ export fn lseek(fd: i32, off: i64, whence: i32) i64 = { return syscall3(SYS_LSEEK, fd: i64, off, whence: i64); }; -// filesize — convenience: returns the byte length of an open fd by -// seeking to the end and back. -1 on error. -export fn filesize(fd: i32) i64 = { +// oserror — the underlying errno from a failed syscall, as a +// negative i64 (Linux's int convention; e.g. -2 = ENOENT). The +// NAMED-i64 alias makes it a distinct variant tag from a "good" +// i64 byte count. Hare's analogue is errors::errno. +export type oserror = i64; + +// filesize — byte length of an open fd via lseek-to-end-and-back. +export fn filesize(fd: i32) (i64 | oserror) = { let end: i64 = lseek(fd, 0i64, SEEK_END); - if (end < 0) { return -1i64; }; + if (end < 0) { return end: oserror; }; let r: i64 = lseek(fd, 0i64, SEEK_SET); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; return end; }; // readall — keep reading until `n` bytes have arrived or the fd -// closes early. Returns bytes read (0..=n) or -1 on read error. -// Hare name (io::readall); the buffer is caller-supplied, matching -// the Plan 9 subset convention. -export fn readall(fd: i32, buf: *u8, n: u64) i64 = { +// closes early. Hare name (io::readall); the buffer is caller- +// supplied, matching the Plan 9 subset convention. +export fn readall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let got: u64 = 0u64; for (got < n) { let r: i64 = read(fd, buf + got, n - got); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return got: i64; }; // short read: caller decides got += r: u64; }; @@ -136,13 +140,12 @@ export fn readall(fd: i32, buf: *u8, n: u64) i64 = { }; // writeall — keep writing until `n` bytes have been accepted or the -// fd refuses progress. Returns bytes written or -1. Hare name -// (io::writeall). -export fn writeall(fd: i32, buf: *u8, n: u64) i64 = { +// fd refuses progress. Hare name (io::writeall). +export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let sent: u64 = 0u64; for (sent < n) { let r: i64 = write(fd, buf + sent, n - sent); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return sent: i64; }; sent += r: u64; }; @@ -1853,6 +1856,26 @@ use os; use mem; use types; +// Local wrappers around os.writeall's tagged return — collapse the +// (i64 | oserror) back to a boolean / int sentinel for the +// length-checked / fire-and-forget write patterns below. +fn wrn(fd: i32, p: *u8, n: u64, want: i64) bool = { + let r: (i64 | os.oserror) = os.writeall(fd, p, n); + match (r) { + case let v: i64 => return v == want; + case let e: os.oserror => return false; + }; + return false; +}; + +fn wrdrop(fd: i32, p: *u8, n: u64) void = { + let r: (i64 | os.oserror) = os.writeall(fd, p, n); + match (r) { + case let v: i64 => { }; + case let e: os.oserror => { }; + }; +}; + // ---- ELF constants ---------------------------------------------------- def ELFCLASS64: u8 = 2u8; def ELFDATA2LSB: u8 = 1u8; @@ -2043,27 +2066,27 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru16(eh.ptr, 60u64, NSECT); // e_shnum wru16(eh.ptr, 62u64, 5u16); // e_shstrndx - if (os.writeall(fd, eh.ptr, 64u64) != 64i64) { return -1; }; + if (!wrn(fd, eh.ptr, 64u64, 64i64)) { return -1; }; if (a.textlen > 0u64) { - if (os.writeall(fd, a.text, a.textlen) != a.textlen: i64) { return -1; }; + if (!wrn(fd, a.text, a.textlen, a.textlen: i64)) { return -1; }; }; if (rela.n > 0u64) { - if (os.writeall(fd, rela.p, rela.n) != rela.n: i64) { return -1; }; + if (!wrn(fd, rela.p, rela.n, rela.n: i64)) { return -1; }; }; if (sym.n > 0u64) { - if (os.writeall(fd, sym.p, sym.n) != sym.n: i64) { return -1; }; + if (!wrn(fd, sym.p, sym.n, sym.n: i64)) { return -1; }; }; if (str_.n > 0u64) { - if (os.writeall(fd, str_.p, str_.n) != str_.n: i64) { return -1; }; + if (!wrn(fd, str_.p, str_.n, str_.n: i64)) { return -1; }; }; if (shstr.n > 0u64) { - if (os.writeall(fd, shstr.p, shstr.n) != shstr.n: i64) { return -1; }; + if (!wrn(fd, shstr.p, shstr.n, shstr.n: i64)) { return -1; }; }; // Pad to 8 before shdrs. let written: u64 = EHDR_SZ + a.textlen + rela.n + sym.n + str_.n + shstr.n; for ((written & 7u64) != 0u64) { - os.writeall(fd, &zero, 1u64); + wrdrop(fd, &zero, 1u64); written += 1u64; }; @@ -2072,7 +2095,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { // SHT_NULL let sn: i32 = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); // .text sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; @@ -2082,7 +2105,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru64(shbuf.ptr, 24u64, offtext); wru64(shbuf.ptr, 32u64, a.textlen); wru64(shbuf.ptr, 48u64, 1u64); // sh_addralign - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); // .rela.text sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; @@ -2095,7 +2118,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru32(shbuf.ptr, 44u64, 1u32); // sh_info = .text idx wru64(shbuf.ptr, 48u64, 8u64); wru64(shbuf.ptr, 56u64, RELA_SZ); - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); // .symtab sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; @@ -2107,7 +2130,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru32(shbuf.ptr, 44u64, 1u32); // sh_info = one local (STN_UNDEF) wru64(shbuf.ptr, 48u64, 8u64); wru64(shbuf.ptr, 56u64, SYM_SZ); - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); // .strtab sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; @@ -2116,7 +2139,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru64(shbuf.ptr, 24u64, offstr); wru64(shbuf.ptr, 32u64, str_.n); wru64(shbuf.ptr, 48u64, 1u64); - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); // .shstrtab sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; @@ -2125,7 +2148,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru64(shbuf.ptr, 24u64, offshstr); wru64(shbuf.ptr, 32u64, shstr.n); wru64(shbuf.ptr, 48u64, 1u64); - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); return 0; }; @@ -2167,12 +2190,21 @@ fn cstrlen(p: *u8) u64 = { fn slurp(path: *u8) (*u8, u64) = { let fd: i32 = os.open(path, os.O_RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; - let n: i64 = os.filesize(fd); - if (n < 0i64) { os.close(fd); return nil, 0u64; }; + let szr: (i64 | os.oserror) = os.filesize(fd); + let n: i64 = 0i64; + match (szr) { + case let v: i64 => n = v; + case let e: os.oserror => { os.close(fd); return nil, 0u64; }; + }; let nz: u64 = n: u64; let buf: *u8 = os.alloc(nz + 1u64): *u8; - let got: i64 = os.readall(fd, buf, nz); + let rr: (i64 | os.oserror) = os.readall(fd, buf, nz); os.close(fd); + let got: i64 = 0i64; + match (rr) { + case let v: i64 => got = v; + case let e: os.oserror => return nil, 0u64; + }; if (got != n) { return nil, 0u64; }; buf[nz] = 0u8; return buf, nz; diff --git a/selfhost/cmd/w6a/main.ww b/selfhost/cmd/w6a/main.ww index 82ef34f6..d69c24ff 100644 --- a/selfhost/cmd/w6a/main.ww +++ b/selfhost/cmd/w6a/main.ww @@ -34,12 +34,21 @@ fn cstrlen(p: *u8) u64 = { fn slurp(path: *u8) (*u8, u64) = { let fd: i32 = os.open(path, os.O_RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; - let n: i64 = os.filesize(fd); - if (n < 0i64) { os.close(fd); return nil, 0u64; }; + let szr: (i64 | os.oserror) = os.filesize(fd); + let n: i64 = 0i64; + match (szr) { + case let v: i64 => n = v; + case let e: os.oserror => { os.close(fd); return nil, 0u64; }; + }; let nz: u64 = n: u64; let buf: *u8 = os.alloc(nz + 1u64): *u8; - let got: i64 = os.readall(fd, buf, nz); + let rr: (i64 | os.oserror) = os.readall(fd, buf, nz); os.close(fd); + let got: i64 = 0i64; + match (rr) { + case let v: i64 => got = v; + case let e: os.oserror => return nil, 0u64; + }; if (got != n) { return nil, 0u64; }; buf[nz] = 0u8; return buf, nz; diff --git a/selfhost/cmd/w6a/obj.ww b/selfhost/cmd/w6a/obj.ww index acf6b1fc..e8c7575e 100644 --- a/selfhost/cmd/w6a/obj.ww +++ b/selfhost/cmd/w6a/obj.ww @@ -15,6 +15,26 @@ use os; use mem; use types; +// Local wrappers around os.writeall's tagged return — collapse the +// (i64 | oserror) back to a boolean / int sentinel for the +// length-checked / fire-and-forget write patterns below. +fn wrn(fd: i32, p: *u8, n: u64, want: i64) bool = { + let r: (i64 | os.oserror) = os.writeall(fd, p, n); + match (r) { + case let v: i64 => return v == want; + case let e: os.oserror => return false; + }; + return false; +}; + +fn wrdrop(fd: i32, p: *u8, n: u64) void = { + let r: (i64 | os.oserror) = os.writeall(fd, p, n); + match (r) { + case let v: i64 => { }; + case let e: os.oserror => { }; + }; +}; + // ---- ELF constants ---------------------------------------------------- def ELFCLASS64: u8 = 2u8; def ELFDATA2LSB: u8 = 1u8; @@ -205,27 +225,27 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru16(eh.ptr, 60u64, NSECT); // e_shnum wru16(eh.ptr, 62u64, 5u16); // e_shstrndx - if (os.writeall(fd, eh.ptr, 64u64) != 64i64) { return -1; }; + if (!wrn(fd, eh.ptr, 64u64, 64i64)) { return -1; }; if (a.textlen > 0u64) { - if (os.writeall(fd, a.text, a.textlen) != a.textlen: i64) { return -1; }; + if (!wrn(fd, a.text, a.textlen, a.textlen: i64)) { return -1; }; }; if (rela.n > 0u64) { - if (os.writeall(fd, rela.p, rela.n) != rela.n: i64) { return -1; }; + if (!wrn(fd, rela.p, rela.n, rela.n: i64)) { return -1; }; }; if (sym.n > 0u64) { - if (os.writeall(fd, sym.p, sym.n) != sym.n: i64) { return -1; }; + if (!wrn(fd, sym.p, sym.n, sym.n: i64)) { return -1; }; }; if (str_.n > 0u64) { - if (os.writeall(fd, str_.p, str_.n) != str_.n: i64) { return -1; }; + if (!wrn(fd, str_.p, str_.n, str_.n: i64)) { return -1; }; }; if (shstr.n > 0u64) { - if (os.writeall(fd, shstr.p, shstr.n) != shstr.n: i64) { return -1; }; + if (!wrn(fd, shstr.p, shstr.n, shstr.n: i64)) { return -1; }; }; // Pad to 8 before shdrs. let written: u64 = EHDR_SZ + a.textlen + rela.n + sym.n + str_.n + shstr.n; for ((written & 7u64) != 0u64) { - os.writeall(fd, &zero, 1u64); + wrdrop(fd, &zero, 1u64); written += 1u64; }; @@ -234,7 +254,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { // SHT_NULL let sn: i32 = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); // .text sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; @@ -244,7 +264,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru64(shbuf.ptr, 24u64, offtext); wru64(shbuf.ptr, 32u64, a.textlen); wru64(shbuf.ptr, 48u64, 1u64); // sh_addralign - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); // .rela.text sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; @@ -257,7 +277,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru32(shbuf.ptr, 44u64, 1u32); // sh_info = .text idx wru64(shbuf.ptr, 48u64, 8u64); wru64(shbuf.ptr, 56u64, RELA_SZ); - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); // .symtab sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; @@ -269,7 +289,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru32(shbuf.ptr, 44u64, 1u32); // sh_info = one local (STN_UNDEF) wru64(shbuf.ptr, 48u64, 8u64); wru64(shbuf.ptr, 56u64, SYM_SZ); - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); // .strtab sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; @@ -278,7 +298,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru64(shbuf.ptr, 24u64, offstr); wru64(shbuf.ptr, 32u64, str_.n); wru64(shbuf.ptr, 48u64, 1u64); - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); // .shstrtab sn = 0; for (sn < 64) { shbuf[sn] = 0u8; sn += 1; }; @@ -287,7 +307,7 @@ export fn emitelf(a: *asm_, fd: i32) i32 = { wru64(shbuf.ptr, 24u64, offshstr); wru64(shbuf.ptr, 32u64, shstr.n); wru64(shbuf.ptr, 48u64, 1u64); - os.writeall(fd, shbuf.ptr, 64u64); + wrdrop(fd, shbuf.ptr, 64u64); return 0; }; diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index a41a36a8..d604b478 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -110,25 +110,29 @@ export fn lseek(fd: i32, off: i64, whence: i32) i64 = { return syscall3(SYS_LSEEK, fd: i64, off, whence: i64); }; -// filesize — convenience: returns the byte length of an open fd by -// seeking to the end and back. -1 on error. -export fn filesize(fd: i32) i64 = { +// oserror — the underlying errno from a failed syscall, as a +// negative i64 (Linux's int convention; e.g. -2 = ENOENT). The +// NAMED-i64 alias makes it a distinct variant tag from a "good" +// i64 byte count. Hare's analogue is errors::errno. +export type oserror = i64; + +// filesize — byte length of an open fd via lseek-to-end-and-back. +export fn filesize(fd: i32) (i64 | oserror) = { let end: i64 = lseek(fd, 0i64, SEEK_END); - if (end < 0) { return -1i64; }; + if (end < 0) { return end: oserror; }; let r: i64 = lseek(fd, 0i64, SEEK_SET); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; return end; }; // readall — keep reading until `n` bytes have arrived or the fd -// closes early. Returns bytes read (0..=n) or -1 on read error. -// Hare name (io::readall); the buffer is caller-supplied, matching -// the Plan 9 subset convention. -export fn readall(fd: i32, buf: *u8, n: u64) i64 = { +// closes early. Hare name (io::readall); the buffer is caller- +// supplied, matching the Plan 9 subset convention. +export fn readall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let got: u64 = 0u64; for (got < n) { let r: i64 = read(fd, buf + got, n - got); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return got: i64; }; // short read: caller decides got += r: u64; }; @@ -136,13 +140,12 @@ export fn readall(fd: i32, buf: *u8, n: u64) i64 = { }; // writeall — keep writing until `n` bytes have been accepted or the -// fd refuses progress. Returns bytes written or -1. Hare name -// (io::writeall). -export fn writeall(fd: i32, buf: *u8, n: u64) i64 = { +// fd refuses progress. Hare name (io::writeall). +export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let sent: u64 = 0u64; for (sent < n) { let r: i64 = write(fd, buf + sent, n - sent); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return sent: i64; }; sent += r: u64; }; @@ -2954,6 +2957,26 @@ fn expectbindname(p: *parser, into: *str) bool = { // Other forms (slice, array, struct, fn, chan, tuple, tagged) will // land in subsequent commits. +// joindotted — arena-build "head.tail" for dotted type-name path +// collapse. Mirrors aprintf in C parser; pulled local to avoid a +// cross-module dependency. +fn joindotted(a: *arena, head: str, tail: str) str = { + let n: u64 = head.len: u64 + 1u64 + tail.len: u64; + let p: *u8 = amalloc(a, n + 1u64): *u8; + let i: u64 = 0u64; + let j: i32 = 0; + for (j < head.len) { p[i] = head[j]; i += 1u64; j += 1; }; + p[i] = 46u8; // '.' + i += 1u64; + j = 0; + for (j < tail.len) { p[i] = tail[j]; i += 1u64; j += 1; }; + p[i] = 0u8; + let r: str; + r.ptr = p; + r.len = n: i32; + return r; +}; + fn parsetype(p: *parser) *node = { let pf: str = p.curfile; let pl: i32 = p.curline; @@ -3025,10 +3048,17 @@ fn parsetype(p: *parser) *node = { if (p.curkind == TK_IDENT) { let n: *node = newnode(p.a, N_TNAME, pf, pl, pc); - n.str = p.curtext; + let acc: str = p.curtext; advance(p); - // Dotted path collapse (pkg.Type) deferred — fixtures don't - // need it yet. + // Dotted path collapse: pkg.Type → single TNAME with the + // joined string. Mirrors C parsetype's loop. + for (p.curkind == TK_DOT) { + advance(p); + if (p.curkind != TK_IDENT) { break; }; + acc = joindotted(p.a, acc, p.curtext); + advance(p); + }; + n.str = acc; return n; }; @@ -3758,6 +3788,28 @@ fn resolvewalk(c: *checker, n: *node) void = { let nm: str = n.str; if (nm.len > 0) { let s: *sym = scopelookup(c.cur, nm); + // `pkg.Type` — strip the last dot prefix and look up + // the leaf if `pkg` is a use-imported name. Mirrors + // cmd/wcc/check.c resolve_typename. + if (s == nil) { + let dot: i32 = nm.len - 1; + for (dot >= 0) { + if (nm[dot] == 46u8) { break; }; + dot -= 1; + }; + if (dot > 0) { + let head: str; + head.ptr = nm.ptr; + head.len = dot; + let m: *sym = scopelookup(c.cur, head); + if (m != nil) { + let leaf: str; + leaf.ptr = nm.ptr + (dot + 1): u64; + leaf.len = nm.len - (dot + 1); + s = scopelookup(c.cur, leaf); + }; + }; + }; if (s == nil) { c.nunresolved += 1; if (c.verbose != 0) { @@ -7997,12 +8049,21 @@ fn cstrlen(p: *u8) u64 = { fn slurp(path: *u8) (*u8, u64) = { let fd: i32 = os.open(path, os.O_RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; - let n: i64 = os.filesize(fd); - if (n < 0i64) { os.close(fd); return nil, 0u64; }; + let szr: (i64 | os.oserror) = os.filesize(fd); + let n: i64 = 0i64; + match (szr) { + case let v: i64 => n = v; + case let e: os.oserror => { os.close(fd); return nil, 0u64; }; + }; let nz: u64 = n: u64; let buf: *u8 = os.alloc(nz + 1u64): *u8; - let got: i64 = os.readall(fd, buf, nz); + let rr: (i64 | os.oserror) = os.readall(fd, buf, nz); os.close(fd); + let got: i64 = 0i64; + match (rr) { + case let v: i64 => got = v; + case let e: os.oserror => return nil, 0u64; + }; if (got != n) { return nil, 0u64; }; buf[nz] = 0u8; return buf, nz; diff --git a/selfhost/cmd/w6c/main.ww b/selfhost/cmd/w6c/main.ww index df03c843..a210687f 100644 --- a/selfhost/cmd/w6c/main.ww +++ b/selfhost/cmd/w6c/main.ww @@ -42,12 +42,21 @@ fn cstrlen(p: *u8) u64 = { fn slurp(path: *u8) (*u8, u64) = { let fd: i32 = os.open(path, os.O_RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; - let n: i64 = os.filesize(fd); - if (n < 0i64) { os.close(fd); return nil, 0u64; }; + let szr: (i64 | os.oserror) = os.filesize(fd); + let n: i64 = 0i64; + match (szr) { + case let v: i64 => n = v; + case let e: os.oserror => { os.close(fd); return nil, 0u64; }; + }; let nz: u64 = n: u64; let buf: *u8 = os.alloc(nz + 1u64): *u8; - let got: i64 = os.readall(fd, buf, nz); + let rr: (i64 | os.oserror) = os.readall(fd, buf, nz); os.close(fd); + let got: i64 = 0i64; + match (rr) { + case let v: i64 => got = v; + case let e: os.oserror => return nil, 0u64; + }; if (got != n) { return nil, 0u64; }; buf[nz] = 0u8; return buf, nz; diff --git a/selfhost/cmd/w6l/dyn.ww b/selfhost/cmd/w6l/dyn.ww index 0abbdf44..e223b222 100644 --- a/selfhost/cmd/w6l/dyn.ww +++ b/selfhost/cmd/w6l/dyn.ww @@ -126,11 +126,20 @@ fn dbasename(p: *u8) *u8 = { fn slurpso(path: *u8) (*u8, u64) = { let fd: i32 = os.open(path, os.O_RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; - let n: i64 = os.filesize(fd); - if (n < 0i64) { os.close(fd); return nil, 0u64; }; + let szr: (i64 | os.oserror) = os.filesize(fd); + let n: i64 = 0i64; + match (szr) { + case let v: i64 => n = v; + case let e: os.oserror => { os.close(fd); return nil, 0u64; }; + }; let buf: *u8 = os.alloc(n: u64): *u8; - let got: i64 = os.readall(fd, buf, n: u64); + let rr: (i64 | os.oserror) = os.readall(fd, buf, n: u64); os.close(fd); + let got: i64 = 0i64; + match (rr) { + case let v: i64 => got = v; + case let e: os.oserror => return nil, 0u64; + }; if (got != n) { return nil, 0u64; }; return buf, n: u64; }; diff --git a/selfhost/cmd/w6l/dynout.ww b/selfhost/cmd/w6l/dynout.ww index d0b5fb93..b0f6c311 100644 --- a/selfhost/cmd/w6l/dynout.ww +++ b/selfhost/cmd/w6l/dynout.ww @@ -720,9 +720,10 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { dbcopy(filebuf, gotpltoff, gotpltbuf, gotpltsz); dbcopy(filebuf, dynamicoff, dynamicbuf, dynamicsz); - let wrote: i64 = os.writeall(fd, filebuf, fileend); - if (wrote != fileend: i64) { - return 1; + let wr: (i64 | os.oserror) = os.writeall(fd, filebuf, fileend); + match (wr) { + case let v: i64 => { if (v != fileend: i64) { return 1; }; }; + case let e: os.oserror => return 1; }; return 0; }; diff --git a/selfhost/cmd/w6l/main.combined.ww b/selfhost/cmd/w6l/main.combined.ww index 476e1b0d..f0c01619 100644 --- a/selfhost/cmd/w6l/main.combined.ww +++ b/selfhost/cmd/w6l/main.combined.ww @@ -110,25 +110,29 @@ export fn lseek(fd: i32, off: i64, whence: i32) i64 = { return syscall3(SYS_LSEEK, fd: i64, off, whence: i64); }; -// filesize — convenience: returns the byte length of an open fd by -// seeking to the end and back. -1 on error. -export fn filesize(fd: i32) i64 = { +// oserror — the underlying errno from a failed syscall, as a +// negative i64 (Linux's int convention; e.g. -2 = ENOENT). The +// NAMED-i64 alias makes it a distinct variant tag from a "good" +// i64 byte count. Hare's analogue is errors::errno. +export type oserror = i64; + +// filesize — byte length of an open fd via lseek-to-end-and-back. +export fn filesize(fd: i32) (i64 | oserror) = { let end: i64 = lseek(fd, 0i64, SEEK_END); - if (end < 0) { return -1i64; }; + if (end < 0) { return end: oserror; }; let r: i64 = lseek(fd, 0i64, SEEK_SET); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; return end; }; // readall — keep reading until `n` bytes have arrived or the fd -// closes early. Returns bytes read (0..=n) or -1 on read error. -// Hare name (io::readall); the buffer is caller-supplied, matching -// the Plan 9 subset convention. -export fn readall(fd: i32, buf: *u8, n: u64) i64 = { +// closes early. Hare name (io::readall); the buffer is caller- +// supplied, matching the Plan 9 subset convention. +export fn readall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let got: u64 = 0u64; for (got < n) { let r: i64 = read(fd, buf + got, n - got); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return got: i64; }; // short read: caller decides got += r: u64; }; @@ -136,13 +140,12 @@ export fn readall(fd: i32, buf: *u8, n: u64) i64 = { }; // writeall — keep writing until `n` bytes have been accepted or the -// fd refuses progress. Returns bytes written or -1. Hare name -// (io::writeall). -export fn writeall(fd: i32, buf: *u8, n: u64) i64 = { +// fd refuses progress. Hare name (io::writeall). +export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let sent: u64 = 0u64; for (sent < n) { let r: i64 = write(fd, buf + sent, n - sent); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return sent: i64; }; sent += r: u64; }; @@ -503,11 +506,20 @@ def RELA_ADDEND: u64 = 16u64; fn slurp(path: *u8) (*u8, u64) = { let fd: i32 = os.open(path, os.O_RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; - let n: i64 = os.filesize(fd); - if (n < 0i64) { os.close(fd); return nil, 0u64; }; + let szr: (i64 | os.oserror) = os.filesize(fd); + let n: i64 = 0i64; + match (szr) { + case let v: i64 => n = v; + case let e: os.oserror => { os.close(fd); return nil, 0u64; }; + }; let buf: *u8 = os.alloc(n: u64): *u8; - let got: i64 = os.readall(fd, buf, n: u64); + let rr: (i64 | os.oserror) = os.readall(fd, buf, n: u64); os.close(fd); + let got: i64 = 0i64; + match (rr) { + case let v: i64 => got = v; + case let e: os.oserror => return nil, 0u64; + }; if (got != n) { return nil, 0u64; }; return buf, n: u64; }; @@ -1039,11 +1051,20 @@ fn dbasename(p: *u8) *u8 = { fn slurpso(path: *u8) (*u8, u64) = { let fd: i32 = os.open(path, os.O_RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; - let n: i64 = os.filesize(fd); - if (n < 0i64) { os.close(fd); return nil, 0u64; }; + let szr: (i64 | os.oserror) = os.filesize(fd); + let n: i64 = 0i64; + match (szr) { + case let v: i64 => n = v; + case let e: os.oserror => { os.close(fd); return nil, 0u64; }; + }; let buf: *u8 = os.alloc(n: u64): *u8; - let got: i64 = os.readall(fd, buf, n: u64); + let rr: (i64 | os.oserror) = os.readall(fd, buf, n: u64); os.close(fd); + let got: i64 = 0i64; + match (rr) { + case let v: i64 => got = v; + case let e: os.oserror => return nil, 0u64; + }; if (got != n) { return nil, 0u64; }; return buf, n: u64; }; @@ -2149,9 +2170,10 @@ export fn emitdynelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { dbcopy(filebuf, gotpltoff, gotpltbuf, gotpltsz); dbcopy(filebuf, dynamicoff, dynamicbuf, dynamicsz); - let wrote: i64 = os.writeall(fd, filebuf, fileend); - if (wrote != fileend: i64) { - return 1; + let wr: (i64 | os.oserror) = os.writeall(fd, filebuf, fileend); + match (wr) { + case let v: i64 => { if (v != fileend: i64) { return 1; }; }; + case let e: os.oserror => return 1; }; return 0; }; @@ -2249,10 +2271,20 @@ export fn emitelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { wru64(hdr, 112u64, TEXT_OFF); // p_align // Write [0..0x1000) then .text. - let n1: i64 = os.writeall(fd, hdr, TEXT_OFF); + let r1: (i64 | os.oserror) = os.writeall(fd, hdr, TEXT_OFF); + let n1: i64 = 0i64; + match (r1) { + case let v: i64 => n1 = v; + case let e: os.oserror => return -1; + }; if (n1 != TEXT_OFF: i64) { return -1; }; if (l.textlen > 0u64) { - let n2: i64 = os.writeall(fd, l.text, l.textlen); + let r2: (i64 | os.oserror) = os.writeall(fd, l.text, l.textlen); + let n2: i64 = 0i64; + match (r2) { + case let v: i64 => n2 = v; + case let e: os.oserror => return -1; + }; if (n2 != l.textlen: i64) { return -1; }; }; return 0; diff --git a/selfhost/cmd/w6l/obj.ww b/selfhost/cmd/w6l/obj.ww index b60007ad..624072e5 100644 --- a/selfhost/cmd/w6l/obj.ww +++ b/selfhost/cmd/w6l/obj.ww @@ -78,11 +78,20 @@ def RELA_ADDEND: u64 = 16u64; fn slurp(path: *u8) (*u8, u64) = { let fd: i32 = os.open(path, os.O_RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; - let n: i64 = os.filesize(fd); - if (n < 0i64) { os.close(fd); return nil, 0u64; }; + let szr: (i64 | os.oserror) = os.filesize(fd); + let n: i64 = 0i64; + match (szr) { + case let v: i64 => n = v; + case let e: os.oserror => { os.close(fd); return nil, 0u64; }; + }; let buf: *u8 = os.alloc(n: u64): *u8; - let got: i64 = os.readall(fd, buf, n: u64); + let rr: (i64 | os.oserror) = os.readall(fd, buf, n: u64); os.close(fd); + let got: i64 = 0i64; + match (rr) { + case let v: i64 => got = v; + case let e: os.oserror => return nil, 0u64; + }; if (got != n) { return nil, 0u64; }; return buf, n: u64; }; diff --git a/selfhost/cmd/w6l/out.ww b/selfhost/cmd/w6l/out.ww index cf19d5f8..5abef31f 100644 --- a/selfhost/cmd/w6l/out.ww +++ b/selfhost/cmd/w6l/out.ww @@ -90,10 +90,20 @@ export fn emitelf(l: *lnk, fd: i32, base: u64, entry: u64) i32 = { wru64(hdr, 112u64, TEXT_OFF); // p_align // Write [0..0x1000) then .text. - let n1: i64 = os.writeall(fd, hdr, TEXT_OFF); + let r1: (i64 | os.oserror) = os.writeall(fd, hdr, TEXT_OFF); + let n1: i64 = 0i64; + match (r1) { + case let v: i64 => n1 = v; + case let e: os.oserror => return -1; + }; if (n1 != TEXT_OFF: i64) { return -1; }; if (l.textlen > 0u64) { - let n2: i64 = os.writeall(fd, l.text, l.textlen); + let r2: (i64 | os.oserror) = os.writeall(fd, l.text, l.textlen); + let n2: i64 = 0i64; + match (r2) { + case let v: i64 => n2 = v; + case let e: os.oserror => return -1; + }; if (n2 != l.textlen: i64) { return -1; }; }; return 0; diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index 53beb22b..1a6e74a7 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -108,6 +108,28 @@ fn resolvewalk(c: *checker, n: *node) void = { let nm: str = n.str; if (nm.len > 0) { let s: *sym = scopelookup(c.cur, nm); + // `pkg.Type` — strip the last dot prefix and look up + // the leaf if `pkg` is a use-imported name. Mirrors + // cmd/wcc/check.c resolve_typename. + if (s == nil) { + let dot: i32 = nm.len - 1; + for (dot >= 0) { + if (nm[dot] == 46u8) { break; }; + dot -= 1; + }; + if (dot > 0) { + let head: str; + head.ptr = nm.ptr; + head.len = dot; + let m: *sym = scopelookup(c.cur, head); + if (m != nil) { + let leaf: str; + leaf.ptr = nm.ptr + (dot + 1): u64; + leaf.len = nm.len - (dot + 1); + s = scopelookup(c.cur, leaf); + }; + }; + }; if (s == nil) { c.nunresolved += 1; if (c.verbose != 0) { diff --git a/selfhost/cmd/ww/main.combined.ww b/selfhost/cmd/ww/main.combined.ww index 9d482fc6..2f0950f9 100644 --- a/selfhost/cmd/ww/main.combined.ww +++ b/selfhost/cmd/ww/main.combined.ww @@ -110,25 +110,29 @@ export fn lseek(fd: i32, off: i64, whence: i32) i64 = { return syscall3(SYS_LSEEK, fd: i64, off, whence: i64); }; -// filesize — convenience: returns the byte length of an open fd by -// seeking to the end and back. -1 on error. -export fn filesize(fd: i32) i64 = { +// oserror — the underlying errno from a failed syscall, as a +// negative i64 (Linux's int convention; e.g. -2 = ENOENT). The +// NAMED-i64 alias makes it a distinct variant tag from a "good" +// i64 byte count. Hare's analogue is errors::errno. +export type oserror = i64; + +// filesize — byte length of an open fd via lseek-to-end-and-back. +export fn filesize(fd: i32) (i64 | oserror) = { let end: i64 = lseek(fd, 0i64, SEEK_END); - if (end < 0) { return -1i64; }; + if (end < 0) { return end: oserror; }; let r: i64 = lseek(fd, 0i64, SEEK_SET); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; return end; }; // readall — keep reading until `n` bytes have arrived or the fd -// closes early. Returns bytes read (0..=n) or -1 on read error. -// Hare name (io::readall); the buffer is caller-supplied, matching -// the Plan 9 subset convention. -export fn readall(fd: i32, buf: *u8, n: u64) i64 = { +// closes early. Hare name (io::readall); the buffer is caller- +// supplied, matching the Plan 9 subset convention. +export fn readall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let got: u64 = 0u64; for (got < n) { let r: i64 = read(fd, buf + got, n - got); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return got: i64; }; // short read: caller decides got += r: u64; }; @@ -136,13 +140,12 @@ export fn readall(fd: i32, buf: *u8, n: u64) i64 = { }; // writeall — keep writing until `n` bytes have been accepted or the -// fd refuses progress. Returns bytes written or -1. Hare name -// (io::writeall). -export fn writeall(fd: i32, buf: *u8, n: u64) i64 = { +// fd refuses progress. Hare name (io::writeall). +export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let sent: u64 = 0u64; for (sent < n) { let r: i64 = write(fd, buf + sent, n - sent); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return sent: i64; }; sent += r: u64; }; @@ -605,12 +608,21 @@ fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64) *u8 = { fn slurp(pathcs: *u8) (*u8, u64) = { let fd: i32 = os.open(pathcs, os.O_RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; - let n: i64 = os.filesize(fd); - if (n < 0i64) { os.close(fd); return nil, 0u64; }; + let szr: (i64 | os.oserror) = os.filesize(fd); + let n: i64 = 0i64; + match (szr) { + case let v: i64 => n = v; + case let e: os.oserror => { os.close(fd); return nil, 0u64; }; + }; let nu: u64 = n: u64; let buf: *u8 = os.alloc(nu + 1u64): *u8; - let got: i64 = os.readall(fd, buf, nu); + let rr: (i64 | os.oserror) = os.readall(fd, buf, nu); os.close(fd); + let got: i64 = 0i64; + match (rr) { + case let v: i64 => got = v; + case let e: os.oserror => return nil, 0u64; + }; if (got != n) { return nil, 0u64; }; buf[nu] = 0u8; return buf, nu; diff --git a/selfhost/cmd/ww/main.ww b/selfhost/cmd/ww/main.ww index 60088bf3..b5e6a3e0 100644 --- a/selfhost/cmd/ww/main.ww +++ b/selfhost/cmd/ww/main.ww @@ -283,12 +283,21 @@ fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64) *u8 = { fn slurp(pathcs: *u8) (*u8, u64) = { let fd: i32 = os.open(pathcs, os.O_RDONLY, 0i32); if (fd < 0) { return nil, 0u64; }; - let n: i64 = os.filesize(fd); - if (n < 0i64) { os.close(fd); return nil, 0u64; }; + let szr: (i64 | os.oserror) = os.filesize(fd); + let n: i64 = 0i64; + match (szr) { + case let v: i64 => n = v; + case let e: os.oserror => { os.close(fd); return nil, 0u64; }; + }; let nu: u64 = n: u64; let buf: *u8 = os.alloc(nu + 1u64): *u8; - let got: i64 = os.readall(fd, buf, nu); + let rr: (i64 | os.oserror) = os.readall(fd, buf, nu); os.close(fd); + let got: i64 = 0i64; + match (rr) { + case let v: i64 => got = v; + case let e: os.oserror => return nil, 0u64; + }; if (got != n) { return nil, 0u64; }; buf[nu] = 0u8; return buf, nu; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 873701cd..25af5a5c 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -110,25 +110,29 @@ export fn lseek(fd: i32, off: i64, whence: i32) i64 = { return syscall3(SYS_LSEEK, fd: i64, off, whence: i64); }; -// filesize — convenience: returns the byte length of an open fd by -// seeking to the end and back. -1 on error. -export fn filesize(fd: i32) i64 = { +// oserror — the underlying errno from a failed syscall, as a +// negative i64 (Linux's int convention; e.g. -2 = ENOENT). The +// NAMED-i64 alias makes it a distinct variant tag from a "good" +// i64 byte count. Hare's analogue is errors::errno. +export type oserror = i64; + +// filesize — byte length of an open fd via lseek-to-end-and-back. +export fn filesize(fd: i32) (i64 | oserror) = { let end: i64 = lseek(fd, 0i64, SEEK_END); - if (end < 0) { return -1i64; }; + if (end < 0) { return end: oserror; }; let r: i64 = lseek(fd, 0i64, SEEK_SET); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; return end; }; // readall — keep reading until `n` bytes have arrived or the fd -// closes early. Returns bytes read (0..=n) or -1 on read error. -// Hare name (io::readall); the buffer is caller-supplied, matching -// the Plan 9 subset convention. -export fn readall(fd: i32, buf: *u8, n: u64) i64 = { +// closes early. Hare name (io::readall); the buffer is caller- +// supplied, matching the Plan 9 subset convention. +export fn readall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let got: u64 = 0u64; for (got < n) { let r: i64 = read(fd, buf + got, n - got); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return got: i64; }; // short read: caller decides got += r: u64; }; @@ -136,13 +140,12 @@ export fn readall(fd: i32, buf: *u8, n: u64) i64 = { }; // writeall — keep writing until `n` bytes have been accepted or the -// fd refuses progress. Returns bytes written or -1. Hare name -// (io::writeall). -export fn writeall(fd: i32, buf: *u8, n: u64) i64 = { +// fd refuses progress. Hare name (io::writeall). +export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let sent: u64 = 0u64; for (sent < n) { let r: i64 = write(fd, buf + sent, n - sent); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return sent: i64; }; sent += r: u64; }; @@ -2954,6 +2957,26 @@ fn expectbindname(p: *parser, into: *str) bool = { // Other forms (slice, array, struct, fn, chan, tuple, tagged) will // land in subsequent commits. +// joindotted — arena-build "head.tail" for dotted type-name path +// collapse. Mirrors aprintf in C parser; pulled local to avoid a +// cross-module dependency. +fn joindotted(a: *arena, head: str, tail: str) str = { + let n: u64 = head.len: u64 + 1u64 + tail.len: u64; + let p: *u8 = amalloc(a, n + 1u64): *u8; + let i: u64 = 0u64; + let j: i32 = 0; + for (j < head.len) { p[i] = head[j]; i += 1u64; j += 1; }; + p[i] = 46u8; // '.' + i += 1u64; + j = 0; + for (j < tail.len) { p[i] = tail[j]; i += 1u64; j += 1; }; + p[i] = 0u8; + let r: str; + r.ptr = p; + r.len = n: i32; + return r; +}; + fn parsetype(p: *parser) *node = { let pf: str = p.curfile; let pl: i32 = p.curline; @@ -3025,10 +3048,17 @@ fn parsetype(p: *parser) *node = { if (p.curkind == TK_IDENT) { let n: *node = newnode(p.a, N_TNAME, pf, pl, pc); - n.str = p.curtext; + let acc: str = p.curtext; advance(p); - // Dotted path collapse (pkg.Type) deferred — fixtures don't - // need it yet. + // Dotted path collapse: pkg.Type → single TNAME with the + // joined string. Mirrors C parsetype's loop. + for (p.curkind == TK_DOT) { + advance(p); + if (p.curkind != TK_IDENT) { break; }; + acc = joindotted(p.a, acc, p.curtext); + advance(p); + }; + n.str = acc; return n; }; @@ -3758,6 +3788,28 @@ fn resolvewalk(c: *checker, n: *node) void = { let nm: str = n.str; if (nm.len > 0) { let s: *sym = scopelookup(c.cur, nm); + // `pkg.Type` — strip the last dot prefix and look up + // the leaf if `pkg` is a use-imported name. Mirrors + // cmd/wcc/check.c resolve_typename. + if (s == nil) { + let dot: i32 = nm.len - 1; + for (dot >= 0) { + if (nm[dot] == 46u8) { break; }; + dot -= 1; + }; + if (dot > 0) { + let head: str; + head.ptr = nm.ptr; + head.len = dot; + let m: *sym = scopelookup(c.cur, head); + if (m != nil) { + let leaf: str; + leaf.ptr = nm.ptr + (dot + 1): u64; + leaf.len = nm.len - (dot + 1); + s = scopelookup(c.cur, leaf); + }; + }; + }; if (s == nil) { c.nunresolved += 1; if (c.verbose != 0) { @@ -8041,17 +8093,29 @@ export fn main(argc: i32, argv: **u8) i32 = { }; }; - let sz: i64 = os.filesize(fd); - if (sz < 0i64) { + let szr: (i64 | os.oserror) = os.filesize(fd); + let sz: i64 = 0i64; + match (szr) { + case let v: i64 => sz = v; + case let e: os.oserror => { os.write(2, "wwdump: filesize failed\n".ptr, 24u64); os.close(fd); return 1; }; + }; let a: *arena = newarena(); let buf: *u8 = amalloc(a, sz: u64): *u8; - let r: i64 = os.readall(fd, buf, sz: u64); + let rr: (i64 | os.oserror) = os.readall(fd, buf, sz: u64); os.close(fd); + let r: i64 = 0i64; + match (rr) { + case let v: i64 => r = v; + case let e: os.oserror => { + os.write(2, "wwdump: read failed\n".ptr, 20u64); + return 1; + }; + }; if (r != sz) { os.write(2, "wwdump: short read\n".ptr, 19u64); return 1; diff --git a/selfhost/cmd/wwdump/main.ww b/selfhost/cmd/wwdump/main.ww index b9ef1bdf..78f7f709 100644 --- a/selfhost/cmd/wwdump/main.ww +++ b/selfhost/cmd/wwdump/main.ww @@ -86,17 +86,29 @@ export fn main(argc: i32, argv: **u8) i32 = { }; }; - let sz: i64 = os.filesize(fd); - if (sz < 0i64) { + let szr: (i64 | os.oserror) = os.filesize(fd); + let sz: i64 = 0i64; + match (szr) { + case let v: i64 => sz = v; + case let e: os.oserror => { os.write(2, "wwdump: filesize failed\n".ptr, 24u64); os.close(fd); return 1; }; + }; let a: *arena = newarena(); let buf: *u8 = amalloc(a, sz: u64): *u8; - let r: i64 = os.readall(fd, buf, sz: u64); + let rr: (i64 | os.oserror) = os.readall(fd, buf, sz: u64); os.close(fd); + let r: i64 = 0i64; + match (rr) { + case let v: i64 => r = v; + case let e: os.oserror => { + os.write(2, "wwdump: read failed\n".ptr, 20u64); + return 1; + }; + }; if (r != sz) { os.write(2, "wwdump: short read\n".ptr, 19u64); return 1; diff --git a/selfhost/test/smoke.combined.ww b/selfhost/test/smoke.combined.ww index 431b4d27..23d3cc59 100644 --- a/selfhost/test/smoke.combined.ww +++ b/selfhost/test/smoke.combined.ww @@ -110,25 +110,29 @@ export fn lseek(fd: i32, off: i64, whence: i32) i64 = { return syscall3(SYS_LSEEK, fd: i64, off, whence: i64); }; -// filesize — convenience: returns the byte length of an open fd by -// seeking to the end and back. -1 on error. -export fn filesize(fd: i32) i64 = { +// oserror — the underlying errno from a failed syscall, as a +// negative i64 (Linux's int convention; e.g. -2 = ENOENT). The +// NAMED-i64 alias makes it a distinct variant tag from a "good" +// i64 byte count. Hare's analogue is errors::errno. +export type oserror = i64; + +// filesize — byte length of an open fd via lseek-to-end-and-back. +export fn filesize(fd: i32) (i64 | oserror) = { let end: i64 = lseek(fd, 0i64, SEEK_END); - if (end < 0) { return -1i64; }; + if (end < 0) { return end: oserror; }; let r: i64 = lseek(fd, 0i64, SEEK_SET); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; return end; }; // readall — keep reading until `n` bytes have arrived or the fd -// closes early. Returns bytes read (0..=n) or -1 on read error. -// Hare name (io::readall); the buffer is caller-supplied, matching -// the Plan 9 subset convention. -export fn readall(fd: i32, buf: *u8, n: u64) i64 = { +// closes early. Hare name (io::readall); the buffer is caller- +// supplied, matching the Plan 9 subset convention. +export fn readall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let got: u64 = 0u64; for (got < n) { let r: i64 = read(fd, buf + got, n - got); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return got: i64; }; // short read: caller decides got += r: u64; }; @@ -136,13 +140,12 @@ export fn readall(fd: i32, buf: *u8, n: u64) i64 = { }; // writeall — keep writing until `n` bytes have been accepted or the -// fd refuses progress. Returns bytes written or -1. Hare name -// (io::writeall). -export fn writeall(fd: i32, buf: *u8, n: u64) i64 = { +// fd refuses progress. Hare name (io::writeall). +export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = { let sent: u64 = 0u64; for (sent < n) { let r: i64 = write(fd, buf + sent, n - sent); - if (r < 0) { return -1i64; }; + if (r < 0) { return r: oserror; }; if (r == 0) { return sent: i64; }; sent += r: u64; }; @@ -576,7 +579,11 @@ export fn main() i32 = { case let e: str => return 21; }; let rbuf: [128]u8; - let n: i64 = os.readall(fd, rbuf.ptr, 128u64); + // Use raw os.read here (single syscall, plain i64) instead of + // os.readall: the 990 cgen-match probe compiles smoke.ww + // standalone without `use os;` expansion, so cross-module type + // references like `os.oserror` can't be resolved. + let n: i64 = os.read(fd, rbuf.ptr, 128u64); os.close(fd); if (n <= 0i64) { return 22; }; diff --git a/selfhost/test/smoke.ww b/selfhost/test/smoke.ww index 187f2ce2..bcf0c9ff 100644 --- a/selfhost/test/smoke.ww +++ b/selfhost/test/smoke.ww @@ -159,7 +159,11 @@ export fn main() i32 = { case let e: str => return 21; }; let rbuf: [128]u8; - let n: i64 = os.readall(fd, rbuf.ptr, 128u64); + // Use raw os.read here (single syscall, plain i64) instead of + // os.readall: the 990 cgen-match probe compiles smoke.ww + // standalone without `use os;` expansion, so cross-module type + // references like `os.oserror` can't be resolved. + let n: i64 = os.read(fd, rbuf.ptr, 128u64); os.close(fd); if (n <= 0i64) { return 22; };