RFC-0043: Value types — int/bool lowered to EIR
Status: Accepted. This RFC specifies how the language's value kinds
(f64, i64/i32/u64/u32, bool) are lowered to typed EIR registers,
instead of today's single f64 register file with surface-only checks.
Motivation
The EIR value model already carries I32/U32/I64/U64/F32/F64/Bool
(reference/src/eir.rs, enum ValueType) and the interpreter's arith/
divrem/compare are width- and kind-correct. The surface language, however,
widens everything to f64: comparisons lower to a Bool opcode but are then
re-widened by truthy(); integer literals become f64; and let-type
annotations (detail 89) are syntax-only. This wastes the typed core, loses exact
integer arithmetic (counts/indices), and makes int/bool second-class.
Design
Type inference (surface)
expr_kind(&Expr) -> Kind where Kind ∈ {F64, Int, Bool, Unknown}:
- integer literals (
7, no decimal point) →Int; decimal (7.0) →F64; + - * / %→Intiff both operands areInt, elseF64;- comparisons and
and/or/not→Bool; - casts (
i64(…)→Int,f64(…)→F64,bool(…)→Bool); - a bare name/slot/ref/call →
Unknownunless alet-tracked local establishes its kind (the checker tracksletkinds in declaration order).
Lowering
lower_expr(expr, …) -> (u32, ValueType) (the existing u32-returning
lower_expr becomes a wrapper that coerces to F64 via the conversion
opcodes below, so existing call sites are unchanged):
Intnodes emitConstwithImmediate::I64andI64arithmetic opcodes;Div/RemonI64trap on zero (RFC-0021) and divide toward zero;Boolnodes emitBooltyped results (Cmp,Ne,SelectwithBool);- state writes (
WriteView, state slots aref64) and function arguments declaredf64coerceInt→F64; aBoolwritten to state coerces to1/0; Unknownoperands coerce toF64(today's behavior).
New opcodes
Two conversions, added via the declare_opcodes! table (single source):
I64ToF64 = 233,F64ToI64 = 234(convert width/kind;F64ToI64truncates toward zero and traps on non-finite input, detail 18).
Both are pure (no effect bits); the interpreter is the oracle and the JIT/AOT
must remain byte-identical (step_cross).
Diagnostics
- detail 89 (type mismatch) uses
expr_kindwithlet-kind tracking, so integer annotations become meaningful (not vacuous) and bool aliases pass. - A
Boolused where a number is required (and vice-versa) is detail 89.
Validation
- Conformance: a cross-backend case asserting
Int/Boolregisters produce byte-identical writes and thatI64ToF64/F64ToI64round-trip. - Property tests over the typed arithmetic opcodes.
- Equivalence: every program that compiles under today's
f64-only lowering and does not use integer literals in ambiguous/positions yields identical results.
Backward compatibility
Source-compatible: 1.0-style literals are unchanged; 2 (no decimal) becomes
Int and coerces where a number is required, so existing programs keep working.
The one behavioral edge — 1 / 2 (both integer literals) now yields 0 instead
of 0.5 — is intentional (integer division) and documented.
Alternatives
- Keep
f64-only (status quo): simple, but forfeits the typed core and exact integer semantics. Rejected as the long-term shape. - Auto-narrow every integer literal to
Intwithoutlettracking: simpler but makes1 / 2a surprising0in more places. Thelet-tracked variant is safer and is what this RFC specifies.