Issue #630: int: the values of an i64, its representation the compiler’s choice; a fixed width is exact and is written where the representation shows

Open and answer this issue on GitHub.

ready · opened 2026-10-17 · updated 2026-10-17 · 2 comments

How the question came up

The owner noticed confusion about when an integer type must or should be written. A read-only analysis (October 17, 2026) checked today’s rules (ADR-285), counted the annotations and compared other languages:

The first proposal on the table, widths only at the C/Rust interfaces, a warning elsewhere, was dropped: it would fire on the deliberate widths, and no surveyed language warns about a written width.

The owner’s direction then: i32 was preferred as a fallback to keep room for optimisation (32-bit targets, SIMD lanes, smaller lists). But because an overflow aborts, a narrower type changes what a program computes. The room belongs in the representation: as with String, where the compiler chooses between borrowing and owning without changing the result, the compiler may choose an integer’s width where nothing observable changes. And a programmer who writes i64 expects exactly that; so the soft promise needs a name of its own: int.

Decision

ADR-285 D36-D39:

Not decided here (ADR-285 §6): whether a number no use asks about falls back to int instead of i32 (D21); whether an arithmetic operand may narrow an open number (the u8 byte sum); an integer bound for generics (fn f[T: Integer]); whether len() and an index become int; a hint for an i64 where int would do; which narrower representations are built, and when.

To build

Prerequisite: #629 (D-1 to D-3), so that no annotation change can turn an abort into a wrong value.

  1. Specification (raises the version): Part I 2.2 gains int (D36, D37) and the rule of thumb (D39: int for most numbers, a fixed width where the width is the point); Part I 2.4 says int and i64 take each other; Part III’s table gains a code for int in an extern "C" signature or a described contract (D38).
  2. Checker: int in tools/types.nika as a built-in type with every rule of i64; int ↔ i64 without as in both directions; the D38 refusal. Tests: fn f(a: int) -> int { return a + 5 } checks and runs; let x: i64 = f(1) checks without as; extern "C" fn g(n: int) is refused with the new code; int overflow aborts at the i64 bound (9223372036854775807 + 1 read at run time).
  3. Lowering: int is emitted as i64 (D37 allows no narrowing yet). Test: the generated Rust of the program in 2 equals that of the same program written with i64.
  4. lower-std, commit package.rs.
  5. guide/ and examples/: write int where the width is not the point (no version raise).
  6. tools/*.nika: move to int when a file is next reworked; not in bulk.
  7. Before any narrowing (D37) is built, measure with callgrind: an unannotated literal list as Vec[i32] against Vec[i64], and one SIMD kernel; then a decision round on which narrowings to build first.

Comments

keywan-ghadami, 2026-10-17

Step 1 (specification) is done in d0231d7 (0.0.976): Part I 2.2 gains int beside the five fixed widths, and Part III C.3 gains NK1262 for an int in an extern "C" signature or a described Rust contract. Steps 2 to 7 are still open, with #629 as the prerequisite.


Generated by Claude Code

keywan-ghadami, 2026-10-17

Steps 2 to 5 are built. The prerequisite #629 (D-1 to D-3) came first:

What is still open:

  1. NK1262 in a described Rust contract (contracts/<crate>.contracts). This is not built. When a ledger fails to parse today, the file is dropped (Ledger::read_beside returns None), and nothing reports a finding with a location. The refusal needs a way to report from a ledger first.
  2. Step 6: move tools/*.nika to int when each file is next reworked.
  3. Step 7: take the callgrind measurements and hold the decision round before any narrowing (D37).
  4. Smaller points found while building:
    • Messages name an int as i64, because the checker reads it as one.
    • An int parameter of an extern fn gets NK1240 (cannot cross to C) next to NK1262. The second message is redundant.

Generated by Claude Code

Back to Open Issues