From f69ef9b9da848a1f89d203d00d083a35ce6b3af3 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Mon, 15 Jun 2026 23:06:11 +0900 Subject: [PATCH] lib/types,wcc/ww: export the limit constants (#48) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- cmd/w6c/wwi.c | 30 ++++++++ lib/types/types.ww | 54 ++++++++------- selfhost/cmd/w6a/main.combined.ww | 54 ++++++++------- selfhost/cmd/w6c/main.combined.ww | 100 ++++++++++++++++++++------- selfhost/cmd/w6l/main.combined.ww | 54 ++++++++------- selfhost/cmd/wcc/wwi.ww | 46 +++++++++++- selfhost/cmd/ww/main.combined.ww | 54 ++++++++------- selfhost/cmd/wwdump/main.combined.ww | 54 ++++++++------- selfhost/test/smoke.combined.ww | 54 ++++++++------- test/wcc/989_m2wwi_run.c | 61 ++++++++++++++++ 10 files changed, 384 insertions(+), 177 deletions(-) diff --git a/cmd/w6c/wwi.c b/cmd/w6c/wwi.c index 797666a7..339b3aa7 100644 --- a/cmd/w6c/wwi.c +++ b/cmd/w6c/wwi.c @@ -164,6 +164,31 @@ wwi_quote(FILE *of, const char *s, u64 n) fputc('"', of); } +static void +wwi_rune(FILE *of, u64 cp) +{ + /* 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). */ + if (cp > 0xff) + fatal("wwi: rune codepoint U+%llx exceeds \\xHH (task #50)", + (unsigned long long)cp); + unsigned char c = (unsigned char)cp; + fputc('\'', of); + switch (c) { + case '\'': fputs("\\'", of); break; + case '\\': fputs("\\\\", of); break; + case '\n': fputs("\\n", of); break; + case '\t': fputs("\\t", of); break; + default: + if (c < 0x20 || c >= 0x7f) fprintf(of, "\\x%02x", c); + else fputc(c, of); + } + fputc('\'', of); +} + static void wwi_param(FILE *of, Node *p) { @@ -296,6 +321,11 @@ wwi_expr(FILE *of, Node *e) case N_FALSE: fputs("false", of); break; case N_NIL: fputs("nil", of); break; case N_STRLIT: wwi_quote(of, e->str, e->strlen); break; + /* 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). */ + case N_RUNELIT: wwi_rune(of, e->uval); break; case N_BIN: wwi_expr(of, e->lhs); fprintf(of, " %s ", tokname(e->op)); diff --git a/lib/types/types.ww b/lib/types/types.ww index 4fa18452..8c8396fa 100644 --- a/lib/types/types.ww +++ b/lib/types/types.ww @@ -4,40 +4,44 @@ package types; -def I8_MAX: i8 = 127; -def I16_MAX: i16 = 32767; -def I32_MAX: i32 = 2147483647; -def I64_MAX: i64 = 9223372036854775807; +export def I8_MAX: i8 = 127; +export def I16_MAX: i16 = 32767; +export def I32_MAX: i32 = 2147483647; +export def I64_MAX: i64 = 9223372036854775807; -def I8_MIN: i8 = -128; -def I16_MIN: i16 = -32768; -def I32_MIN: i32 = -2147483648; -def I64_MIN: i64 = -9223372036854775808; +export def I8_MIN: i8 = -128; +export def I16_MIN: i16 = -32768; +export def I32_MIN: i32 = -2147483648; +export def I64_MIN: i64 = -9223372036854775808; -def U8_MAX: u8 = 255; -def U16_MAX: u16 = 65535; -def U32_MAX: u32 = 4294967295; -def U64_MAX: u64 = 18446744073709551615; +export def U8_MAX: u8 = 255; +export def U16_MAX: u16 = 65535; +export def U32_MAX: u32 = 4294967295; +export def U64_MAX: u64 = 18446744073709551615; -def U8_MIN: u8 = 0; -def U16_MIN: u16 = 0; -def U32_MIN: u32 = 0; -def U64_MIN: u64 = 0; +export def U8_MIN: u8 = 0; +export def U16_MIN: u16 = 0; +export def U32_MIN: u32 = 0; +export def U64_MIN: u64 = 0; // int/uint are machine-word (Go-style, type.c:58); limits derived from // size(int) per #114 + user ruling; cf Go math.MaxInt; diverges from // Hare's per-arch literal (arch+x86_64.ha) because ww's int is 64-bit. -def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; -def INT_MIN: int = -1 << (size(int)*8 - 1); -def UINT_MIN: uint = 0; -def UINT_MAX: uint = ~(0: uint); +export def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; +export def INT_MIN: int = -1 << (size(int)*8 - 1); +export def UINT_MIN: uint = 0; +export def UINT_MAX: uint = ~(0: uint); // size is 8B on amd64; no cast needed (size ∈ unsigned class per #113). -def SIZE_MIN: size = U64_MIN; -def SIZE_MAX: size = U64_MAX; +export def SIZE_MIN: size = U64_MIN; +export def SIZE_MAX: size = U64_MAX; // uintptr not in the unsigned class, so the cast is required (Hare's form). -def UINTPTR_MIN: uintptr = U64_MIN: uintptr; -def UINTPTR_MAX: uintptr = U64_MAX: uintptr; +export def UINTPTR_MIN: uintptr = U64_MIN: uintptr; +export def UINTPTR_MAX: uintptr = U64_MAX: uintptr; -def RUNE_MIN: rune = '\0'; +export def RUNE_MIN: rune = '\0'; +// Hare spells this `'\U0010ffff'` (ref/hare/types/limits.ha:57); ww's lexer +// has no \U/\u escape (cmd/wcc/lex.c escape(): \xHH only, max 0xFF) so the +// codepoint is written as an int-cast — same value, forced spelling. #50. +export def RUNE_MAX: rune = 0x10ffff: rune; diff --git a/selfhost/cmd/w6a/main.combined.ww b/selfhost/cmd/w6a/main.combined.ww index 904ad05f..73e8c0bf 100644 --- a/selfhost/cmd/w6a/main.combined.ww +++ b/selfhost/cmd/w6a/main.combined.ww @@ -866,43 +866,47 @@ export fn exists(path: str) bool = { package types; -def I8_MAX: i8 = 127; -def I16_MAX: i16 = 32767; -def I32_MAX: i32 = 2147483647; -def I64_MAX: i64 = 9223372036854775807; +export def I8_MAX: i8 = 127; +export def I16_MAX: i16 = 32767; +export def I32_MAX: i32 = 2147483647; +export def I64_MAX: i64 = 9223372036854775807; -def I8_MIN: i8 = -128; -def I16_MIN: i16 = -32768; -def I32_MIN: i32 = -2147483648; -def I64_MIN: i64 = -9223372036854775808; +export def I8_MIN: i8 = -128; +export def I16_MIN: i16 = -32768; +export def I32_MIN: i32 = -2147483648; +export def I64_MIN: i64 = -9223372036854775808; -def U8_MAX: u8 = 255; -def U16_MAX: u16 = 65535; -def U32_MAX: u32 = 4294967295; -def U64_MAX: u64 = 18446744073709551615; +export def U8_MAX: u8 = 255; +export def U16_MAX: u16 = 65535; +export def U32_MAX: u32 = 4294967295; +export def U64_MAX: u64 = 18446744073709551615; -def U8_MIN: u8 = 0; -def U16_MIN: u16 = 0; -def U32_MIN: u32 = 0; -def U64_MIN: u64 = 0; +export def U8_MIN: u8 = 0; +export def U16_MIN: u16 = 0; +export def U32_MIN: u32 = 0; +export def U64_MIN: u64 = 0; // int/uint are machine-word (Go-style, type.c:58); limits derived from // size(int) per #114 + user ruling; cf Go math.MaxInt; diverges from // Hare's per-arch literal (arch+x86_64.ha) because ww's int is 64-bit. -def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; -def INT_MIN: int = -1 << (size(int)*8 - 1); -def UINT_MIN: uint = 0; -def UINT_MAX: uint = ~(0: uint); +export def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; +export def INT_MIN: int = -1 << (size(int)*8 - 1); +export def UINT_MIN: uint = 0; +export def UINT_MAX: uint = ~(0: uint); // size is 8B on amd64; no cast needed (size ∈ unsigned class per #113). -def SIZE_MIN: size = U64_MIN; -def SIZE_MAX: size = U64_MAX; +export def SIZE_MIN: size = U64_MIN; +export def SIZE_MAX: size = U64_MAX; // uintptr not in the unsigned class, so the cast is required (Hare's form). -def UINTPTR_MIN: uintptr = U64_MIN: uintptr; -def UINTPTR_MAX: uintptr = U64_MAX: uintptr; +export def UINTPTR_MIN: uintptr = U64_MIN: uintptr; +export def UINTPTR_MAX: uintptr = U64_MAX: uintptr; -def RUNE_MIN: rune = '\0'; +export def RUNE_MIN: rune = '\0'; +// Hare spells this `'\U0010ffff'` (ref/hare/types/limits.ha:57); ww's lexer +// has no \U/\u escape (cmd/wcc/lex.c escape(): \xHH only, max 0xFF) so the +// codepoint is written as an int-cast — same value, forced spelling. #50. +export def RUNE_MAX: rune = 0x10ffff: rune; //ww:module bytes // bytes — slice operations over []u8. Mirrors Hare's bytes module diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index b649a21c..4cbc48bf 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -866,43 +866,47 @@ export fn exists(path: str) bool = { package types; -def I8_MAX: i8 = 127; -def I16_MAX: i16 = 32767; -def I32_MAX: i32 = 2147483647; -def I64_MAX: i64 = 9223372036854775807; +export def I8_MAX: i8 = 127; +export def I16_MAX: i16 = 32767; +export def I32_MAX: i32 = 2147483647; +export def I64_MAX: i64 = 9223372036854775807; -def I8_MIN: i8 = -128; -def I16_MIN: i16 = -32768; -def I32_MIN: i32 = -2147483648; -def I64_MIN: i64 = -9223372036854775808; +export def I8_MIN: i8 = -128; +export def I16_MIN: i16 = -32768; +export def I32_MIN: i32 = -2147483648; +export def I64_MIN: i64 = -9223372036854775808; -def U8_MAX: u8 = 255; -def U16_MAX: u16 = 65535; -def U32_MAX: u32 = 4294967295; -def U64_MAX: u64 = 18446744073709551615; +export def U8_MAX: u8 = 255; +export def U16_MAX: u16 = 65535; +export def U32_MAX: u32 = 4294967295; +export def U64_MAX: u64 = 18446744073709551615; -def U8_MIN: u8 = 0; -def U16_MIN: u16 = 0; -def U32_MIN: u32 = 0; -def U64_MIN: u64 = 0; +export def U8_MIN: u8 = 0; +export def U16_MIN: u16 = 0; +export def U32_MIN: u32 = 0; +export def U64_MIN: u64 = 0; // int/uint are machine-word (Go-style, type.c:58); limits derived from // size(int) per #114 + user ruling; cf Go math.MaxInt; diverges from // Hare's per-arch literal (arch+x86_64.ha) because ww's int is 64-bit. -def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; -def INT_MIN: int = -1 << (size(int)*8 - 1); -def UINT_MIN: uint = 0; -def UINT_MAX: uint = ~(0: uint); +export def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; +export def INT_MIN: int = -1 << (size(int)*8 - 1); +export def UINT_MIN: uint = 0; +export def UINT_MAX: uint = ~(0: uint); // size is 8B on amd64; no cast needed (size ∈ unsigned class per #113). -def SIZE_MIN: size = U64_MIN; -def SIZE_MAX: size = U64_MAX; +export def SIZE_MIN: size = U64_MIN; +export def SIZE_MAX: size = U64_MAX; // uintptr not in the unsigned class, so the cast is required (Hare's form). -def UINTPTR_MIN: uintptr = U64_MIN: uintptr; -def UINTPTR_MAX: uintptr = U64_MAX: uintptr; +export def UINTPTR_MIN: uintptr = U64_MIN: uintptr; +export def UINTPTR_MAX: uintptr = U64_MAX: uintptr; -def RUNE_MIN: rune = '\0'; +export def RUNE_MIN: rune = '\0'; +// Hare spells this `'\U0010ffff'` (ref/hare/types/limits.ha:57); ww's lexer +// has no \U/\u escape (cmd/wcc/lex.c escape(): \xHH only, max 0xFF) so the +// codepoint is written as an int-cast — same value, forced spelling. #50. +export def RUNE_MAX: rune = 0x10ffff: rune; //ww:module bytes // bytes — slice operations over []u8. Mirrors Hare's bytes module @@ -45528,6 +45532,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) { @@ -45547,6 +45589,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); @@ -45559,7 +45607,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 = { diff --git a/selfhost/cmd/w6l/main.combined.ww b/selfhost/cmd/w6l/main.combined.ww index 22297431..3a948c29 100644 --- a/selfhost/cmd/w6l/main.combined.ww +++ b/selfhost/cmd/w6l/main.combined.ww @@ -866,43 +866,47 @@ export fn exists(path: str) bool = { package types; -def I8_MAX: i8 = 127; -def I16_MAX: i16 = 32767; -def I32_MAX: i32 = 2147483647; -def I64_MAX: i64 = 9223372036854775807; +export def I8_MAX: i8 = 127; +export def I16_MAX: i16 = 32767; +export def I32_MAX: i32 = 2147483647; +export def I64_MAX: i64 = 9223372036854775807; -def I8_MIN: i8 = -128; -def I16_MIN: i16 = -32768; -def I32_MIN: i32 = -2147483648; -def I64_MIN: i64 = -9223372036854775808; +export def I8_MIN: i8 = -128; +export def I16_MIN: i16 = -32768; +export def I32_MIN: i32 = -2147483648; +export def I64_MIN: i64 = -9223372036854775808; -def U8_MAX: u8 = 255; -def U16_MAX: u16 = 65535; -def U32_MAX: u32 = 4294967295; -def U64_MAX: u64 = 18446744073709551615; +export def U8_MAX: u8 = 255; +export def U16_MAX: u16 = 65535; +export def U32_MAX: u32 = 4294967295; +export def U64_MAX: u64 = 18446744073709551615; -def U8_MIN: u8 = 0; -def U16_MIN: u16 = 0; -def U32_MIN: u32 = 0; -def U64_MIN: u64 = 0; +export def U8_MIN: u8 = 0; +export def U16_MIN: u16 = 0; +export def U32_MIN: u32 = 0; +export def U64_MIN: u64 = 0; // int/uint are machine-word (Go-style, type.c:58); limits derived from // size(int) per #114 + user ruling; cf Go math.MaxInt; diverges from // Hare's per-arch literal (arch+x86_64.ha) because ww's int is 64-bit. -def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; -def INT_MIN: int = -1 << (size(int)*8 - 1); -def UINT_MIN: uint = 0; -def UINT_MAX: uint = ~(0: uint); +export def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; +export def INT_MIN: int = -1 << (size(int)*8 - 1); +export def UINT_MIN: uint = 0; +export def UINT_MAX: uint = ~(0: uint); // size is 8B on amd64; no cast needed (size ∈ unsigned class per #113). -def SIZE_MIN: size = U64_MIN; -def SIZE_MAX: size = U64_MAX; +export def SIZE_MIN: size = U64_MIN; +export def SIZE_MAX: size = U64_MAX; // uintptr not in the unsigned class, so the cast is required (Hare's form). -def UINTPTR_MIN: uintptr = U64_MIN: uintptr; -def UINTPTR_MAX: uintptr = U64_MAX: uintptr; +export def UINTPTR_MIN: uintptr = U64_MIN: uintptr; +export def UINTPTR_MAX: uintptr = U64_MAX: uintptr; -def RUNE_MIN: rune = '\0'; +export def RUNE_MIN: rune = '\0'; +// Hare spells this `'\U0010ffff'` (ref/hare/types/limits.ha:57); ww's lexer +// has no \U/\u escape (cmd/wcc/lex.c escape(): \xHH only, max 0xFF) so the +// codepoint is written as an int-cast — same value, forced spelling. #50. +export def RUNE_MAX: rune = 0x10ffff: rune; //ww:module bytes // bytes — slice operations over []u8. Mirrors Hare's bytes module diff --git a/selfhost/cmd/wcc/wwi.ww b/selfhost/cmd/wcc/wwi.ww index 928b2bf5..500130bf 100644 --- a/selfhost/cmd/wcc/wwi.ww +++ b/selfhost/cmd/wcc/wwi.ww @@ -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 = { diff --git a/selfhost/cmd/ww/main.combined.ww b/selfhost/cmd/ww/main.combined.ww index 58a872f5..7e7b18ae 100644 --- a/selfhost/cmd/ww/main.combined.ww +++ b/selfhost/cmd/ww/main.combined.ww @@ -866,43 +866,47 @@ export fn exists(path: str) bool = { package types; -def I8_MAX: i8 = 127; -def I16_MAX: i16 = 32767; -def I32_MAX: i32 = 2147483647; -def I64_MAX: i64 = 9223372036854775807; +export def I8_MAX: i8 = 127; +export def I16_MAX: i16 = 32767; +export def I32_MAX: i32 = 2147483647; +export def I64_MAX: i64 = 9223372036854775807; -def I8_MIN: i8 = -128; -def I16_MIN: i16 = -32768; -def I32_MIN: i32 = -2147483648; -def I64_MIN: i64 = -9223372036854775808; +export def I8_MIN: i8 = -128; +export def I16_MIN: i16 = -32768; +export def I32_MIN: i32 = -2147483648; +export def I64_MIN: i64 = -9223372036854775808; -def U8_MAX: u8 = 255; -def U16_MAX: u16 = 65535; -def U32_MAX: u32 = 4294967295; -def U64_MAX: u64 = 18446744073709551615; +export def U8_MAX: u8 = 255; +export def U16_MAX: u16 = 65535; +export def U32_MAX: u32 = 4294967295; +export def U64_MAX: u64 = 18446744073709551615; -def U8_MIN: u8 = 0; -def U16_MIN: u16 = 0; -def U32_MIN: u32 = 0; -def U64_MIN: u64 = 0; +export def U8_MIN: u8 = 0; +export def U16_MIN: u16 = 0; +export def U32_MIN: u32 = 0; +export def U64_MIN: u64 = 0; // int/uint are machine-word (Go-style, type.c:58); limits derived from // size(int) per #114 + user ruling; cf Go math.MaxInt; diverges from // Hare's per-arch literal (arch+x86_64.ha) because ww's int is 64-bit. -def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; -def INT_MIN: int = -1 << (size(int)*8 - 1); -def UINT_MIN: uint = 0; -def UINT_MAX: uint = ~(0: uint); +export def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; +export def INT_MIN: int = -1 << (size(int)*8 - 1); +export def UINT_MIN: uint = 0; +export def UINT_MAX: uint = ~(0: uint); // size is 8B on amd64; no cast needed (size ∈ unsigned class per #113). -def SIZE_MIN: size = U64_MIN; -def SIZE_MAX: size = U64_MAX; +export def SIZE_MIN: size = U64_MIN; +export def SIZE_MAX: size = U64_MAX; // uintptr not in the unsigned class, so the cast is required (Hare's form). -def UINTPTR_MIN: uintptr = U64_MIN: uintptr; -def UINTPTR_MAX: uintptr = U64_MAX: uintptr; +export def UINTPTR_MIN: uintptr = U64_MIN: uintptr; +export def UINTPTR_MAX: uintptr = U64_MAX: uintptr; -def RUNE_MIN: rune = '\0'; +export def RUNE_MIN: rune = '\0'; +// Hare spells this `'\U0010ffff'` (ref/hare/types/limits.ha:57); ww's lexer +// has no \U/\u escape (cmd/wcc/lex.c escape(): \xHH only, max 0xFF) so the +// codepoint is written as an int-cast — same value, forced spelling. #50. +export def RUNE_MAX: rune = 0x10ffff: rune; //ww:module bytes // bytes — slice operations over []u8. Mirrors Hare's bytes module diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 7440ab75..fe8e38d6 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -2388,43 +2388,47 @@ let POW5_TABLE: [26]u64 = [ package types; -def I8_MAX: i8 = 127; -def I16_MAX: i16 = 32767; -def I32_MAX: i32 = 2147483647; -def I64_MAX: i64 = 9223372036854775807; +export def I8_MAX: i8 = 127; +export def I16_MAX: i16 = 32767; +export def I32_MAX: i32 = 2147483647; +export def I64_MAX: i64 = 9223372036854775807; -def I8_MIN: i8 = -128; -def I16_MIN: i16 = -32768; -def I32_MIN: i32 = -2147483648; -def I64_MIN: i64 = -9223372036854775808; +export def I8_MIN: i8 = -128; +export def I16_MIN: i16 = -32768; +export def I32_MIN: i32 = -2147483648; +export def I64_MIN: i64 = -9223372036854775808; -def U8_MAX: u8 = 255; -def U16_MAX: u16 = 65535; -def U32_MAX: u32 = 4294967295; -def U64_MAX: u64 = 18446744073709551615; +export def U8_MAX: u8 = 255; +export def U16_MAX: u16 = 65535; +export def U32_MAX: u32 = 4294967295; +export def U64_MAX: u64 = 18446744073709551615; -def U8_MIN: u8 = 0; -def U16_MIN: u16 = 0; -def U32_MIN: u32 = 0; -def U64_MIN: u64 = 0; +export def U8_MIN: u8 = 0; +export def U16_MIN: u16 = 0; +export def U32_MIN: u32 = 0; +export def U64_MIN: u64 = 0; // int/uint are machine-word (Go-style, type.c:58); limits derived from // size(int) per #114 + user ruling; cf Go math.MaxInt; diverges from // Hare's per-arch literal (arch+x86_64.ha) because ww's int is 64-bit. -def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; -def INT_MIN: int = -1 << (size(int)*8 - 1); -def UINT_MIN: uint = 0; -def UINT_MAX: uint = ~(0: uint); +export def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; +export def INT_MIN: int = -1 << (size(int)*8 - 1); +export def UINT_MIN: uint = 0; +export def UINT_MAX: uint = ~(0: uint); // size is 8B on amd64; no cast needed (size ∈ unsigned class per #113). -def SIZE_MIN: size = U64_MIN; -def SIZE_MAX: size = U64_MAX; +export def SIZE_MIN: size = U64_MIN; +export def SIZE_MAX: size = U64_MAX; // uintptr not in the unsigned class, so the cast is required (Hare's form). -def UINTPTR_MIN: uintptr = U64_MIN: uintptr; -def UINTPTR_MAX: uintptr = U64_MAX: uintptr; +export def UINTPTR_MIN: uintptr = U64_MIN: uintptr; +export def UINTPTR_MAX: uintptr = U64_MAX: uintptr; -def RUNE_MIN: rune = '\0'; +export def RUNE_MIN: rune = '\0'; +// Hare spells this `'\U0010ffff'` (ref/hare/types/limits.ha:57); ww's lexer +// has no \U/\u escape (cmd/wcc/lex.c escape(): \xHH only, max 0xFF) so the +// codepoint is written as an int-cast — same value, forced spelling. #50. +export def RUNE_MAX: rune = 0x10ffff: rune; //ww:module bytes // bytes — slice operations over []u8. Mirrors Hare's bytes module diff --git a/selfhost/test/smoke.combined.ww b/selfhost/test/smoke.combined.ww index cfe9aab8..d19a21a5 100644 --- a/selfhost/test/smoke.combined.ww +++ b/selfhost/test/smoke.combined.ww @@ -2388,43 +2388,47 @@ let POW5_TABLE: [26]u64 = [ package types; -def I8_MAX: i8 = 127; -def I16_MAX: i16 = 32767; -def I32_MAX: i32 = 2147483647; -def I64_MAX: i64 = 9223372036854775807; +export def I8_MAX: i8 = 127; +export def I16_MAX: i16 = 32767; +export def I32_MAX: i32 = 2147483647; +export def I64_MAX: i64 = 9223372036854775807; -def I8_MIN: i8 = -128; -def I16_MIN: i16 = -32768; -def I32_MIN: i32 = -2147483648; -def I64_MIN: i64 = -9223372036854775808; +export def I8_MIN: i8 = -128; +export def I16_MIN: i16 = -32768; +export def I32_MIN: i32 = -2147483648; +export def I64_MIN: i64 = -9223372036854775808; -def U8_MAX: u8 = 255; -def U16_MAX: u16 = 65535; -def U32_MAX: u32 = 4294967295; -def U64_MAX: u64 = 18446744073709551615; +export def U8_MAX: u8 = 255; +export def U16_MAX: u16 = 65535; +export def U32_MAX: u32 = 4294967295; +export def U64_MAX: u64 = 18446744073709551615; -def U8_MIN: u8 = 0; -def U16_MIN: u16 = 0; -def U32_MIN: u32 = 0; -def U64_MIN: u64 = 0; +export def U8_MIN: u8 = 0; +export def U16_MIN: u16 = 0; +export def U32_MIN: u32 = 0; +export def U64_MIN: u64 = 0; // int/uint are machine-word (Go-style, type.c:58); limits derived from // size(int) per #114 + user ruling; cf Go math.MaxInt; diverges from // Hare's per-arch literal (arch+x86_64.ha) because ww's int is 64-bit. -def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; -def INT_MIN: int = -1 << (size(int)*8 - 1); -def UINT_MIN: uint = 0; -def UINT_MAX: uint = ~(0: uint); +export def INT_MAX: int = (1 << (size(int)*8 - 1)) - 1; +export def INT_MIN: int = -1 << (size(int)*8 - 1); +export def UINT_MIN: uint = 0; +export def UINT_MAX: uint = ~(0: uint); // size is 8B on amd64; no cast needed (size ∈ unsigned class per #113). -def SIZE_MIN: size = U64_MIN; -def SIZE_MAX: size = U64_MAX; +export def SIZE_MIN: size = U64_MIN; +export def SIZE_MAX: size = U64_MAX; // uintptr not in the unsigned class, so the cast is required (Hare's form). -def UINTPTR_MIN: uintptr = U64_MIN: uintptr; -def UINTPTR_MAX: uintptr = U64_MAX: uintptr; +export def UINTPTR_MIN: uintptr = U64_MIN: uintptr; +export def UINTPTR_MAX: uintptr = U64_MAX: uintptr; -def RUNE_MIN: rune = '\0'; +export def RUNE_MIN: rune = '\0'; +// Hare spells this `'\U0010ffff'` (ref/hare/types/limits.ha:57); ww's lexer +// has no \U/\u escape (cmd/wcc/lex.c escape(): \xHH only, max 0xFF) so the +// codepoint is written as an int-cast — same value, forced spelling. #50. +export def RUNE_MAX: rune = 0x10ffff: rune; //ww:module bytes // bytes — slice operations over []u8. Mirrors Hare's bytes module diff --git a/test/wcc/989_m2wwi_run.c b/test/wcc/989_m2wwi_run.c index 5c59cb2f..684f5061 100644 --- a/test/wcc/989_m2wwi_run.c +++ b/test/wcc/989_m2wwi_run.c @@ -323,6 +323,65 @@ out: return rc; } +/* #48 gate: the lib/types limit constants are `export def`s (Hare exports + * types::I32_MAX &c). Produce types.wwi on both stages directly from the + * real lib source (no imports → its own primary unit), assert byte-id + + * re-parse + that the exported limits actually appear in the .wwi (a non- + * exported def is omitted, so this proves the export reached the interface + * a cross-package sep-compile consumer reads). Returns 0 on pass. */ +static int +typesexport(const char *bin, const char *cwd) +{ + char td[64], cmd[4096], src[1024]; + int rc = -1; + + snprintf(td, sizeof td, "/tmp/wwm2typ_%d", getpid()); + snprintf(cmd, sizeof cmd, "rm -rf %s", td); + runwait(cmd); + mkdir(td, 0755); + + snprintf(src, sizeof src, "%s/lib/types/types.ww", cwd); + char cs[1100], ws[1100]; + snprintf(cs, sizeof cs, "%s/cs.wwi", td); + snprintf(ws, sizeof ws, "%s/ww.wwi", td); + + snprintf(cmd, sizeof cmd, + "timeout 180 %s/w6c -I %s %s >/dev/null 2>&1", bin, cs, src); + if (runwait(cmd) != 0) { + fprintf(stderr, "m2wwi FAIL: types — w6c -I rejected\n"); + goto out; + } + snprintf(cmd, sizeof cmd, + "timeout 180 %s/w6c_ww -I %s %s >/dev/null 2>&1", bin, ws, src); + if (runwait(cmd) != 0) { + fprintf(stderr, "m2wwi FAIL: types — w6c_ww -I rejected\n"); + goto out; + } + if (files_eq(cs, ws) != 0) { + fprintf(stderr, "m2wwi FAIL: types — cs.wwi != ww.wwi\n"); + goto out; + } + if (file_contains(cs, "export def I32_MAX") != 0 + || file_contains(cs, "export def RUNE_MAX") != 0) { + fprintf(stderr, "m2wwi FAIL: types — limit consts not exported " + "into the .wwi (cross-package sep-compile would not see " + "types.I32_MAX)\n"); + goto out; + } + snprintf(cmd, sizeof cmd, + "timeout 180 %s/wwdump -a %s >/dev/null 2>&1", bin, cs); + if (runwait(cmd) != 0) { + fprintf(stderr, "m2wwi FAIL: types — emitted .wwi does not " + "re-parse (wwdump -a)\n"); + goto out; + } + rc = 0; +out: + snprintf(cmd, sizeof cmd, "rm -rf %s", td); + runwait(cmd); + return rc; +} + int main(void) { @@ -339,6 +398,8 @@ main(void) } if (synth(bin) != 0) fail++; else npos++; + if (typesexport(bin, cwd) != 0) fail++; + else npos++; if (negative(bin) != 0) fail++; if (fail) {