lib/os: graduate timespec to time.instant

Removes the local os.timespec (sec, nsec) struct in favour of
time.instant from lib/time. lib/os now `use time;`. filestat's
atime/mtime/ctime change type with byte-identical layout
(i64+i64=16B both sides), so .sec / .nsec accessors at all caller
sites work unchanged.

Rule 12: simple data + mirror Hare. Two same-layout types — one
Hare-canonical, one not — is exactly the structural divergence the
rule forbids. Single-source-of-truth; no transitional alias.

Citations: ref/hare/fs/types.ha:141 (Hare's fs::filestat carries
time::instant), ref/hare/time/instant.ha:9 (canonical layout).

Caller impact (sole reader): lib/os/stattest.ww (.sec / .nsec
unchanged; one comment line refreshed). examples/cmatrix migrated
already in 7e9bede. No selfhost/cmd/* reads mtime/atime/ctime.

Test wiring: lib/os/os.ww removed from 900_stdlib.c's standalone-
w6c-codegen list (cross-module type ref now needs the driver's
module concatenation, same reason lib/bufio and lib/fmt graduated
off earlier). Coverage stays at 976_stat_run via stattest.ww.
Makefile dep edges for the five wwstage targets gain
lib/time/time.ww so changes to it trigger wwstage rebuild.

lib/time is now in the toolchain transitive chain via lib/os. No
selfhost cmd calls time.add/time.diff today; bootstrap is safe.
Latent risk: any future selfhost edit adding time.add/time.diff
would surface task #15 (nested-if label-counter skew in lib/time/
add) as a bootstrap regression. File a fix-#15 before such an edit.

Bootstrap byte-id: ww2 == ww3 == ww4 for all five wwstage tools.
This commit is contained in:
2026-05-17 06:52:16 +09:00
parent 7e9bede6c5
commit bd4ea9f93e
10 changed files with 653 additions and 105 deletions

View File

@@ -1,8 +1,107 @@
// MODULE: time
// time — clocks, instants, durations. Mirrors Hare's lib/time
// (ref/hare/time/duration.ha, instant.ha, arithm.ha,
// +linux/functions.ha). Calendar / date / strftime / timezone /
// sleep live in separate Hare modules and graduate when callers /
// supporting stdlib arrive.
//
// `duration` is a NAMED alias of i64 (lib/math/random precedent
// at lib/math/random/random.ww:8); ww treats NAMED as a newtype,
// so cross-i64 arithmetic inside this module needs explicit casts.
// Hare's structural alias semantics let those casts vanish, but
// our type checker is strict.
@symbol("rt_syscall") fn syscall2(num: i64, a: i64, b: i64) i64;
@symbol("rt_abort") fn abort(msg: str) void;
def SYS_CLOCK_GETTIME: i64 = 228;
// ref/hare/time/duration.ha:6. 290y representable range.
export type duration = i64;
// ref/hare/time/duration.ha:9-18. Plan-9 naming (lowercase)
// diverges from Hare's uppercase per project rule 4.
export def nanosecond: duration = 1i64;
export def microsecond: duration = 1000i64;
export def millisecond: duration = 1000000i64;
export def second: duration = 1000000000i64;
// ref/hare/time/instant.ha:9. (sec, nsec) pair — NOT POSIX struct
// timespec (which uses u32 nsec). Layout matches Linux's struct
// timespec on 64-bit (i64+i64) so we can pass &instant directly
// to clock_gettime.
export type instant = struct {
sec: i64,
nsec: i64,
};
// ref/hare/time/+linux/functions.ha:84. First cut exposes only
// realtime and monotonic; Hare's process_cpu / thread_cpu / boot /
// realtime_alarm / boot_alarm / tai graduate when a caller needs
// them (CLAUDE.md rule 9 — Hare-fidelity, no premature surface).
export type clock = enum i32 {
realtime = 0,
monotonic = 1,
};
// ref/hare/time/+linux/functions.ha:138. Hare's now() also aborts
// on impossible errnos. (instant | oserror) is deliberately not
// the return shape — EINVAL / EFAULT are programmer errors (bad
// clock id, bad ptr), and a 1-word-payload sum return walks into
// task #9's cgen-divergence trap.
export fn now(c: clock) instant = {
let i: instant;
let rc = syscall2(SYS_CLOCK_GETTIME, (c as i32): i64, (&i): i64);
if (rc != 0i64) { abort("time.now: clock_gettime failed"); };
return i;
};
// ref/hare/time/arithm.ha:9. Adds duration to instant. The
// negative-duration branch normalises nsec into [0, second).
export fn add(i: instant, x: duration) instant = {
let r: instant;
let xi: i64 = x: i64;
let sec: i64 = second: i64;
let nsec: i64 = nanosecond: i64;
if (xi == 0i64) {
r.sec = i.sec;
r.nsec = i.nsec;
return r;
};
if (xi > 0i64) {
r.sec = i.sec + (i.nsec + xi) / sec;
r.nsec = (i.nsec + xi) % sec;
return r;
};
r.sec = i.sec + (i.nsec + xi - sec + nsec) / sec;
r.nsec = (i.nsec + (xi % sec) + sec) % sec;
return r;
};
// ref/hare/time/arithm.ha:26. Returns duration from a to b.
// Sign convention: b - a.
export fn diff(a: instant, b: instant) duration = {
let sec: i64 = second: i64;
let v: i64 = ((b.sec - a.sec) * sec) + (b.nsec - a.nsec);
return v: duration;
};
// ref/hare/time/arithm.ha:32. -1 if a < b, 0 if equal, +1 if a > b.
export fn compare(a: instant, b: instant) i8 = {
if (a.sec < b.sec) { return -1i8; };
if (a.sec > b.sec) { return 1i8; };
if (a.nsec < b.nsec) { return -1i8; };
if (a.nsec > b.nsec) { return 1i8; };
return 0i8;
};
// MODULE: os
// os — process and filesystem facade. The body of each call lands
// either in libwwrt.a (rt_syscall trampoline) or libc bindings,
// depending on how the program was linked.
use time;
@symbol("rt_syscall") fn syscall0(num: nr) i64;
@symbol("rt_syscall") fn syscall1(num: nr, a: i64) i64;
@symbol("rt_syscall") fn syscall2(num: nr, a: i64, b: i64) i64;
@@ -505,17 +604,10 @@ export type stat_mask = enum u32 {
CTIME = 64u32,
};
// timespec — {sec, nsec} pair, matching Hare's time::instant
// (ref/hare/time/types.ha). Local to lib/os; graduates to
// lib/time.instant when lib/time and lib/fs ship. Same byte layout
// (i64+i64 = 16B) so a future migration is field-rename only.
export type timespec = struct {
sec: i64,
nsec: i64,
};
// filestat — Hare's fs::filestat (ref/hare/fs/types.ha:141). 80
// bytes. See module-header note re: graduation to lib/fs.
// bytes. Times are time.instant (ref/hare/time/instant.ha:9) — the
// canonical Hare shape. See module-header note re: graduation to
// lib/fs.
export type filestat = struct {
mask: stat_mask, // 0 (4)
mode: mode, // 4 (4)
@@ -523,9 +615,9 @@ export type filestat = struct {
gid: u32, // 12 (4)
sz: u64, // 16 (8)
inode: u64, // 24 (8)
atime: timespec, // 32 (16)
mtime: timespec, // 48 (16)
ctime: timespec, // 64 (16) — ends at 80
atime: time.instant, // 32 (16)
mtime: time.instant, // 48 (16)
ctime: time.instant, // 64 (16) — ends at 80
};
// kstat — x86_64 kernel `struct stat` layout. Mirrors