lang: rename signed integer types sN -> iN
Surface rename of the signed integer family: s1..s64 become i1..i64
(u1..u64, usize, isize unchanged). 'string' keeps the s-prefix arm in
name classification; width parsing moves to the i-prefix arm next to
isize.
Internal TypeId tags follow the surface (.s8/.s16/.s32/.s64 ->
.i8/.i16/.i32/.i64), as do mono-key mangle fragments (ptr_i64,
tu_i64_bool) and all display/diagnostic formatting (i{d}).
Migrated in the same sweep: stdlib + examples + issue repros + FFI C
companions (shared symbol names like ffi_id_i64), expected
stdout/stderr/ir snapshots, specs.md, readme.md, CLAUDE.md/AGENTS.md,
implementation_plan.md, docs/, issue writeups. Vendored stb_image and
historical flow state left untouched.
zig build test: 426/426; examples suite: 595/595.
This commit is contained in:
@@ -1,21 +1,21 @@
|
||||
# 0090 — integer formatter can't render i64::MIN or unsigned all-ones
|
||||
|
||||
> STATUS: RESOLVED (F0.8). Both extremes now render correctly:
|
||||
> `s64.min` → `-9223372036854775808`, `u64.max` → `18446744073709551615`.
|
||||
> `i64.min` → `-9223372036854775808`, `u64.max` → `18446744073709551615`.
|
||||
>
|
||||
> **Root cause.**
|
||||
> - Symptom 1 (i64::MIN): `std.int_to_string` computed the magnitude as
|
||||
> `0 - n`, which overflows for `s64::MIN` (its magnitude is
|
||||
> unrepresentable as a positive s64) — the value stayed negative, the
|
||||
> `0 - n`, which overflows for `i64::MIN` (its magnitude is
|
||||
> unrepresentable as a positive i64) — the value stayed negative, the
|
||||
> `while v > 0` loop ran zero times, and only the `-` was emitted.
|
||||
> - Symptom 2 (unsigned all-ones): `any_to_string`'s `case int:` arm
|
||||
> formatted every integer as s64 (`int_to_string(xx val)`); there was no
|
||||
> way to tell a `u64` from an `s64`, so an all-ones u64 printed as `-1`.
|
||||
> formatted every integer as i64 (`int_to_string(xx val)`); there was no
|
||||
> way to tell a `u64` from an `i64`, so an all-ones u64 printed as `-1`.
|
||||
>
|
||||
> **Fix per file.**
|
||||
> - `library/modules/std.sx` — `int_to_string` now extracts digits straight
|
||||
> from `n` (taking `|n % 10|` per digit, `n` truncates toward zero) so it
|
||||
> never negates `s64::MIN`. Added `uint_to_string` (unsigned decimal via
|
||||
> never negates `i64::MIN`. Added `uint_to_string` (unsigned decimal via
|
||||
> long-division-by-10 over four 16-bit limbs) and `decompose_u16x4` (the
|
||||
> shared 16-bit-limb split, now reused by `int_to_hex_string` too).
|
||||
> `any_to_string`'s `case int:` routes through the new
|
||||
@@ -35,7 +35,7 @@
|
||||
> table built from `isUnsignedInt`; runtime arm GEPs in at the TypeId.
|
||||
>
|
||||
> **Regression test.** `examples/0046-basic-int-formatter-extremes.sx`
|
||||
> pins both extremes plus a width spread (s8/s16/s32 + u8/u16/u32/u64,
|
||||
> pins both extremes plus a width spread (i8/i16/i32 + u8/u16/u32/u64,
|
||||
> mins/maxes, 0, ordinary values). Unit tests: `isUnsignedInt` in
|
||||
> `src/ir/types.test.zig`.
|
||||
>
|
||||
@@ -67,13 +67,13 @@
|
||||
> accepts an `Any` argument (the formatter passes `val: Any`), but the dynamic
|
||||
> `type_name` / `type_is_unsigned` path still read the Any's payload as a
|
||||
> TypeId index unconditionally — correct only when the Any holds a *Type
|
||||
> value*. For an Any holding a *value* (`av : Any = 6`, runtime tag `s64`,
|
||||
> value*. For an Any holding a *value* (`av : Any = 6`, runtime tag `i64`,
|
||||
> payload `6`) it reported `types[6]` (`u8`): `type_name(av)` → `"u8"`,
|
||||
> `type_is_unsigned(av)` → `true`. Per Agra's ruling ("Any is a type AND a
|
||||
> value, so it's expected to work"), both builtins now branch on the Any's
|
||||
> runtime tag: tag `== .any` → the box is a Type value, use the payload as the
|
||||
> TypeId; otherwise the tag IS the held value's type. So `type_name(av)` →
|
||||
> `"s64"`, `type_is_unsigned(av)` → `false`, while `type_name(type_of(x))`
|
||||
> `"i64"`, `type_is_unsigned(av)` → `false`, while `type_name(type_of(x))`
|
||||
> still names the held type. The formatter is unchanged (it already passed
|
||||
> `type_of(val)`, a proper Type value).
|
||||
> - `src/ir/interp.zig` — shared `Value.reflectTypeId` (the tag-branching
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
## Symptom
|
||||
|
||||
`print("{}", x)` mis-renders the integer extremes the s64-based formatter can't
|
||||
`print("{}", x)` mis-renders the integer extremes the i64-based formatter can't
|
||||
represent:
|
||||
- `i64::MIN` (`-9223372036854775808`) prints a bare `-` (the minus sign with NO
|
||||
digits).
|
||||
@@ -115,8 +115,8 @@ root reason.
|
||||
|
||||
## Root cause (suspected)
|
||||
|
||||
The integer-to-string path is `s64`-based (`std.int_to_string` / the `{}` formatter
|
||||
takes `s64`): it negates the value to print the sign, but `-i64::MIN` overflows, and
|
||||
The integer-to-string path is `i64`-based (`std.int_to_string` / the `{}` formatter
|
||||
takes `i64`): it negates the value to print the sign, but `-i64::MIN` overflows, and
|
||||
it has no unsigned-aware path so an all-ones u64 is read as `-1`. Needs a width/
|
||||
signedness-aware integer formatter (format by the value's actual integer TYPE:
|
||||
unsigned types print the unsigned decimal; signed `MIN` is handled without negating).
|
||||
|
||||
Reference in New Issue
Block a user