WriteLn(Boolean) prints 0/1 instead of FALSE/TRUE
- Type: bug (output formatting / FPC-compat) — Track A
- Status: done
- Owner: — (Track A)
- Opened: 2026-06-23
- Closed: 2026-06-23
- Found by: differential probe vs FPC (
writeln(1>0)→ pxx1, fpcTRUE).
Resolution (2026-06-23)
Approach 2 (target-independent IR lowering) implemented. Added IRSyntheticConstStr
(appends a literal to the TokChars raw pool and returns an IR_CONST_STR node —
the missing "synthetic string literal" the earlier attempt was blocked on) and
IRLowerBoolWrite in compiler/ir.inc. In AN_WRITE/AN_WRITELN lowering, a
tyBoolean arg (plain, AN_ARG/:width, or AN_PAIR) now lowers to
IR_JUMP_IF_FALSE / IR_WRITE('TRUE') / IR_JUMP / IR_LABEL / IR_WRITE('FALSE') / IR_LABEL — all primitives every backend already handles, so x86-64, i386,
aarch64, arm32 and ESP riscv32/xtensa all print FPC-correct TRUE/FALSE at
once (field width honored via the existing IR_CONST_STR write path). The
Nil-Python frontend (isNilPy) renders Python's True/False instead.
Test expectations flipped from 0/1: test_set_runtime, test_uint64_ops,
test_conformance_1, test_conformance_2, test_cross_global_init,
test_variant_ops, test_variant_string_ops, test_array_of_const_types
(VBoolean), test_nilpy_bool (→ True/False). Gate green: make test
(self-host byte-identical after 1-gen reseed), cross-output tests, cross-bootstrap.
Problem
write/writeln of a Boolean value prints the integer 0/1. FPC prints
FALSE/TRUE. Affects any writeln(b) / writeln(x>y) etc. Severity is
formatting, not a silent miscompile (loud divergence), but it breaks FPC-output
parity broadly.
Why it is not a quick fix (attempted 2026-06-23, reverted)
The write path resists both obvious fixes:
- Per-backend codegen —
IR_WRITEis lowered per target. A Boolean arrives as an ordinal and falls through to the integer-write path in EACH backend (x86-64ir_codegen.inc, plusir_codegen_{386,aarch64,arm32}.inc; ESP riscv32/xtensawriteis a bare no-op). Adding atyBooleancase (test; jz .false; write 'TRUE'; jmp; .false: write 'FALSE') is mechanical but must be repeated in 4 hosted backends with per-arch branch encoding, and the cross-output comparison tests (make test-i386/aarch64/arm32, which diff cross output against the x86-64 oracle) break unless ALL hosted backends are fixed together. An x86-64-only fix was implemented + verified (TRUE/FALSE, FPC-matched) but reverted to avoid the half-done cross inconsistency. - Target-independent IR lowering (the clean design) — lower a Boolean write
arg in
ir.incAN_WRITEtoif b goto L1; write('FALSE'); goto L2; L1: write('TRUE'); L2:usingIR_IF_GOTO/IR_LABEL/IR_JMP+IR_WRITEof a string constant (the linear IR emitter runs them in order, so injecting them into the write chain works on every backend at once). BLOCKER: anIR_WRITEstring literal currently keys off a RAW SOURCE token span (GetTokenStrFromRaw(IRA, IRB)), so a synthetic'TRUE'/'FALSE'(not in the user's source) can't be referenced directly. Needs a way to emit anIR_WRITEof an interned/synthetic string (e.g. append the two literals to the raw buffer at init and remember their spans, or anIR_WRITEvariant that takes an interned-string index). Once that exists, this is the right fix — one place, all targets.
Recommended fix
Approach 2 (target-independent IR), after adding synthetic-string-literal support
to the write path. ~8 existing test expectations encode the current 0/1
output and must flip to TRUE/FALSE (FPC-correct): test_set_runtime,
test_uint64_ops, test_conformance_1, test_conformance_2,
test_cross_global_init, test_variant_ops, test_variant_string_ops (the
exact new strings were computed during the reverted attempt).
Gate
make test + make cross-bootstrap + the cross-output tests
(make test-i386/aarch64/arm32) must stay green; FPC oracle-match the boolean
output.