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

@@ -472,15 +472,13 @@ fn localfind(c: *cgen, name: str) i32 = {
fn emitline(s: str) void = { os.write(1, s.ptr, s.len: u64); };
fn emitint(v: i64) void = {
let buf: [32]u8;
let n: i32 = strconv.i64tos(buf[0:32], v);
os.write(1, buf.ptr, n: u64);
let s: str = strconv.i64tos(v, strconv.DEC);
os.write(1, s.ptr, s.len: u64);
};
fn emituint(v: u64) void = {
let buf: [32]u8;
let n: i32 = strconv.u64tos(buf[0:32], v);
os.write(1, buf.ptr, n: u64);
let s: str = strconv.u64tos(v, strconv.DEC);
os.write(1, s.ptr, s.len: u64);
};
// emitdispreg — print "disp(reg)" or "(reg)" when disp == 0, the
@@ -499,9 +497,9 @@ fn emitoff(v: i64) void = {
if (v != 0i64) { emitint(v); };
};
// mklabel — fresh label "<fnname>_<base>_<seq>". Returns an
// mklabel — fresh label "<fnname>_<prefix>_<seq>". Returns an
// arena-owned str. Mirrors C cgen's mklabel so diffs match.
fn mklabel(c: *cgen, base: str) str = {
fn mklabel(c: *cgen, prefix: str) str = {
let buf: [128]u8;
let i: i32 = 0;
let fname: str = c.fnname;
@@ -512,12 +510,15 @@ fn mklabel(c: *cgen, base: str) str = {
};
buf[i] = 95u8; i += 1; // '_'
j = 0;
for (j < base.len) {
buf[i] = base[j];
for (j < prefix.len) {
buf[i] = prefix[j];
i += 1; j += 1;
};
buf[i] = 95u8; i += 1; // '_'
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
let ns: str = strconv.i64tos(c.labelseq: i64, strconv.DEC);
let n: i32 = ns.len;
let dk: i32 = 0;
for (dk < n) { buf[i + dk] = ns.ptr[dk]; dk += 1; };
c.labelseq += 1;
let total: i32 = i + n;
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
@@ -538,22 +539,25 @@ fn emitlabel(s: str) void = {
emitline(":\n");
};
// mkscratchname — fresh local-slot name ".<base>_<labelseq>". Used for
// mkscratchname — fresh local-slot name ".<prefix>_<labelseq>". Used for
// compiler-synthesised slots (switch scrutinee, forrange index/len)
// that need to be unique per use site but are never referenced by user
// code. Increments labelseq so the same source position lines up with
// C cgen's labelseq stream.
fn mkscratchname(c: *cgen, base: str) str = {
fn mkscratchname(c: *cgen, prefix: str) str = {
let buf: [128]u8;
let i: i32 = 0;
buf[i] = 46u8; i += 1; // '.'
let j: i32 = 0;
for (j < base.len) {
buf[i] = base[j];
for (j < prefix.len) {
buf[i] = prefix[j];
i += 1; j += 1;
};
buf[i] = 95u8; i += 1; // '_'
let n: i32 = strconv.i64tos(buf[i:128], c.labelseq: i64);
let ns: str = strconv.i64tos(c.labelseq: i64, strconv.DEC);
let n: i32 = ns.len;
let dk: i32 = 0;
for (dk < n) { buf[i + dk] = ns.ptr[dk]; dk += 1; };
c.labelseq += 1;
let total: i32 = i + n;
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;
@@ -587,7 +591,10 @@ fn internstrlit(c: *cgen, bytes: str) str = {
// New label "_S_<seq>".
let buf: [32]u8;
buf[0] = 95u8; buf[1] = 83u8; buf[2] = 95u8; // "_S_"
let n: i32 = strconv.i64tos(buf[3:32], c.strlitseq: i64);
let ns: str = strconv.i64tos(c.strlitseq: i64, strconv.DEC);
let n: i32 = ns.len;
let dk: i32 = 0;
for (dk < n) { buf[3 + dk] = ns.ptr[dk]; dk += 1; };
c.strlitseq += 1;
let total: i32 = 3 + n;
let p: *u8 = amalloc(c.a, (total: u64) + 1u64): *u8;