lib/types,wcc/ww: export the limit constants (#48)

lib/types limit consts were bare `def`s, so the .wwi (sep-compile's
interface) correctly omitted them while the flat combined.ww let a
cross-package user (lib/strings splitn → types.I32_MAX) reach the
private def — sep-compile then failed (wwstage `asserttyped: dot
'I32_MAX'`; cstage undefined-ref). Hare exports types::I32_MAX and the
whole limit family (ref/hare/types/limits.ha, arch+x86_64.ha); ww not
exporting them was the divergence.

export the 24 existing limit defs ({I,U}{8,16,32,64}_{MIN,MAX},
INT/UINT/SIZE/UINTPTR_{MIN,MAX}) and the existing RUNE_MIN, and add
exported RUNE_MAX. ww's derived machine-word int/uint/size/uintptr
VALUES are kept verbatim (user-ratified 64-bit-int divergence); fidelity
here is the NAME SET + export-visibility, not the values. RUNE_MAX is
written `0x10ffff: rune` — same codepoint as Hare's '\U0010ffff', forced
because ww's lexer has no \u/\U escape (#50).

Exporting the consts made `w6c -I` walk them and fatal on RUNE_MIN
('\0'): the .wwi const-expr unparser had no N_RUNELIT arm. Add one,
both stages (wwi_rune / wwirune), rendering a \xHH-escaped rune literal
(>0xFF fails loud, #50). Const casts need no arm — the checker folds
them to integer literals before the producer runs. 989_m2wwi_run gains
a types.wwi gate (byte-id + re-parse + asserts export def I32_MAX and
RUNE_MAX reach the interface). byte-id-neutral: a def emits no symbol.
This commit is contained in:
2026-06-15 23:06:11 +09:00
parent 4622556c62
commit f69ef9b9da
10 changed files with 384 additions and 177 deletions

View File

@@ -201,6 +201,44 @@ fn wwicheckdecl(c: *checker, d: *node) i32 = {
// --- type-expr + const-expr unparser (rob §2.2/§2.4) ------------------
// The lexer's rune escapes stop at \xHH (no \u/\U — task #50), so a
// codepoint above 0xff can't render as a re-parseable rune literal; fail
// loud rather than emit a malformed one. No exported def names such a rune
// today (RUNE_MAX is written as an int-cast for the same reason).
fn wwirune(fd: i32, cp: u64) void = {
if (cp > 255u64) {
wputs(2, "wwi: rune codepoint exceeds \\xHH (task #50)\n");
os.exit(1);
};
let c: u8 = cp: u8;
wputb(fd, '\'');
if (c == '\'') {
wputs(fd, "\\'");
} else { if (c == '\\') {
wputs(fd, "\\\\");
} else { if (c == '\n') {
wputs(fd, "\\n");
} else { if (c == '\t') {
wputs(fd, "\\t");
} else { if (c < 32u8 || c >= 127u8) {
let hi: u8 = c >> 4u8;
let lo: u8 = c & 15u8;
let h: u8 = 0u8;
let l: u8 = 0u8;
if (hi < 10u8) { h = hi + 48u8; } else { h = (hi - 10u8) + 97u8; };
if (lo < 10u8) { l = lo + 48u8; } else { l = (lo - 10u8) + 97u8; };
let buf: [4]u8;
buf[0] = 92u8;
buf[1] = 120u8;
buf[2] = h;
buf[3] = l;
os.write(fd, buf.ptr, 4u64);
} else {
wputb(fd, c);
};};};};};
wputb(fd, '\'');
};
fn wwiexpr(fd: i32, e: *node) void = {
if (e == nil) { return; };
if (e.kind == nkind.N_INTLIT) {
@@ -220,6 +258,12 @@ fn wwiexpr(fd: i32, e: *node) void = {
wputs(fd, "nil");
} else { if (e.kind == nkind.N_STRLIT) {
wquote(fd, e.str);
} else { if (e.kind == nkind.N_RUNELIT) {
// A bare rune literal in a const-expr (types.RUNE_MIN = '\0');
// casts never reach here — the checker folds a const cast to an
// integer literal before the producer runs (RUNE_MAX
// `0x10ffff: rune` emits as the plain int 1114111).
wwirune(fd, e.uval);
} else { if (e.kind == nkind.N_BIN) {
wwiexpr(fd, e.lhs);
wputb(fd, 32u8);
@@ -232,7 +276,7 @@ fn wwiexpr(fd: i32, e: *node) void = {
} else {
wputs(2, "wwi: unhandled const-expr node kind\n");
os.exit(1);
};};};};};};};};};
};};};};};};};};};};
};
fn wwiparam(fd: i32, p: *node) void = {