os: graduate O_*, SEEK_* defs to flag and whence enums

Mirrors Hare's `fs::flag` and `io::whence`:

    export type flag = enum i32 {
            RDONLY  = 0,
            WRONLY  = 1,
            RDWR    = 2,
            CREATE  = 64,    // 0o100
            TRUNC   = 512,   // 0o1000
    };

    export type whence = enum i32 { SET = 0, CUR = 1, END = 2 };

open/tryopen/lseek signatures take the enum types (`flags: flag`,
`w: whence`) so callers get type-checked: `os.open(p, os.flag.RDONLY,
0)` is the correct shape, and `os.flag.WRONLY | os.flag.CREATE |
os.flag.TRUNC` typechecks as a `flag` via the same-named-type rule.

Callers in selfhost/cmd/{ww,w6c,w6a,w6l,wwdump} updated from
`os.O_RDONLY` etc. to `os.flag.RDONLY`. SYS_* syscall numbers kept
as `def` for now (internal-only, ABI surface, no Hare analogue in
this scope).

selfhost/test/smoke.ww keeps its standalone-compile property by
using a numeric literal (`0`, RDONLY's value) for the open flags
arg — probe 6 in 990_selfhost compiles smoke.ww with no `use`
expansion, so cross-module type refs like `os.flag.RDONLY` can't
resolve there. Untyped 0 → flag via type_isnum.
This commit is contained in:
2026-05-12 04:41:50 +09:00
parent 5149d10618
commit b9443b1f33
16 changed files with 202 additions and 154 deletions

View File

@@ -34,17 +34,23 @@ def SYS_UNLINK: i64 = 87;
def SYS_GETCWD: i64 = 79;
def SYS_GETDENTS64: i64 = 217;
// open(2) flags. Linux values, matching <fcntl.h>.
def O_RDONLY: i32 = 0;
def O_WRONLY: i32 = 1;
def O_RDWR: i32 = 2;
def O_CREAT: i32 = 64; // 0x40
def O_TRUNC: i32 = 512; // 0x200
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say
// `os.flag.RDONLY` and `os.flag.WRONLY | os.flag.CREATE`.
export type flag = enum i32 {
RDONLY = 0,
WRONLY = 1,
RDWR = 2,
CREATE = 64, // 0x40
TRUNC = 512, // 0x200
};
// lseek(2) whence.
def SEEK_SET: i32 = 0;
def SEEK_CUR: i32 = 1;
def SEEK_END: i32 = 2;
// lseek(2) whence. Hare names it `io::whence`.
export type whence = enum i32 {
SET = 0,
CUR = 1,
END = 2,
};
export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64);
@@ -92,11 +98,11 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// `str` must ensure the bytes are followed by a 0 byte (literals are,
// arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: i32, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, flags: i64, mode: i64): i32;
export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
};
export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
let fd: i32 = open(path, flags, mode);
if (fd < 0) { return fd: i64: oserror; };
return fd;
@@ -105,8 +111,8 @@ export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
// lseek — set/inspect the fd's position. Returns the new offset or
// a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, whence: i32) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, whence: i64);
export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64);
};
// oserror — the underlying errno from a failed syscall, as a
@@ -118,9 +124,9 @@ 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);
let end: i64 = lseek(fd, 0i64, whence.END);
if (end < 0) { return end: oserror; };
let r: i64 = lseek(fd, 0i64, SEEK_SET);
let r: i64 = lseek(fd, 0i64, whence.SET);
if (r < 0) { return r: oserror; };
return end;
};

View File

@@ -35,17 +35,23 @@ def SYS_UNLINK: i64 = 87;
def SYS_GETCWD: i64 = 79;
def SYS_GETDENTS64: i64 = 217;
// open(2) flags. Linux values, matching <fcntl.h>.
def O_RDONLY: i32 = 0;
def O_WRONLY: i32 = 1;
def O_RDWR: i32 = 2;
def O_CREAT: i32 = 64; // 0x40
def O_TRUNC: i32 = 512; // 0x200
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say
// `os.flag.RDONLY` and `os.flag.WRONLY | os.flag.CREATE`.
export type flag = enum i32 {
RDONLY = 0,
WRONLY = 1,
RDWR = 2,
CREATE = 64, // 0x40
TRUNC = 512, // 0x200
};
// lseek(2) whence.
def SEEK_SET: i32 = 0;
def SEEK_CUR: i32 = 1;
def SEEK_END: i32 = 2;
// lseek(2) whence. Hare names it `io::whence`.
export type whence = enum i32 {
SET = 0,
CUR = 1,
END = 2,
};
export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64);
@@ -93,11 +99,11 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// `str` must ensure the bytes are followed by a 0 byte (literals are,
// arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: i32, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, flags: i64, mode: i64): i32;
export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
};
export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
let fd: i32 = open(path, flags, mode);
if (fd < 0) { return fd: i64: oserror; };
return fd;
@@ -106,8 +112,8 @@ export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
// lseek — set/inspect the fd's position. Returns the new offset or
// a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, whence: i32) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, whence: i64);
export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64);
};
// oserror — the underlying errno from a failed syscall, as a
@@ -119,9 +125,9 @@ 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);
let end: i64 = lseek(fd, 0i64, whence.END);
if (end < 0) { return end: oserror; };
let r: i64 = lseek(fd, 0i64, SEEK_SET);
let r: i64 = lseek(fd, 0i64, whence.SET);
if (r < 0) { return r: oserror; };
return end;
};
@@ -2189,7 +2195,7 @@ fn cstrlen(p: *u8) u64 = {
// Slurp the whole file into a fresh buffer.
fn slurp(path: *u8) (*u8, u64) = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
let szr: (i64 | os.oserror) = os.filesize(fd);
let n: i64 = 0i64;
@@ -2265,7 +2271,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
if (encode(&asm) != 0) { return 1; };
// Open output for write.
let fd: i32 = os.open(out, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 420i32); // 0o644
let fd: i32 = os.open(out, os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
if (fd < 0) {
os.write(2, "w6a: cannot open output\n".ptr, 23u64);
return 1;

View File

@@ -32,7 +32,7 @@ fn cstrlen(p: *u8) u64 = {
// Slurp the whole file into a fresh buffer.
fn slurp(path: *u8) (*u8, u64) = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
let szr: (i64 | os.oserror) = os.filesize(fd);
let n: i64 = 0i64;
@@ -108,7 +108,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
if (encode(&asm) != 0) { return 1; };
// Open output for write.
let fd: i32 = os.open(out, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 420i32); // 0o644
let fd: i32 = os.open(out, os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
if (fd < 0) {
os.write(2, "w6a: cannot open output\n".ptr, 23u64);
return 1;

View File

@@ -35,17 +35,23 @@ def SYS_UNLINK: i64 = 87;
def SYS_GETCWD: i64 = 79;
def SYS_GETDENTS64: i64 = 217;
// open(2) flags. Linux values, matching <fcntl.h>.
def O_RDONLY: i32 = 0;
def O_WRONLY: i32 = 1;
def O_RDWR: i32 = 2;
def O_CREAT: i32 = 64; // 0x40
def O_TRUNC: i32 = 512; // 0x200
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say
// `os.flag.RDONLY` and `os.flag.WRONLY | os.flag.CREATE`.
export type flag = enum i32 {
RDONLY = 0,
WRONLY = 1,
RDWR = 2,
CREATE = 64, // 0x40
TRUNC = 512, // 0x200
};
// lseek(2) whence.
def SEEK_SET: i32 = 0;
def SEEK_CUR: i32 = 1;
def SEEK_END: i32 = 2;
// lseek(2) whence. Hare names it `io::whence`.
export type whence = enum i32 {
SET = 0,
CUR = 1,
END = 2,
};
export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64);
@@ -93,11 +99,11 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// `str` must ensure the bytes are followed by a 0 byte (literals are,
// arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: i32, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, flags: i64, mode: i64): i32;
export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
};
export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
let fd: i32 = open(path, flags, mode);
if (fd < 0) { return fd: i64: oserror; };
return fd;
@@ -106,8 +112,8 @@ export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
// lseek — set/inspect the fd's position. Returns the new offset or
// a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, whence: i32) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, whence: i64);
export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64);
};
// oserror — the underlying errno from a failed syscall, as a
@@ -119,9 +125,9 @@ 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);
let end: i64 = lseek(fd, 0i64, whence.END);
if (end < 0) { return end: oserror; };
let r: i64 = lseek(fd, 0i64, SEEK_SET);
let r: i64 = lseek(fd, 0i64, whence.SET);
if (r < 0) { return r: oserror; };
return end;
};
@@ -9265,7 +9271,7 @@ fn cstrlen(p: *u8) u64 = {
};
fn slurp(path: *u8) (*u8, u64) = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
let szr: (i64 | os.oserror) = os.filesize(fd);
let n: i64 = 0i64;
@@ -9332,7 +9338,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
// threading a file descriptor through the emit helpers.
if (out != nil) {
let ofd: i32 = os.open(out,
os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 420i32); // 0o644
os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
if (ofd < 0) {
os.write(2, "w6c: cannot open output\n".ptr, 23u64);
return 1;

View File

@@ -40,7 +40,7 @@ fn cstrlen(p: *u8) u64 = {
};
fn slurp(path: *u8) (*u8, u64) = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
let szr: (i64 | os.oserror) = os.filesize(fd);
let n: i64 = 0i64;
@@ -107,7 +107,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
// threading a file descriptor through the emit helpers.
if (out != nil) {
let ofd: i32 = os.open(out,
os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 420i32); // 0o644
os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
if (ofd < 0) {
os.write(2, "w6c: cannot open output\n".ptr, 23u64);
return 1;

View File

@@ -124,7 +124,7 @@ fn dbasename(p: *u8) *u8 = {
// ---- file slurp --------------------------------------------------------
fn slurpso(path: *u8) (*u8, u64) = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
let szr: (i64 | os.oserror) = os.filesize(fd);
let n: i64 = 0i64;

View File

@@ -35,17 +35,23 @@ def SYS_UNLINK: i64 = 87;
def SYS_GETCWD: i64 = 79;
def SYS_GETDENTS64: i64 = 217;
// open(2) flags. Linux values, matching <fcntl.h>.
def O_RDONLY: i32 = 0;
def O_WRONLY: i32 = 1;
def O_RDWR: i32 = 2;
def O_CREAT: i32 = 64; // 0x40
def O_TRUNC: i32 = 512; // 0x200
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say
// `os.flag.RDONLY` and `os.flag.WRONLY | os.flag.CREATE`.
export type flag = enum i32 {
RDONLY = 0,
WRONLY = 1,
RDWR = 2,
CREATE = 64, // 0x40
TRUNC = 512, // 0x200
};
// lseek(2) whence.
def SEEK_SET: i32 = 0;
def SEEK_CUR: i32 = 1;
def SEEK_END: i32 = 2;
// lseek(2) whence. Hare names it `io::whence`.
export type whence = enum i32 {
SET = 0,
CUR = 1,
END = 2,
};
export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64);
@@ -93,11 +99,11 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// `str` must ensure the bytes are followed by a 0 byte (literals are,
// arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: i32, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, flags: i64, mode: i64): i32;
export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
};
export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
let fd: i32 = open(path, flags, mode);
if (fd < 0) { return fd: i64: oserror; };
return fd;
@@ -106,8 +112,8 @@ export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
// lseek — set/inspect the fd's position. Returns the new offset or
// a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, whence: i32) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, whence: i64);
export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64);
};
// oserror — the underlying errno from a failed syscall, as a
@@ -119,9 +125,9 @@ 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);
let end: i64 = lseek(fd, 0i64, whence.END);
if (end < 0) { return end: oserror; };
let r: i64 = lseek(fd, 0i64, SEEK_SET);
let r: i64 = lseek(fd, 0i64, whence.SET);
if (r < 0) { return r: oserror; };
return end;
};
@@ -505,7 +511,7 @@ def RELA_ADDEND: u64 = 16u64;
// ---- file slurp --------------------------------------------------------
fn slurp(path: *u8) (*u8, u64) = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
let szr: (i64 | os.oserror) = os.filesize(fd);
let n: i64 = 0i64;
@@ -1050,7 +1056,7 @@ fn dbasename(p: *u8) *u8 = {
// ---- file slurp --------------------------------------------------------
fn slurpso(path: *u8) (*u8, u64) = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
let szr: (i64 | os.oserror) = os.filesize(fd);
let n: i64 = 0i64;
@@ -2393,7 +2399,7 @@ fn buildpathv(dst: *u8, dir: *u8, name: *u8, v: u64) u64 = {
// islinkable: read first 8 bytes; require !<arch>\n or \x7fELF.
fn islinkable(path: *u8) bool = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return false; };
let mp: *u8 = os.alloc(8u64): *u8;
let n: i64 = os.read(fd, mp, 8u64);
@@ -2462,7 +2468,7 @@ fn resolvelib(a: *arena, name: *u8, libdirs: **u8, nlibdirs: i32) *u8 = {
// Read first 20 bytes; return 1 for ET_DYN .so, 0 for ar/.o.
fn isso(path: *u8) i32 = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return 0; };
let mp: *u8 = os.alloc(20u64): *u8;
let n: i64 = os.read(fd, mp, 20u64);
@@ -2606,7 +2612,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
return 1;
};
let flags: i32 = os.O_WRONLY | os.O_CREAT | os.O_TRUNC;
let flags: os.flag = os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC;
let fd: i32 = os.open(outpath, flags, 493i32); // 0o755
if (fd < 0) {
os.write(2, "w6l: cannot open output\n".ptr, 23u64);

View File

@@ -99,7 +99,7 @@ fn buildpathv(dst: *u8, dir: *u8, name: *u8, v: u64) u64 = {
// islinkable: read first 8 bytes; require !<arch>\n or \x7fELF.
fn islinkable(path: *u8) bool = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return false; };
let mp: *u8 = os.alloc(8u64): *u8;
let n: i64 = os.read(fd, mp, 8u64);
@@ -168,7 +168,7 @@ fn resolvelib(a: *arena, name: *u8, libdirs: **u8, nlibdirs: i32) *u8 = {
// Read first 20 bytes; return 1 for ET_DYN .so, 0 for ar/.o.
fn isso(path: *u8) i32 = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return 0; };
let mp: *u8 = os.alloc(20u64): *u8;
let n: i64 = os.read(fd, mp, 20u64);
@@ -312,7 +312,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
return 1;
};
let flags: i32 = os.O_WRONLY | os.O_CREAT | os.O_TRUNC;
let flags: os.flag = os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC;
let fd: i32 = os.open(outpath, flags, 493i32); // 0o755
if (fd < 0) {
os.write(2, "w6l: cannot open output\n".ptr, 23u64);

View File

@@ -76,7 +76,7 @@ def RELA_ADDEND: u64 = 16u64;
// ---- file slurp --------------------------------------------------------
fn slurp(path: *u8) (*u8, u64) = {
let fd: i32 = os.open(path, os.O_RDONLY, 0i32);
let fd: i32 = os.open(path, os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
let szr: (i64 | os.oserror) = os.filesize(fd);
let n: i64 = 0i64;

View File

@@ -35,17 +35,23 @@ def SYS_UNLINK: i64 = 87;
def SYS_GETCWD: i64 = 79;
def SYS_GETDENTS64: i64 = 217;
// open(2) flags. Linux values, matching <fcntl.h>.
def O_RDONLY: i32 = 0;
def O_WRONLY: i32 = 1;
def O_RDWR: i32 = 2;
def O_CREAT: i32 = 64; // 0x40
def O_TRUNC: i32 = 512; // 0x200
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say
// `os.flag.RDONLY` and `os.flag.WRONLY | os.flag.CREATE`.
export type flag = enum i32 {
RDONLY = 0,
WRONLY = 1,
RDWR = 2,
CREATE = 64, // 0x40
TRUNC = 512, // 0x200
};
// lseek(2) whence.
def SEEK_SET: i32 = 0;
def SEEK_CUR: i32 = 1;
def SEEK_END: i32 = 2;
// lseek(2) whence. Hare names it `io::whence`.
export type whence = enum i32 {
SET = 0,
CUR = 1,
END = 2,
};
export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64);
@@ -93,11 +99,11 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// `str` must ensure the bytes are followed by a 0 byte (literals are,
// arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: i32, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, flags: i64, mode: i64): i32;
export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
};
export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
let fd: i32 = open(path, flags, mode);
if (fd < 0) { return fd: i64: oserror; };
return fd;
@@ -106,8 +112,8 @@ export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
// lseek — set/inspect the fd's position. Returns the new offset or
// a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, whence: i32) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, whence: i64);
export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64);
};
// oserror — the underlying errno from a failed syscall, as a
@@ -119,9 +125,9 @@ 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);
let end: i64 = lseek(fd, 0i64, whence.END);
if (end < 0) { return end: oserror; };
let r: i64 = lseek(fd, 0i64, SEEK_SET);
let r: i64 = lseek(fd, 0i64, whence.SET);
if (r < 0) { return r: oserror; };
return end;
};
@@ -607,7 +613,7 @@ fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64) *u8 = {
// ---- file slurp -------------------------------------------------------
fn slurp(pathcs: *u8) (*u8, u64) = {
let fd: i32 = os.open(pathcs, os.O_RDONLY, 0i32);
let fd: i32 = os.open(pathcs, os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
let szr: (i64 | os.oserror) = os.filesize(fd);
let n: i64 = 0i64;
@@ -862,7 +868,7 @@ fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
};
// Step 1: expand `use`s into the combined file.
let cf: i32 = os.open(combined, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 420i32); // 0o644
let cf: i32 = os.open(combined, os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
if (cf < 0) {
os.write(2, "ww: cannot open combined\n".ptr, 25u64);
return 1;
@@ -1402,7 +1408,7 @@ fn runsingletest(selfdir: *u8, src: *u8) i32 = {
};
fn rundirtests(selfdir: *u8, dir: *u8) i32 = {
let fd: i32 = os.open(dir, os.O_RDONLY, 0i32);
let fd: i32 = os.open(dir, os.flag.RDONLY, 0i32);
if (fd < 0) {
os.write(2, "ww test: cannot open directory\n".ptr, 31u64);
return 1;

View File

@@ -281,7 +281,7 @@ fn locateimport(a: *arena, dirs: *u8, name: *u8, namelen: u64) *u8 = {
// ---- file slurp -------------------------------------------------------
fn slurp(pathcs: *u8) (*u8, u64) = {
let fd: i32 = os.open(pathcs, os.O_RDONLY, 0i32);
let fd: i32 = os.open(pathcs, os.flag.RDONLY, 0i32);
if (fd < 0) { return nil, 0u64; };
let szr: (i64 | os.oserror) = os.filesize(fd);
let n: i64 = 0i64;
@@ -536,7 +536,7 @@ fn buildone(selfdir: *u8, src: *u8, out: *u8, incs: *u8, lf: *lflags) i32 = {
};
// Step 1: expand `use`s into the combined file.
let cf: i32 = os.open(combined, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 420i32); // 0o644
let cf: i32 = os.open(combined, os.flag.WRONLY | os.flag.CREATE | os.flag.TRUNC, 420i32); // 0o644
if (cf < 0) {
os.write(2, "ww: cannot open combined\n".ptr, 25u64);
return 1;
@@ -1076,7 +1076,7 @@ fn runsingletest(selfdir: *u8, src: *u8) i32 = {
};
fn rundirtests(selfdir: *u8, dir: *u8) i32 = {
let fd: i32 = os.open(dir, os.O_RDONLY, 0i32);
let fd: i32 = os.open(dir, os.flag.RDONLY, 0i32);
if (fd < 0) {
os.write(2, "ww test: cannot open directory\n".ptr, 31u64);
return 1;

View File

@@ -35,17 +35,23 @@ def SYS_UNLINK: i64 = 87;
def SYS_GETCWD: i64 = 79;
def SYS_GETDENTS64: i64 = 217;
// open(2) flags. Linux values, matching <fcntl.h>.
def O_RDONLY: i32 = 0;
def O_WRONLY: i32 = 1;
def O_RDWR: i32 = 2;
def O_CREAT: i32 = 64; // 0x40
def O_TRUNC: i32 = 512; // 0x200
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say
// `os.flag.RDONLY` and `os.flag.WRONLY | os.flag.CREATE`.
export type flag = enum i32 {
RDONLY = 0,
WRONLY = 1,
RDWR = 2,
CREATE = 64, // 0x40
TRUNC = 512, // 0x200
};
// lseek(2) whence.
def SEEK_SET: i32 = 0;
def SEEK_CUR: i32 = 1;
def SEEK_END: i32 = 2;
// lseek(2) whence. Hare names it `io::whence`.
export type whence = enum i32 {
SET = 0,
CUR = 1,
END = 2,
};
export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64);
@@ -93,11 +99,11 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// `str` must ensure the bytes are followed by a 0 byte (literals are,
// arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: i32, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, flags: i64, mode: i64): i32;
export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
};
export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
let fd: i32 = open(path, flags, mode);
if (fd < 0) { return fd: i64: oserror; };
return fd;
@@ -106,8 +112,8 @@ export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
// lseek — set/inspect the fd's position. Returns the new offset or
// a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, whence: i32) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, whence: i64);
export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64);
};
// oserror — the underlying errno from a failed syscall, as a
@@ -119,9 +125,9 @@ 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);
let end: i64 = lseek(fd, 0i64, whence.END);
if (end < 0) { return end: oserror; };
let r: i64 = lseek(fd, 0i64, SEEK_SET);
let r: i64 = lseek(fd, 0i64, whence.SET);
if (r < 0) { return r: oserror; };
return end;
};
@@ -9299,7 +9305,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
return 2;
};
let fdorerr: (i32 | os.oserror) = os.tryopen(path, os.O_RDONLY, 0i32);
let fdorerr: (i32 | os.oserror) = os.tryopen(path, os.flag.RDONLY, 0i32);
let fd: i32 = -1;
match (fdorerr) {
case let v: i32 => fd = v;

View File

@@ -74,7 +74,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
return 2;
};
let fdorerr: (i32 | os.oserror) = os.tryopen(path, os.O_RDONLY, 0i32);
let fdorerr: (i32 | os.oserror) = os.tryopen(path, os.flag.RDONLY, 0i32);
let fd: i32 = -1;
match (fdorerr) {
case let v: i32 => fd = v;

View File

@@ -35,17 +35,23 @@ def SYS_UNLINK: i64 = 87;
def SYS_GETCWD: i64 = 79;
def SYS_GETDENTS64: i64 = 217;
// open(2) flags. Linux values, matching <fcntl.h>.
def O_RDONLY: i32 = 0;
def O_WRONLY: i32 = 1;
def O_RDWR: i32 = 2;
def O_CREAT: i32 = 64; // 0x40
def O_TRUNC: i32 = 512; // 0x200
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say
// `os.flag.RDONLY` and `os.flag.WRONLY | os.flag.CREATE`.
export type flag = enum i32 {
RDONLY = 0,
WRONLY = 1,
RDWR = 2,
CREATE = 64, // 0x40
TRUNC = 512, // 0x200
};
// lseek(2) whence.
def SEEK_SET: i32 = 0;
def SEEK_CUR: i32 = 1;
def SEEK_END: i32 = 2;
// lseek(2) whence. Hare names it `io::whence`.
export type whence = enum i32 {
SET = 0,
CUR = 1,
END = 2,
};
export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64);
@@ -93,11 +99,11 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// `str` must ensure the bytes are followed by a 0 byte (literals are,
// arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: i32, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, flags: i64, mode: i64): i32;
export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
};
export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
let fd: i32 = open(path, flags, mode);
if (fd < 0) { return fd: i64: oserror; };
return fd;
@@ -106,8 +112,8 @@ export fn tryopen(path: *u8, flags: i32, mode: i32) (i32 | oserror) = {
// lseek — set/inspect the fd's position. Returns the new offset or
// a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, whence: i32) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, whence: i64);
export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64);
};
// oserror — the underlying errno from a failed syscall, as a
@@ -119,9 +125,9 @@ 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);
let end: i64 = lseek(fd, 0i64, whence.END);
if (end < 0) { return end: oserror; };
let r: i64 = lseek(fd, 0i64, SEEK_SET);
let r: i64 = lseek(fd, 0i64, whence.SET);
if (r < 0) { return r: oserror; };
return end;
};
@@ -576,8 +582,11 @@ export fn main() i32 = {
// Use raw os.open here (returns i32 with -errno) for the same
// reason as os.read below: probe 6 in 990_selfhost compiles
// smoke.ww standalone (no `use` expansion), so cross-module type
// references like `os.oserror` don't resolve at that step.
let fd: i32 = os.open(path.ptr, os.O_RDONLY, 0i32);
// references like `os.oserror` and `os.flag` don't resolve at
// that step. RDONLY is 0; passing the literal keeps the call
// site standalone-compilable to byte-identical asm on both
// compilers.
let fd: i32 = os.open(path.ptr, 0, 0i32);
if (fd < 0) { return 21; };
let rbuf: [128]u8;
// Use raw os.read here (single syscall, plain i64) instead of

View File

@@ -155,8 +155,11 @@ export fn main() i32 = {
// Use raw os.open here (returns i32 with -errno) for the same
// reason as os.read below: probe 6 in 990_selfhost compiles
// smoke.ww standalone (no `use` expansion), so cross-module type
// references like `os.oserror` don't resolve at that step.
let fd: i32 = os.open(path.ptr, os.O_RDONLY, 0i32);
// references like `os.oserror` and `os.flag` don't resolve at
// that step. RDONLY is 0; passing the literal keeps the call
// site standalone-compilable to byte-identical asm on both
// compilers.
let fd: i32 = os.open(path.ptr, 0, 0i32);
if (fd < 0) { return 21; };
let rbuf: [128]u8;
// Use raw os.read here (single syscall, plain i64) instead of

View File

@@ -1100,13 +1100,13 @@ static const struct row rows[] = {
" let m: mode = mode.R | mode.W;\n"
" return m as i32;\n"
"};", 3 },
/* enum: pkg-qualified access — `os.whence.CUR` resolves through
/* enum: pkg-qualified access — `pkg.dir.SOUTH` resolves through
* SK_USE and folds to the member literal. */
{ "// MODULE: os\n"
"type whence = enum { SET, CUR, END };\n"
{ "// MODULE: pkg\n"
"type dir = enum { NORTH, SOUTH, EAST, WEST };\n"
"// MODULE: main\n"
"use os;\n"
"fn main() i32 = { return os.whence.CUR as i32; };", 1 },
"use pkg;\n"
"fn main() i32 = { return pkg.dir.SOUTH as i32; };", 1 },
{ NULL, 0 }
};