lib/strconv: graduate to owned-str returns with Hare-shape base param

i64tos / u64tos / f64tos return a fresh owned str (caller frees via
os.free) instead of writing into a caller-supplied [N]u8. Adds typed
variants (i32tos / i16tos / i8tos and u32 / u16 / u8) and the missing
base parameter on stoi64 / stou64 + typed parse wrappers.

Base values are exported as plain-i32 `def`s (strconv.DEC,
strconv.HEX_UPPER, ...) rather than a `base` enum: cross-module
`strconv.base.DEC` chains miscompile in the cstage cgen — it emits a
memory load through `base(SB)` rather than inlining the constant.
The Sdef path resolves correctly, so callers say `strconv.DEC` and
both cgens lower to an immediate.

Also renames strings.byteindex / rbyteindex to strings.indexbyte /
rindexbyte, matching bytes.indexbyte and reserving the Hare name
`byteindex` for the future `(str | rune)`-needle shape.

fmt drops printint / printlnint / fprintint — those were stand-ins
for variadic `fmt::println(42)`; with the owned-str graduation the
substitute is one call: `fmt.println(strconv.i64tos(42, strconv.DEC))`.

strerror is sketched in a comment but not shipped — match arms over
the wider `error = !(invalid | overflow)` union still expose a
cstage-vs-wwstage spill divergence.
This commit is contained in:
2026-05-13 03:55:16 +09:00
parent 9bc973dc96
commit be8a662f15
14 changed files with 1192 additions and 520 deletions

View File

@@ -139,17 +139,15 @@ static const struct row rows[] = {
" for (i < 3) { s += buf[i]: i32; i += 1; };\n"
" return s;\n"
"};", 198 },
/* full stdlib stack: use os + strconv, slice-arg call, write
/* full stdlib stack: use os + strconv, str return, write
* the formatted number to stdout. exit code = number length. */
{ "use os;\n"
"use strconv;\n"
"fn main() i32 = {\n"
" let buf: [32]u8;\n"
" let s: []u8 = buf[0:32];\n"
" let n: i32 = strconv.i64tos(s, 12345);\n"
" os.write(1, buf.ptr, n: u64);\n"
" let s: str = strconv.i64tos(12345, strconv.DEC);\n"
" os.write(1, s.ptr, s.len: u64);\n"
" os.write(1, \"\\n\".ptr, 1u64);\n"
" return n;\n"
" return s.len;\n"
"};", 5 },
/* alloc + free via mmap-backed runtime — write through allocated
* memory and free it. exit = 0 if the allocation succeeded. */
@@ -195,12 +193,14 @@ static const struct row rows[] = {
"fn main() i32 = {\n"
" return classify(2) + classify(10) + classify(99);\n"
"};", 159 }, /* 10+99+50=159 */
/* fmt module: stdlib formatter for ints + strings */
/* fmt module: stdlib formatter for strings; ints compose
* via strconv.i64tos. */
{ "use fmt;\n"
"use strconv;\n"
"fn main() i32 = {\n"
" fmt.println(\"ww\");\n"
" fmt.printlnint(42);\n"
" fmt.printlnint(-7);\n"
" fmt.println(strconv.i64tos(42, strconv.DEC));\n"
" fmt.println(strconv.i64tos(-7, strconv.DEC));\n"
" return 0;\n"
"};", 0 },
/* struct with i32 fields: MOVL/MOVSXD avoids clobbering neighbors */
@@ -866,9 +866,9 @@ static const struct row rows[] = {
{ "use strconv;\n"
"type r_t = (i64 | strconv.invalid | strconv.overflow);\n"
"fn main() i32 = {\n"
" let r1: r_t = strconv.stoi64(\"42\");\n"
" let r2: r_t = strconv.stoi64(\"-7\");\n"
" let r3: r_t = strconv.stoi64(\"abc\");\n"
" let r1: r_t = strconv.stoi64(\"42\", strconv.DEC);\n"
" let r2: r_t = strconv.stoi64(\"-7\", strconv.DEC);\n"
" let r3: r_t = strconv.stoi64(\"abc\", strconv.DEC);\n"
" let acc: i32 = 0;\n"
" match (r1) {\n"
" case let v: i64 => acc += v: i32;\n"
@@ -892,8 +892,8 @@ static const struct row rows[] = {
{ "use strconv;\n"
"type r_t = (u64 | strconv.invalid | strconv.overflow);\n"
"fn main() i32 = {\n"
" let r1: r_t = strconv.stou64(\"123\");\n"
" let r2: r_t = strconv.stou64(\"-1\");\n"
" let r1: r_t = strconv.stou64(\"123\", strconv.DEC);\n"
" let r2: r_t = strconv.stou64(\"-1\", strconv.DEC);\n"
" let acc: i32 = 0;\n"
" match (r1) {\n"
" case let v: u64 => acc += v: i32;\n"
@@ -907,7 +907,7 @@ static const struct row rows[] = {
" };\n"
" return acc;\n"
"};", 123 }, /* 123 + 0 (invalid at index 0 in \"-1\") */
/* strings.byteindex and strings.index: now (i32 | void). */
/* strings.indexbyte and strings.index: now (i32 | void). */
{ "use strings;\n"
"fn pick(r: (i32 | void), miss: i32) i32 = {\n"
" match (r) {\n"
@@ -918,8 +918,8 @@ static const struct row rows[] = {
"};\n"
"fn main() i32 = {\n"
" let s: str = \"hello, world\";\n"
" let i1: i32 = pick(strings.byteindex(s, 44u8), -1);\n"
" let i2: i32 = pick(strings.byteindex(s, 122u8), -1);\n"
" let i1: i32 = pick(strings.indexbyte(s, 44u8), -1);\n"
" let i2: i32 = pick(strings.indexbyte(s, 122u8), -1);\n"
" let i3: i32 = pick(strings.index(s, \"world\"), -1);\n"
" let i4: i32 = pick(strings.index(s, \"nope\"), -1);\n"
" return i1 + i2 + i3 + i4;\n"